From 79b19c5f90e819d6589fb61e49a712c1e302ba45 Mon Sep 17 00:00:00 2001 From: Brian 'Redbeard' Harrington Date: Tue, 18 Apr 2017 08:24:42 -0700 Subject: [PATCH] contrib: LF Programming script Submitting a well documented Lua script whose purpose is to do bulk, repeated, sequential programming of RFID tags. Could be useful for users of the ACCX Open Access 4.0 or other simple RFID systems. Tags programmed with this would potentially be vulnerabile to bisection attacks, etc... But honestly, they likely would have been anyway. To use run from within the proxmark3 client: ``` proxmark3> script run lf_bulk_program.lua -b 1000 -c 5 -f 23 --- Executing: lf_bulk_program.lua, args '-b 1000 -c 5 -f 23' Press enter to program card 1000:23 (hex: 0020042e07d1) Cloning tag with ID 20042e07d1 Press enter to program card 1001:23 (hex: 0020042e07d2) Cloning tag with ID 20042e07d2 Press enter to program card 1002:23 (hex: 0020042e07d4) Cloning tag with ID 20042e07d4 Press enter to program card 1003:23 (hex: 0020042e07d7) Cloning tag with ID 20042e07d7 Press enter to program card 1004:23 (hex: 0020042e07d8) Cloning tag with ID 20042e07d8 -----Finished ``` --- client/scripts/lf_bulk_program.lua | 158 +++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 client/scripts/lf_bulk_program.lua diff --git a/client/scripts/lf_bulk_program.lua b/client/scripts/lf_bulk_program.lua new file mode 100644 index 00000000..4758c203 --- /dev/null +++ b/client/scripts/lf_bulk_program.lua @@ -0,0 +1,158 @@ +-- +-- lf_bulk_program.lua - A tool to clone a large number of tags at once. +-- Updated 2017-04-18 +-- +-- The getopt-functionality is loaded from pm3/client/lualibs/getopt.lua +-- Have a look there for further details +getopt = require('getopt') +bit32 = require('bit32') + +usage = [[ script run lf_bulk_program.lua -f facility -b base_id_num -c count + + e.g: + script run lf_bulk_program.lua -f 1 -b 1000 -c 10 +]] +author = "Brian Redbeard" +desc =[[ +Perform bulk enrollment of 26 bit H10301 style RFID Tags +For more info, check the comments in the code +]] + +--[[Implement a function to simply visualize the bitstream in a text format +--This is especially helpful for troubleshooting bitwise math issues]]-- +function toBits(num,bits) + -- returns a table of bits, most significant first. + bits = bits or math.max(1, select(2, math.frexp(num))) + local t = {} -- will contain the bits + for b = bits, 1, -1 do + t[b] = math.fmod(num, 2) + num = math.floor((num - t[b]) / 2) + end + return table.concat(t) +end + +--[[Likely, I'm an idiot, but I couldn't find any parity functions in Lua + This can also be done with a combination of bitwise operations (in fact, + is the canonically "correct" way to do it, but my brain doesn't just + default to this and so counting some ones is good enough for me]]-- +local function evenparity(s) + local _, count = string.gsub(s, "1", "") + + local p = count % 2 + if (p == 0) then + return(false) + else + return(true) + end +end + + +local function isempty(s) + return s == nil or s == '' +end + +--[[The Proxmark3 "clone" functions expect the data to be in hex format so + take the card id number and facility ID as arguments and construct the + hex. This should be easy enough to extend to non 26bit formats]]-- +local function cardHex(i,f) + fac = bit32.lshift(f,16) + id = bit32.bor(i, fac) + stream=toBits(id,26) + + --As the function defaults to even parity and returns a boolean, + --perform a 'not' function to get odd parity + high = evenparity(string.sub(stream,0,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) + bits = bit32.bor(bits, bit32.lshift(high,25)) + + --Since the lua library bit32 is (obviously) 32 bits and we need to + --encode 36 bits to properly do a 26 bit tag with the preamble we need + --to create a higher order and lower order component which we will + --then assemble in the return. The math above defines the proper + --encoding as per HID/Weigand/etc. These bit flips are due to the + --format length check on bit 38 (cmdlfhid.c:64) and + --bit 31 (cmdlfhid.c:66). + preamble = bit32.bor(0, bit32.lshift(1,5)) + bits = bit32.bor(bits, bit32.lshift(1,26)) + + return ("%04x%08x"):format(preamble,bits) + +end + +local function main(args) + + --I really wish a better getopt function would be brought in supporting + --long arguments, but it seems this library was chosen for BSD style + --compatibility + for o, a in getopt.getopt(args, 'f:b:c:h') do + if o == 'f' then + if isempty(a) then + print("You did not supply a facility code, using 0") + facility = 0 + else + facility = a + end + elseif o == 'b' then + if isempty(a) then + print("You must supply the flag -b (base id)") + return + else + baseid = a + end + elseif o == 'c' then + if isempty(a) then + print("You must supply the flag -c (count)") + return + else + count = a + end + elseif o == 'h' then + print(desc) + print(usage) + return + end + end + + --Due to my earlier complaints about how this specific getopt library + --works, specifying ":" does not enforce supplying a value, thus we + --need to do these checks all over again. + + if isempty(baseid) then + print("You must supply the flag -b (base id)") + print(usage) + return + end + + if isempty(count) then + print("You must supply the flag -c (count)") + print(usage) + return + end + + --If the facility ID is non specified, ensure we code it as zero + if isempty(facility) then + print("Using 0 for the facility code as -f was not supplied") + facility = 0 + end + + --The next baseid + count function presents a logic/UX conflict + --where users specifying -c 1 (count = 1) would try to program two + --tags. This makes it so that -c 0 & -c 1 both code one tag, and all + --other values encode the expected amount. + if tonumber(count) > 0 then count = count -1 end + + endid = baseid + count + + for cardnum = baseid,endid do + local card = cardHex(cardnum, facility) + print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")") + --This would be better with "press any key", but we'll take + --what we can get. + io.read() + core.console( ('lf hid clone %s'):format(card) ) + end +end + + +main(args) -- 2.39.5