| 1 | desc = [[\r |
| 2 | \r |
| 3 | .-----------------------------------------------------------------.\r |
| 4 | / .-. .-. \\r |
| 5 | | / \ BruteSim / \ |\r |
| 6 | | |\_. | (bruteforce simulation for multiple tags) | /| |\r |
| 7 | |\| | /| by |\ | |/|\r |
| 8 | | `---' | Kenzy Carey | `---' |\r |
| 9 | | | | |\r |
| 10 | | |-----------------------------------------------------| |\r |
| 11 | \ | | /\r |
| 12 | \ / \ /\r |
| 13 | `---' `---'\r |
| 14 | ]]\r |
| 15 | author = [[ Kenzy Carey ]]\r |
| 16 | usage = [[\r |
| 17 | \r |
| 18 | USAGE:\r |
| 19 | script run brutesim -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction\r |
| 20 | option argument description\r |
| 21 | ------ -------- -----------\r |
| 22 | -r *see below RFID Tag: the RFID tag to emulate\r |
| 23 | -f 0-999 Facility Code: The facility code (dfx: country id, 14a: type)\r |
| 24 | -b 0-65535 Base Card Number: base card number to start from\r |
| 25 | -c 1-65536 Count: number of cards to try\r |
| 26 | -t .0-99999, pause Timeout: timeout between cards (use the word 'pause' to wait for user input)\r |
| 27 | -d up, down Direction: direction to move through card numbers\r |
| 28 | -h Show this\r |
| 29 | \r |
| 30 | *SUPPORTED TAGS: pyramid, awid, fdx, jablotron, noralsy, presco, visa2000, 14a, hid\r |
| 31 | \r |
| 32 | EXAMPLE: \r |
| 33 | script run brutesim -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down\r |
| 34 | (the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card)\r |
| 35 | ]]\r |
| 36 | \r |
| 37 | -- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable.\r |
| 38 | -- The idea was based on proxbrute, but i needed more options, and support for different readers.\r |
| 39 | -- I dont know LUA, so I used Brian Redbeards lf_bulk_program.lua script as a starting point, sorry if its kludgy.\r |
| 40 | \r |
| 41 | getopt = require('getopt') -- Used to get get command line arguments\r |
| 42 | bit32 = require('bit32') -- Used to convert FC/CN to hex\r |
| 43 | \r |
| 44 | local function isempty(s) -- Check if a string is empty\r |
| 45 | return s == nil or s == ''\r |
| 46 | end\r |
| 47 | \r |
| 48 | local function main(args)\r |
| 49 | \r |
| 50 | print("") -- Print a blank line to make things look cleaner\r |
| 51 | \r |
| 52 | for o, a in getopt.getopt(args, 'r:f:b:c:t:d:h') do -- Populate command like arguments\r |
| 53 | if o == 'r' then rfidtag = a end\r |
| 54 | if o == 'f' then facility = a end\r |
| 55 | if o == 'b' then baseid = a end\r |
| 56 | if o == 'c' then count = a end\r |
| 57 | if o == 't' then timeout = a end\r |
| 58 | if o == 'd' then direction = a end\r |
| 59 | if o == 'h' then return print(usage) end\r |
| 60 | end\r |
| 61 | \r |
| 62 | if isempty(rfidtag) then -- Check to see if -r argument was passed \r |
| 63 | print("You must supply the flag -r (rfid tag)")\r |
| 64 | print(usage)\r |
| 65 | return\r |
| 66 | end\r |
| 67 | -- Check what RFID Tag we are using\r |
| 68 | if rfidtag == 'pyramid' then -- For eaach RFID Tag:\r |
| 69 | consolecommand = 'lf pyramid sim' -- Set the console command\r |
| 70 | rfidtagname = 'Farpointe/Pyramid' -- Set the display name\r |
| 71 | facilityrequired = 1 -- Set if FC is required\r |
| 72 | elseif rfidtag == 'awid' then\r |
| 73 | consolecommand = 'lf awid sim'\r |
| 74 | rfidtagname = 'AWID'\r |
| 75 | facilityrequired = 1\r |
| 76 | elseif rfidtag == 'fdx' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ \r |
| 77 | consolecommand = 'lf fdx sim'\r |
| 78 | rfidtagname = 'FDX-B'\r |
| 79 | facilityrequired = 1\r |
| 80 | elseif rfidtag == 'jablotron' then\r |
| 81 | consolecommand = 'lf jablotron sim'\r |
| 82 | rfidtagname = 'Jablotron'\r |
| 83 | facilityrequired = 0\r |
| 84 | elseif rfidtag == 'noralsy' then\r |
| 85 | consolecommand = 'lf noralsy sim'\r |
| 86 | rfidtagname = 'Noralsy'\r |
| 87 | facilityrequired = 0\r |
| 88 | elseif rfidtag == 'presco' then\r |
| 89 | consolecommand = 'lf presco sim d'\r |
| 90 | rfidtagname = 'Presco'\r |
| 91 | facilityrequired = 0\r |
| 92 | elseif rfidtag == 'visa2000' then\r |
| 93 | consolecommand = 'lf visa2000 sim'\r |
| 94 | rfidtagname = 'Visa2000'\r |
| 95 | facilityrequired = 0\r |
| 96 | elseif rfidtag == '14a' then\r |
| 97 | consolecommand = 'hf 14a sim'\r |
| 98 | if facility == "1" then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code\r |
| 99 | elseif facility == "2" then rfidtagname = 'MIFARE Ultralight'\r |
| 100 | elseif facility == "3" then rfidtagname = 'MIFARE Desfire'\r |
| 101 | elseif facility == "4" then rfidtagname = 'ISO/IEC 14443-4'\r |
| 102 | elseif facility == "5" then rfidtagname = 'MIFARE Tnp3xxx'\r |
| 103 | else \r |
| 104 | print("Invalid 14a type (-f) supplied. Must be 1-5")\r |
| 105 | print(usage)\r |
| 106 | return\r |
| 107 | end\r |
| 108 | facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC\r |
| 109 | elseif rfidtag == 'hid' then\r |
| 110 | consolecommand = 'lf hid sim'\r |
| 111 | rfidtagname = 'HID'\r |
| 112 | facilityrequired = 1\r |
| 113 | else -- Display error and exit out if bad RFID tag was supplied\r |
| 114 | print("Invalid rfid tag (-r) supplied")\r |
| 115 | print(usage)\r |
| 116 | return\r |
| 117 | end\r |
| 118 | \r |
| 119 | if isempty(baseid) then -- Display error and exit out if no starting id is set\r |
| 120 | print("You must supply the flag -b (base id)")\r |
| 121 | print(usage)\r |
| 122 | return\r |
| 123 | end\r |
| 124 | \r |
| 125 | if isempty(count) then -- Display error and exit out of no count is set\r |
| 126 | print("You must supply the flag -c (count)")\r |
| 127 | print(usage)\r |
| 128 | return\r |
| 129 | end\r |
| 130 | \r |
| 131 | if facilityrequired == 1 then -- If FC is required\r |
| 132 | facilitymessage = " - Facility Code: " -- Add FC to status message\r |
| 133 | if isempty(facility) then -- If FC was left blank, display warning and set FC to 0 \r |
| 134 | print("Using 0 for the facility code as -f was not supplied")\r |
| 135 | facility = 0 \r |
| 136 | end\r |
| 137 | else -- If FC is not required\r |
| 138 | facility = "" -- Clear FC\r |
| 139 | facilitymessage = "" -- Remove FC from status message\r |
| 140 | end\r |
| 141 | \r |
| 142 | if isempty(timeout) then -- If timeout was not supplied, show warning and set timeout to 0\r |
| 143 | print("Using 0 for the timeout as -t was not supplied")\r |
| 144 | timeout = 0 \r |
| 145 | end\r |
| 146 | \r |
| 147 | if isempty(direction) then -- If direction was not supplied, show warning and set direction to down\r |
| 148 | print("Using down for direction as -d was not supplied")\r |
| 149 | direction = 'down' \r |
| 150 | end\r |
| 151 | \r |
| 152 | if tonumber(count) < 1 then\r |
| 153 | print("Count -c must be set to 1 or higher")\r |
| 154 | return\r |
| 155 | else\r |
| 156 | count = count -1 -- Make our count accurate by removing 1, because math\r |
| 157 | end \r |
| 158 | \r |
| 159 | if direction == 'down' then -- If counting down, set up our for loop to count down\r |
| 160 | endid = baseid - count\r |
| 161 | fordirection = -1\r |
| 162 | elseif direction == 'up' then -- If counting up, set our for loop to count up\r |
| 163 | endid = baseid + count\r |
| 164 | fordirection = 1\r |
| 165 | else -- If invalid direction was set, show warning and set up our for loop to count down\r |
| 166 | print("Invalid direction (-d) supplied, using down")\r |
| 167 | endid = baseid - count\r |
| 168 | fordirection = -1\r |
| 169 | end\r |
| 170 | \r |
| 171 | -- The code below was blatantly stolen from Brian Redbeard's lf_bulk_program.lua script \r |
| 172 | function toBits(num,bits)\r |
| 173 | bits = bits or math.max(1, select(2, math.frexp(num)))\r |
| 174 | local t = {}\r |
| 175 | for b = bits, 1, -1 do\r |
| 176 | t[b] = math.fmod(num, 2)\r |
| 177 | num = math.floor((num - t[b]) / 2)\r |
| 178 | end\r |
| 179 | return table.concat(t)\r |
| 180 | end\r |
| 181 | \r |
| 182 | local function evenparity(s)\r |
| 183 | local _, count = string.gsub(s, "1", "")\r |
| 184 | local p = count % 2\r |
| 185 | if (p == 0) then\r |
| 186 | return(false)\r |
| 187 | else\r |
| 188 | return(true)\r |
| 189 | end\r |
| 190 | end\r |
| 191 | \r |
| 192 | local function isempty(s)\r |
| 193 | return s == nil or s == ''\r |
| 194 | end\r |
| 195 | \r |
| 196 | local function cardHex(i,f)\r |
| 197 | fac = bit32.lshift(f,16)\r |
| 198 | id = bit32.bor(i, fac)\r |
| 199 | stream=toBits(id,26)\r |
| 200 | high = evenparity(string.sub(stream,0,12)) and 1 or 0\r |
| 201 | low = not evenparity(string.sub(stream,13)) and 1 or 0\r |
| 202 | bits = bit32.bor(bit32.lshift(id,1), low)\r |
| 203 | bits = bit32.bor(bits, bit32.lshift(high,25))\r |
| 204 | preamble = bit32.bor(0, bit32.lshift(1,5))\r |
| 205 | bits = bit32.bor(bits, bit32.lshift(1,26))\r |
| 206 | return ("%04x%08x"):format(preamble,bits)\r |
| 207 | end\r |
| 208 | -- End stolen code \r |
| 209 | \r |
| 210 | \r |
| 211 | print("") -- Display status message\r |
| 212 | print("BruteForcing "..rfidtagname..""..facilitymessage..""..facility.." - CardNumber Start: "..baseid.." - CardNumber End: "..endid.." - TimeOut: "..timeout)\r |
| 213 | print("")\r |
| 214 | for cardnum = baseid,endid,fordirection do -- Loop through for each count (-c)\r |
| 215 | if rfidtag == 'hid' then cardnum = cardHex(cardnum, facility) end -- If rfid tag is set to HID, convert card to HEX using the stolen code above \r |
| 216 | core.console(consolecommand..' '..facility..' '..cardnum) -- Send command to proxmark\r |
| 217 | if timeout == 'pause' then -- If timeout is set to pause, wait for user input\r |
| 218 | print("Press enter to continue ...")\r |
| 219 | io.read()\r |
| 220 | else -- Otherwise sleep for timeout duration\r |
| 221 | os.execute("sleep "..timeout.."")\r |
| 222 | end\r |
| 223 | end\r |
| 224 | core.console('hw ping') -- Ping the proxmark to stop emulation and see if its still responding\r |
| 225 | \r |
| 226 | end -- Go bye bye\r |
| 227 | \r |
| 228 | \r |
| 229 | main(args) -- Do the thing\r |