]>
Commit | Line | Data |
---|---|---|
e108a48a | 1 | local cmds = require('commands') |
2 | local getopt = require('getopt') | |
3 | local utils = require('utils') | |
4 | local lib14a = require('read14a') | |
5 | ||
6 | example = "script iterates over all possible sectors for a tag and runs hardnested attack against them to collect the keys." | |
7 | author = "Iceman" | |
8 | desc = | |
9 | [[ | |
10 | This script iterates over all possible sectors for a tag and runs hardnested attack against them to collect the keys. | |
11 | ||
12 | Arguments: | |
13 | -k Known key, 6 bytes (12 hex digits) | |
14 | Examples : | |
15 | script hard -b 112233445566 | |
16 | ]] | |
17 | ||
18 | local numBlocks = 64 | |
19 | local numSectors = 16 | |
20 | local DEBUG = TRUE | |
21 | --- | |
22 | -- A debug printout-function | |
23 | function dbg(args) | |
24 | if not DEBUG then return end | |
25 | ||
26 | if type(args) == "table" then | |
27 | local i = 1 | |
28 | while result[i] do | |
29 | dbg(result[i]) | |
30 | i = i+1 | |
31 | end | |
32 | else | |
33 | print("###", args) | |
34 | end | |
35 | end | |
36 | --- | |
37 | -- This is only meant to be used when errors occur | |
38 | function oops(err) | |
39 | print("ERROR: ",err) | |
40 | return nil,err | |
41 | end | |
42 | --- | |
43 | -- Usage help | |
44 | function help() | |
45 | print(desc) | |
46 | print("Example usage") | |
47 | print(example) | |
48 | end | |
49 | -- | |
50 | -- Exit message | |
51 | function ExitMsg(msg) | |
52 | print( string.rep('--',20) ) | |
53 | print( string.rep('--',20) ) | |
54 | print(msg) | |
55 | print() | |
56 | end | |
d1e197e9 | 57 | -- A little helper to place an item first in the list |
58 | local function placeFirst(akey, list) | |
59 | akey = akey:lower() | |
60 | if list[1] == akey then | |
61 | -- Already at pole position | |
62 | return list | |
63 | end | |
64 | local result = {akey} | |
65 | --print(("Putting '%s' first"):format(akey)) | |
66 | for i,v in ipairs(list) do | |
67 | if v ~= akey then | |
68 | result[#result+1] = v | |
69 | end | |
70 | end | |
71 | return result | |
72 | end | |
73 | -- A function to display the results | |
74 | -- TODO: iceman 2016, still screws up output when a key is not found. | |
75 | local function displayresults(results) | |
76 | local sector, blockNo, keyA, keyB, succA, succB, _ | |
77 | ||
78 | print("|---|----------------|---|----------------|---|") | |
79 | print("|sec|key A |res|key B |res|") | |
80 | print("|---|----------------|---|----------------|---|") | |
81 | ||
82 | for sector,_ in pairs(results) do | |
83 | succA, succB, keyA, keyB = unpack(_) | |
84 | print(("|%03d| %s | %s | %s | %s |"):format(sector, keyA, succA, keyB, succB)) | |
85 | end | |
86 | print("|---|----------------|---|----------------|---|") | |
87 | ||
88 | end | |
e108a48a | 89 | --- |
90 | -- a simple selftest function, | |
91 | local function selftest() | |
92 | return nil | |
93 | end | |
94 | ||
95 | --- | |
96 | -- The main entry point | |
97 | function main(args) | |
98 | ||
99 | local blockno = '00' | |
100 | local keytype = 0 --A 01==B | |
101 | local key = 'fc00018778f7' | |
102 | local trgkey = '' | |
d1e197e9 | 103 | local numSectors = 16 |
e108a48a | 104 | |
e108a48a | 105 | -- Read the parameters |
d1e197e9 | 106 | for o, a in getopt.getopt(args, 'hk:') do |
e108a48a | 107 | if o == "h" then return help() end |
108 | if o == "k" then key = a end | |
e108a48a | 109 | end |
110 | ||
111 | -- Turn off Debug | |
112 | local cmdSetDbgOff = "hf mf dbg 0" | |
113 | core.console( cmdSetDbgOff) | |
114 | -- identify tag | |
115 | result, err = lib14a.read1443a(false) | |
116 | if not result then | |
117 | return oops(err) | |
118 | end | |
119 | core.clearCommandBuffer() | |
120 | ||
121 | -- Show tag info | |
122 | print((' Found tag %s'):format(result.name)) | |
123 | ||
d1e197e9 | 124 | if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k |
125 | -- IFARE Classic 4K offers 4096 bytes split into forty sectors, | |
126 | -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. | |
127 | numSectors = 40 | |
128 | elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k | |
129 | -- 1K offers 1024 bytes of data storage, split into 16 sector | |
130 | numSectors = 16 | |
131 | elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k | |
132 | -- MIFARE Classic mini offers 320 bytes split into five sectors. | |
133 | numSectors = 5 | |
134 | elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" | |
135 | numSectors = 32 | |
136 | else | |
137 | print("I don't know how many sectors there are on this type of card, defaulting to 16") | |
e108a48a | 138 | end |
d1e197e9 | 139 | |
140 | result = {} | |
141 | for sector=1,numSectors do | |
142 | ||
143 | --[[ | |
144 | The mifare Classic 1k card has 16 sectors of 4 data blocks each. | |
145 | The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining | |
146 | 8 sectors consist of 16 data blocks. | |
147 | --]] | |
148 | local trgblockno = sector * 4 - 1 | |
149 | if sector > 32 then | |
150 | trgblockno = 32 * 4 + (sector-32) * 16 -1 | |
151 | end | |
152 | ||
153 | trgblockno = ("%02d"):format(trgblockno) | |
154 | ||
155 | local succA = 1 | |
156 | local succB = 1 | |
157 | local errA, keyA = core.hardnested(blockno, keytype, key, trgblockno, '0', trgkey, 0,0,0,0) | |
158 | keyA = keyA or "" | |
b7f40ee2 | 159 | if errA == nil or errA > 0 then succA = 0 end |
d1e197e9 | 160 | |
161 | local errB, keyB = core.hardnested(blockno, keytype, key, trgblockno, '1', trgkey, 0,0,0,0) | |
162 | keyB = keyB or "" | |
b7f40ee2 | 163 | if errB == nil or errB > 0 then succB = 0 end |
d1e197e9 | 164 | result[sector] = { succA, succB, utils.ConvertAsciiToHex(keyA), utils.ConvertAsciiToHex(keyB) } |
165 | ||
166 | -- Check if user aborted | |
167 | if core.ukbhit() then | |
168 | print("Aborted by user") | |
169 | break | |
170 | end | |
e108a48a | 171 | end |
d1e197e9 | 172 | displayresults(result) |
e108a48a | 173 | end |
174 | ||
175 | main(args) |