From 01a7c888b40d91e9f604434a15c616b1e3b7af1a Mon Sep 17 00:00:00 2001 From: Trigat Date: Sun, 24 Aug 2025 12:39:38 -0500 Subject: [PATCH 1/5] Add emv_extract.lua via upload Script that extracts EMV track 2 information Signed-off-by: Trigat --- client/luascripts/emv_extract.lua | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 client/luascripts/emv_extract.lua diff --git a/client/luascripts/emv_extract.lua b/client/luascripts/emv_extract.lua new file mode 100644 index 000000000..d35835459 --- /dev/null +++ b/client/luascripts/emv_extract.lua @@ -0,0 +1,80 @@ +copyright = "Trigat" +author = "Trigat" +desc = "This script scans an EMV (credit or debit) card and extracts Track 2 information." + +local home = os.getenv("HOME") +local json_file = home .. "/emv_output.json" + +core.console(string.format("emv scan -at %s", json_file)) + +os.execute("sleep 1") + +-- Read JSON content +local f = io.open(json_file, "r") +if not f then + print("[!!] Could not open JSON output") + return +end + +local content = f:read("*a") +f:close() + +local track2 = nil +local found = false + +-- Iterate over all "value" fields in JSON +for value_field in content:gmatch('"value"%s*:%s*"(.-)"') do + -- Look for 57 13 anywhere in the string + track2 = value_field:match('57%s*13%s*([0-9A-Fa-f%s]+)') + if track2 then + -- Remove spaces to get clean hex + track2 = track2:gsub("%s+", "") + print("[++] Track 2 data found: 57 13 " .. track2 .. "\n") + found = true + break -- remove break if you want all occurrences + end +end + +if not found then + print("[!!] Track 2 data not found in JSON") + return +end + +-- Remove 57 + Length prefix (first 4 hex digits = 57 13) +track2 = track2:gsub("^57%x%x", "") + +-- Convert hex string into individual nibbles (1 hex digit each) +local nibbles = {} +for hex_digit in track2:gmatch("%x") do + table.insert(nibbles, hex_digit) +end + +-- Find 'D' separator +local sep_index +for i, nib in ipairs(nibbles) do + if nib == "D" then + sep_index = i + break + end +end + +if not sep_index then + print("[!!] Could not find 'D' separator in Track 2") + return +end + +-- Extract Primary Account Number (all nibbles before D) +local pan = table.concat(nibbles, "", 1, sep_index-1) + +-- Expiry is next 4 nibbles after D +local yy = nibbles[sep_index + 1] .. nibbles[sep_index + 2] +local mm = nibbles[sep_index + 3] .. nibbles[sep_index + 4] + +-- Service code: next 3 nibbles +local service = table.concat(nibbles, "", sep_index + 5, sep_index + 7) or "N/A" + +print("[++] PAN: " .. pan) +print("[++] Expiry (MM/YY): " .. mm .. "/" .. yy) +print("[++] Service code: " .. service) + +os.execute('rm -f ' .. json_file) From 34e85935b81cacf825a080c55076cc14f6dc1d5f Mon Sep 17 00:00:00 2001 From: Trigat Date: Sun, 24 Aug 2025 12:41:07 -0500 Subject: [PATCH 2/5] Add ntag_clean.lua via upload Script that zeros NTAG pages Signed-off-by: Trigat --- client/luascripts/ntag_clean.lua | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 client/luascripts/ntag_clean.lua diff --git a/client/luascripts/ntag_clean.lua b/client/luascripts/ntag_clean.lua new file mode 100644 index 000000000..20cc03303 --- /dev/null +++ b/client/luascripts/ntag_clean.lua @@ -0,0 +1,117 @@ +copyright = "Trigat" +author = "Trigat" +desc = "Zero-fill NTAG pages within a range" + +local function help() + print([[ +NTAG_CLEAN - Zero-fill NTAG pages/blocks + +Options: + -h : this help + -s : start block (default 4) + -e : end block (default 39) + -d : data to write (8 hex chars, e.g. 00000000) + If 2 hex chars given (e.g. "FF"), it repeats to 8 ("FFFFFFFF"). + +Running with no options will zero out pages 4-39. + +]]) +end + +local function normalize_hex(h) + if not h then return nil end + h = h:gsub("%s+", "") + if not h:match("^[0-9a-fA-F]+$") then return nil end + if #h == 2 then + h = h:rep(4) -- "FF" converted to "FFFFFFFF" + end + if #h ~= 8 then + return nil + end + return h:upper() +end + +local function parse_args(argv) + local opts = {} + local i = 1 + while i <= #argv do + local a = argv[i] + if a == "-h" then + opts.h = true + elseif a == "-s" then + opts.s = tonumber(argv[i+1]); i = i + 1 + elseif a == "-e" then + opts.e = tonumber(argv[i+1]); i = i + 1 + elseif a == "-d" then + opts.d = argv[i+1]; i = i + 1 + end + i = i + 1 + end + return opts +end + +-- check if card exists +local function card_present() + local reader = require("read14a") + local tag, err = reader.read() + if not tag then + print("No card detected. Aborting.") + return false + end + + print("Card detected:", tag.uid, tag.name) + return true +end + +-- convert global args string into table +local function split_args(argstr) + local t = {} + if not argstr then return t end + for word in argstr:gmatch("%S+") do + table.insert(t, word) + end + return t +end + +local function main(args) + local start_block = 4 + local end_block = 39 -- CHANGE HERE + local data = "00000000" + + local argv = split_args(args) + local opts = parse_args(argv) + + if opts.h then return help() end + if opts.s then start_block = opts.s end + if opts.e then end_block = opts.e end + if opts.d then + local norm = normalize_hex(opts.d) + if not norm then + print("Invalid -d . Use 8 hex chars (e.g. 00000000) or 2 chars (e.g. FF).") + return + end + data = norm + end + + if start_block < 4 then + print("Cannot clean page 0-3. Those are UID and Config pages.") + return + end + if end_block < start_block then + print("End block must be greater than or equal to start page.") + return + end + + -- check if card is present on reader + if not card_present() then + return + end + + print(string.format("Zero-fill (%s) to pages %d..%d", data, start_block, end_block)) + for b = start_block, end_block do + core.console(string.format("hf mfu wrbl -b %d -d %s", b, data)) + end + print("Done.") +end + +main(args) From 44fc91caa690a601187d08b65d973bd5831debfc Mon Sep 17 00:00:00 2001 From: Trigat Date: Sun, 24 Aug 2025 15:39:30 -0500 Subject: [PATCH 3/5] Delete client/luascripts/emv_extract.lua Signed-off-by: Trigat --- client/luascripts/emv_extract.lua | 80 ------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 client/luascripts/emv_extract.lua diff --git a/client/luascripts/emv_extract.lua b/client/luascripts/emv_extract.lua deleted file mode 100644 index d35835459..000000000 --- a/client/luascripts/emv_extract.lua +++ /dev/null @@ -1,80 +0,0 @@ -copyright = "Trigat" -author = "Trigat" -desc = "This script scans an EMV (credit or debit) card and extracts Track 2 information." - -local home = os.getenv("HOME") -local json_file = home .. "/emv_output.json" - -core.console(string.format("emv scan -at %s", json_file)) - -os.execute("sleep 1") - --- Read JSON content -local f = io.open(json_file, "r") -if not f then - print("[!!] Could not open JSON output") - return -end - -local content = f:read("*a") -f:close() - -local track2 = nil -local found = false - --- Iterate over all "value" fields in JSON -for value_field in content:gmatch('"value"%s*:%s*"(.-)"') do - -- Look for 57 13 anywhere in the string - track2 = value_field:match('57%s*13%s*([0-9A-Fa-f%s]+)') - if track2 then - -- Remove spaces to get clean hex - track2 = track2:gsub("%s+", "") - print("[++] Track 2 data found: 57 13 " .. track2 .. "\n") - found = true - break -- remove break if you want all occurrences - end -end - -if not found then - print("[!!] Track 2 data not found in JSON") - return -end - --- Remove 57 + Length prefix (first 4 hex digits = 57 13) -track2 = track2:gsub("^57%x%x", "") - --- Convert hex string into individual nibbles (1 hex digit each) -local nibbles = {} -for hex_digit in track2:gmatch("%x") do - table.insert(nibbles, hex_digit) -end - --- Find 'D' separator -local sep_index -for i, nib in ipairs(nibbles) do - if nib == "D" then - sep_index = i - break - end -end - -if not sep_index then - print("[!!] Could not find 'D' separator in Track 2") - return -end - --- Extract Primary Account Number (all nibbles before D) -local pan = table.concat(nibbles, "", 1, sep_index-1) - --- Expiry is next 4 nibbles after D -local yy = nibbles[sep_index + 1] .. nibbles[sep_index + 2] -local mm = nibbles[sep_index + 3] .. nibbles[sep_index + 4] - --- Service code: next 3 nibbles -local service = table.concat(nibbles, "", sep_index + 5, sep_index + 7) or "N/A" - -print("[++] PAN: " .. pan) -print("[++] Expiry (MM/YY): " .. mm .. "/" .. yy) -print("[++] Service code: " .. service) - -os.execute('rm -f ' .. json_file) From 62fdf5706c6763d0878675eaa5a722363b9ad2a7 Mon Sep 17 00:00:00 2001 From: Trigat Date: Sun, 24 Aug 2025 15:44:15 -0500 Subject: [PATCH 4/5] Update ntag_clean.lua Modified style and format. Signed-off-by: Trigat --- client/luascripts/ntag_clean.lua | 59 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/client/luascripts/ntag_clean.lua b/client/luascripts/ntag_clean.lua index 20cc03303..41ba42a1e 100644 --- a/client/luascripts/ntag_clean.lua +++ b/client/luascripts/ntag_clean.lua @@ -1,22 +1,35 @@ -copyright = "Trigat" -author = "Trigat" -desc = "Zero-fill NTAG pages within a range" +local reader = require('read14a') +local ansicolors = require('ansicolors') -local function help() - print([[ -NTAG_CLEAN - Zero-fill NTAG pages/blocks +copyright = 'Trigat' +author = 'Trigat' +version = 'v0.0.1' +desc = [[ +This script zero-fills NTAG pages in a user-specified range, +or defaults to pages 4–39. -Options: - -h : this help - -s : start block (default 4) - -e : end block (default 39) - -d : data to write (8 hex chars, e.g. 00000000) - If 2 hex chars given (e.g. "FF"), it repeats to 8 ("FFFFFFFF"). +Pages 0–3 (UID and configuration) cannot be erased. +]] +example = [[ + -- Zero-fill default range (pages 4–39) with 0x00 + 1. script run ntag_clean -Running with no options will zero out pages 4-39. + -- Zero-fill a custom range (pages 6–10) + 2. script run ntag_clean -s 6 -e 10 -]]) -end + -- Fill pages 4–39 with 0xFF + 3. script run ntag_clean -d FF +]] +usage = [[ +script run ntag_clean [-s ] [-e ] [-d ] +]] +arguments = [[ + -h : this help + -s : start page (default 4) + -e : end page (default 39) + -d : data to write (8 hex chars, e.g. 00000000) + If 2 hex chars are given (e.g. FF), it repeats to 8 (FFFFFFFF). +]] local function normalize_hex(h) if not h then return nil end @@ -52,7 +65,6 @@ end -- check if card exists local function card_present() - local reader = require("read14a") local tag, err = reader.read() if not tag then print("No card detected. Aborting.") @@ -63,6 +75,19 @@ local function card_present() return true end +-- Help output +local function help() + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end + -- convert global args string into table local function split_args(argstr) local t = {} @@ -75,7 +100,7 @@ end local function main(args) local start_block = 4 - local end_block = 39 -- CHANGE HERE + local end_block = 39 -- Can be modified by user local data = "00000000" local argv = split_args(args) From 3bc79d0e8db287e3bd53d2c227ce9f64af3d84f8 Mon Sep 17 00:00:00 2001 From: Trigat Date: Sun, 24 Aug 2025 16:02:26 -0500 Subject: [PATCH 5/5] Update CHANGELOG.md Added description for new ntag_clean Lua script Signed-off-by: Trigat --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ac7f3c5..f1d7479f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `ntag_clean.lua` script for easier NTAG memory wipe (@trigat) - Changed from Bigbuf malloc to Bigbuf calloc calls on device side (@iceman1001) - Added `lf t55xx view` - now viewing of T55XX dump files is possible (@iceman1001) - Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn`