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