Merge pull request #2654 from Antiklesys/master
[RRG-proxmark3.git] / client / luascripts / multi_bruteforce.lua
blobab11c11c98c1f551df4054f683960863661fa6d4
1 local getopt = require('getopt')
2 local ansicolors = require('ansicolors')
4 copyright = ''
5 author = 'Kenzy Carey'
6 version = 'v1.0.3'
7 desc = [[
9 .-----------------------------------------------------------------.
10 / .-. .-. \
11 | / \ BruteSim / \ |
12 | |\_. | (bruteforce simulation for multiple tags) | /| |
13 |\| | /| by |\ | |/|
14 | `---' | Kenzy Carey | `---' |
15 | | | |
16 | |-----------------------------------------------------| |
17 \ | | /
18 \ / \ /
19 `---' `---'
21 *SUPPORTED TAGS: pyramid, awid, fdxb, jablotron, noralsy, presco, visa2000, 14a, hid
23 This script uses the Proxmark3 implementations of simulation to bruteforce given ranges of id.
24 It uses both LF and HF simulations.
26 -- Author note
27 -- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable.
28 -- The idea was based on proxbrute, but i needed more options, and support for different readers.
29 -- I don't know LUA, so I used Brian Redbeards lf_hid_bulkclone.lua script as a starting point, sorry if its kludgy.
32 example = [[
33 -- (the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card)
35 script run multi_bruteforce -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down
37 usage = [[
38 script run multi_bruteforce -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction
40 arguments = [[
41 -h this help
42 -r *see below RFID Tag: the RFID tag to emulate
43 pyramid
44 awid
45 fdxb
46 jablotron
47 noralsy
48 presco
49 visa2000
50 14a
51 hid
53 -f 0-999 facility code (dfx: country id, 14a: type)
54 -b 0-65535 base card number to start from
55 -c 1-65536 number of cards to try
56 -t .0-99999, pause timeout between cards (use the word 'pause' to wait for user input)
57 -d up, down direction to move through card numbers
60 local DEBUG = true
61 local bor = bit32.bor
62 local bxor = bit32.bxor
63 local lshift = bit32.lshift
64 ---
65 -- A debug printout-function
66 local function dbg(args)
67 if not DEBUG then return end
68 if type(args) == 'table' then
69 local i = 1
70 while result[i] do
71 dbg(result[i])
72 i = i+1
73 end
74 else
75 print('###', args)
76 end
77 end
78 ---
79 -- This is only meant to be used when errors occur
80 local function oops(err)
81 print('ERROR:', err)
82 core.clearCommandBuffer()
83 return nil, err
84 end
85 ---
86 -- Usage help
87 local function help()
88 print(copyright)
89 print(author)
90 print(version)
91 print(desc)
92 print(ansicolors.cyan..'Usage'..ansicolors.reset)
93 print(usage)
94 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
95 print(arguments)
96 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
97 print(example)
98 end
100 -- Exit message
101 local function exitMsg(msg)
102 print( string.rep('--',20) )
103 print( string.rep('--',20) )
104 print(msg)
105 print()
108 -- Check if a string is empty
109 local function isempty(s)
110 return s == nil or s == ''
113 -- The code below was blatantly stolen from Brian Redbeard's lf_hid_bulkclone.lua script
114 local function toBits(num, bits)
115 bits = bits or math.max(1, select(2, math.frexp(num)))
116 local t = {}
117 for b = bits, 1, -1 do
118 t[b] = math.fmod(num, 2)
119 num = math.floor((num - t[b]) / 2)
121 return table.concat(t)
124 -- check for parity in bit-string.
125 local function evenparity(s)
126 local _, count = string.gsub(s, '1', '')
127 local p = count % 2
128 if (p == 0) then
129 return false
131 return true
134 -- calcs hex for HID
135 local function cardHex(i, f)
136 fac = lshift(f, 16)
137 id = bor(i, fac)
138 stream = toBits(id, 26)
139 high = evenparity(string.sub(stream, 0, 12)) and 1 or 0
140 low = not evenparity(string.sub(stream, 13)) and 1 or 0
141 bits = bor(lshift(id, 1), low)
142 bits = bor(bits, lshift(high, 25))
143 preamble = bor(0, lshift(1, 5))
144 bits = bor(bits, lshift(1, 26))
145 return ('%04x%08x'):format(preamble, bits)
149 local function main(args)
151 print( string.rep('--',20) )
152 print( string.rep('--',20) )
153 print()
155 if #args == 0 then return help() end
157 for o, a in getopt.getopt(args, 'r:f:b:c:t:d:h') do -- Populate command like arguments
158 if o == 'r' then rfidtag = a end
159 if o == 'f' then facility = a end
160 if o == 'b' then baseid = a end
161 if o == 'c' then count = a end
162 if o == 't' then timeout = a end
163 if o == 'd' then direction = a end
164 if o == 'h' then return print(usage) end
167 -- Check to see if -r argument was passed
168 if isempty(rfidtag) then
169 print('You must supply the flag -r (rfid tag)')
170 print(usage)
171 return
174 -- Check what RFID Tag we are using
175 if rfidtag == 'pyramid' then
176 consolecommand = 'lf pyramid sim' -- set the console command
177 rfidtagname = 'Farpointe/Pyramid' -- set the display name
178 facilityrequired = 1 -- set if FC is required
179 elseif rfidtag == 'awid' then
180 consolecommand = 'lf awid sim'
181 rfidtagname = 'AWID'
182 facilityrequired = 1
183 elseif rfidtag == 'fdxb' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯
184 consolecommand = 'lf fdxb sim'
185 rfidtagname = 'FDX-B'
186 facilityrequired = 1
187 elseif rfidtag == 'jablotron' then
188 consolecommand = 'lf jablotron sim'
189 rfidtagname = 'Jablotron'
190 facilityrequired = 0
191 elseif rfidtag == 'noralsy' then
192 consolecommand = 'lf noralsy sim'
193 rfidtagname = 'Noralsy'
194 facilityrequired = 0
195 elseif rfidtag == 'presco' then
196 consolecommand = 'lf presco sim -d'
197 rfidtagname = 'Presco'
198 facilityrequired = 0
199 elseif rfidtag == 'visa2000' then
200 consolecommand = 'lf visa2000 sim'
201 rfidtagname = 'Visa2000'
202 facilityrequired = 0
203 elseif rfidtag == '14a' then
204 consolecommand = 'hf 14a sim -t'
205 if facility == '1' then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code
206 elseif facility == '2' then rfidtagname = 'MIFARE Ultralight'
207 elseif facility == '3' then rfidtagname = 'MIFARE Desfire'
208 elseif facility == '4' then rfidtagname = 'ISO/IEC 14443-4'
209 elseif facility == '5' then rfidtagname = 'MIFARE Tnp3xxx'
210 else
211 print('Invalid 14a type (-f) supplied. Must be 1-5')
212 print(usage)
213 return
215 facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC
216 elseif rfidtag == 'hid' then
217 consolecommand = 'lf hid sim -r'
218 rfidtagname = 'HID'
219 facilityrequired = 1
220 else -- Display error and exit out if bad RFID tag was supplied
221 print('Invalid rfid tag (-r) supplied')
222 print(usage)
223 return
226 if isempty(baseid) then -- Display error and exit out if no starting id is set
227 print('You must supply the flag -b (base id)')
228 print(usage)
229 return
232 if isempty(count) then -- Display error and exit out of no count is set
233 print('You must supply the flag -c (count)')
234 print(usage)
235 return
238 if facilityrequired == 1 then -- If FC is required
239 facilitymessage = ' - Facility Code: ' -- Add FC to status message
240 if isempty(facility) then -- If FC was left blank, display warning and set FC to 0
241 print('Using 0 for the facility code as -f was not supplied')
242 facility = 0
244 else -- If FC is not required
245 facility = '' -- Clear FC
246 facilitymessage = '' -- Remove FC from status message
249 if isempty(timeout) then -- If timeout was not supplied, show warning and set timeout to 0
250 print('Using 0 for the timeout as -t was not supplied')
251 timeout = 0
254 if isempty(direction) then -- If direction was not supplied, show warning and set direction to down
255 print("Using down for direction as -d was not supplied")
256 direction = 'down'
259 if tonumber(count) < 1 then
260 print('Count -c must be set to 1 or higher')
261 return
262 else
263 count = count -1 -- Make our count accurate by removing 1, because math
266 if direction == 'down' then -- If counting down, set up our for loop to count down
267 endid = baseid - count
268 fordirection = -1
269 elseif direction == 'up' then -- If counting up, set our for loop to count up
270 endid = baseid + count
271 fordirection = 1
272 else -- If invalid direction was set, show warning and set up our for loop to count down
273 print('Invalid direction (-d) supplied, using down')
274 endid = baseid - count
275 fordirection = -1
279 -- display status message
280 print('')
281 print('BruteForcing '..rfidtagname..''..facilitymessage..''..facility..' - CardNumber Start: '..baseid..' - CardNumber End: '..endid..' - TimeOut: '..timeout)
282 print("")
284 -- loop through for each count (-c)
285 for cardnum = baseid, endid, fordirection do
287 -- If rfid tag is set to HID, convert card to HEX using the stolen code above
288 if rfidtag == 'hid' then cardnum = cardHex(cardnum, facility) end
290 -- send command to proxmark
291 core.console(consolecommand..' '..facility..' '..cardnum)
293 if timeout == 'pause' then
294 print('Press enter to continue ...')
295 io.read()
296 else
297 os.execute('sleep '..timeout..'')
301 -- ping the proxmark to stop emulation and see if its still responding
302 core.console('hw ping')
305 main(args)