]>
Commit | Line | Data |
---|---|---|
1 | local cmds = require('commands') | |
2 | local getopt = require('getopt') | |
3 | local bin = require('bin') | |
4 | local lib14a = require('read14a') | |
5 | local utils = require('utils') | |
6 | local md5 = require('md5') | |
7 | ||
8 | example =[[ | |
9 | 1. script run tnp3 | |
10 | 2. script run tnp3 -k aabbccddeeff | |
11 | ]] | |
12 | author = "Iceman" | |
13 | usage = "script run tnp3 -k <key>" | |
14 | desc =[[ | |
15 | This script will try to dump the contents of a Mifare TNP3xxx card. | |
16 | It will need a valid KeyA in order to find the other keys and decode the card. | |
17 | Arguments: | |
18 | -h - this help | |
19 | -k <key> - Sector 0 Key A. | |
20 | ]] | |
21 | ||
22 | local hashconstant = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' | |
23 | ||
24 | local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds | |
25 | local DEBUG = true -- the debug flag | |
26 | local numBlocks = 64 | |
27 | local numSectors = 16 | |
28 | --- | |
29 | -- A debug printout-function | |
30 | function dbg(args) | |
31 | if not DEBUG then | |
32 | return | |
33 | end | |
34 | ||
35 | if type(args) == "table" then | |
36 | local i = 1 | |
37 | while result[i] do | |
38 | dbg(result[i]) | |
39 | i = i+1 | |
40 | end | |
41 | else | |
42 | print("###", args) | |
43 | end | |
44 | end | |
45 | --- | |
46 | -- This is only meant to be used when errors occur | |
47 | function oops(err) | |
48 | print("ERROR: ",err) | |
49 | end | |
50 | --- | |
51 | -- Usage help | |
52 | function help() | |
53 | print(desc) | |
54 | print("Example usage") | |
55 | print(example) | |
56 | end | |
57 | -- | |
58 | -- Exit message | |
59 | function ExitMsg(msg) | |
60 | print( string.rep('--',20) ) | |
61 | print( string.rep('--',20) ) | |
62 | print(msg) | |
63 | print() | |
64 | end | |
65 | ||
66 | local function show(data) | |
67 | if DEBUG then | |
68 | local formatString = ("H%d"):format(string.len(data)) | |
69 | local _,hexdata = bin.unpack(formatString, data) | |
70 | dbg("Hexdata" , hexdata) | |
71 | end | |
72 | end | |
73 | ||
74 | local function readdumpkeys(infile) | |
75 | t = infile:read("*all") | |
76 | len = string.len(t) | |
77 | local len,hex = bin.unpack(("H%d"):format(len),t) | |
78 | --print(len,hex) | |
79 | return hex | |
80 | end | |
81 | ||
82 | local function waitCmd() | |
83 | local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT) | |
84 | if response then | |
85 | local count,cmd,arg0 = bin.unpack('LL',response) | |
86 | if(arg0==1) then | |
87 | local count,arg1,arg2,data = bin.unpack('LLH511',response,count) | |
88 | return data:sub(1,32) | |
89 | else | |
90 | return nil, "Couldn't read block.." | |
91 | end | |
92 | end | |
93 | return nil, "No response from device" | |
94 | end | |
95 | ||
96 | local function main(args) | |
97 | ||
98 | print( string.rep('--',20) ) | |
99 | print( string.rep('--',20) ) | |
100 | print() | |
101 | ||
102 | local keyA | |
103 | local cmd | |
104 | local err | |
105 | local cmdReadBlockString = 'hf mf rdbl %d A %s' | |
106 | local input = "dumpkeys.bin" | |
107 | ||
108 | -- Arguments for the script | |
109 | for o, a in getopt.getopt(args, 'hk:') do | |
110 | if o == "h" then return help() end | |
111 | if o == "k" then keyA = a end | |
112 | end | |
113 | ||
114 | -- validate input args. | |
115 | keyA = keyA or '4b0b20107ccb' | |
116 | if #(keyA) ~= 12 then | |
117 | return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA)) | |
118 | end | |
119 | ||
120 | result, err = lib14a.read1443a(false) | |
121 | if not result then | |
122 | print(err) | |
123 | return | |
124 | end | |
125 | print((' Found tag : %s'):format(result.name)) | |
126 | ||
127 | core.clearCommandBuffer() | |
128 | ||
129 | if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx | |
130 | print('This is not a TNP3xxx tag. aborting.') | |
131 | return | |
132 | end | |
133 | ||
134 | -- Show info | |
135 | print(('Using keyA : %s'):format(keyA)) | |
136 | print( string.rep('--',20) ) | |
137 | ||
138 | print('Trying to find other keys. ') | |
139 | --core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) | |
140 | ||
141 | -- Reading found keys file | |
142 | local infile = io.open(input, "rb") | |
143 | if infile == nil then | |
144 | return oops('Could not read file ', input) | |
145 | end | |
146 | local akeys = readdumpkeys(infile):sub(0,12*16) | |
147 | ||
148 | --print( ('KEYS: %s'):format(akeys)) | |
149 | ||
150 | print('Reading data need to dump data') | |
151 | ||
152 | -- Read block 0 | |
153 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} | |
154 | err = core.SendCommand(cmd:getBytes()) | |
155 | if err then return oops(err) end | |
156 | local block0, err = waitCmd() | |
157 | if err then return oops(err) end | |
158 | ||
159 | -- Read block 1 | |
160 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA} | |
161 | local err = core.SendCommand(cmd:getBytes()) | |
162 | if err then return oops(err) end | |
163 | local block1, err = waitCmd() | |
164 | if err then return oops(err) end | |
165 | ||
166 | print('Dumping data') | |
167 | ||
168 | -- main loop | |
169 | print('BLOCK MD5 DECRYPTED ASCII' ) | |
170 | ||
171 | local key | |
172 | local keyPosStart = 0 | |
173 | local block | |
174 | for block = 0, numBlocks-1, 1 do | |
175 | local b = (block+1)%4 | |
176 | if b ~= 0 then | |
177 | keyPosStart = (math.floor( block / 4 ) * 12)+1 | |
178 | key = akeys:sub(keyPosStart, keyPosStart + 12 ) | |
179 | --print( ('%02d %s'):format(block, key)) | |
180 | ||
181 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = block ,arg2 = 0,arg3 = 0, data = key} | |
182 | local err = core.SendCommand(cmd:getBytes()) | |
183 | if err then return oops(err) end | |
184 | local blockdata, err = waitCmd() | |
185 | if err then return oops(err) end | |
186 | ||
187 | local base = ('%s%s%02d%s'):format(block0, block1, block, hashconstant) | |
188 | local md5hash = md5.sumhexa(base) | |
189 | local aestest = core.aes(md5hash, blockdata) | |
190 | ||
191 | local _,hex = bin.unpack(("H%d"):format(16),aestest) | |
192 | ||
193 | local hexascii = string.gsub(hex, '(%x%x)', | |
194 | function(value) | |
195 | return string.char(tonumber(value, 16)) | |
196 | end | |
197 | ) | |
198 | ||
199 | print( ('%02d :: %s :: %s :: %s :: %s'):format(block,key,md5hash,hex,hexascii) ) | |
200 | ||
201 | if core.ukbhit() then | |
202 | print("aborted by user") | |
203 | break | |
204 | end | |
205 | end | |
206 | end | |
207 | end | |
208 | ||
209 | main(args) |