Poking around on a Mifare card: LibNFC crash course
Posted on: Sun 07 Feb 09:02:11 2010 under technology
Poking around on a MiFare card: LibNFC crash course
Background
libnfc is a library for communicating with ISO14443
RFID tags. You might know these things for their use in smart card
ticketing systems such as Oyster, Octopus, Snapper and myki. But
they are also present in other forms such as photocopy cards,
student ID's, building access controls* and passports.
Two forms of ISO14443 dominate: Felica, which debuted in Hong
Kong's Octopus RFID ticket and spread across Asia soon after, and
Mifare, which dominates just about everything else.
* It should be noted many building security systems use a
lower frequency, non-ISO14443 system.
Hardware
Our weapon
libnfc works with NXP PN53x series chipsets, which feature USB
connectivity, giving rise to their use in smartcard reload gadgets
such as the Snapper USB 'Feeder'. They can even emulate a RFID
card!
Snapper were kind enough to ship a USB feeder over to me in
Australia. Thanks guys!
The test subject
The best way to experiment with RFID cards would be to source
a blank one. But I didn't have a blank card handy at the time of
writing, so I will examine an existing one. My previous research
indicated that I would be able to modify any unused sectors on the
card, but not modify used sectors that I do not have the keys
for.
In this article I will be examining a common RFID tag, a
photocopy card. I've replaced the artwork on the card with an
identical image to avoid naming the vendor in question.
This card is a Mifare Classic 1k. It has 1024 bytes of memory,
but stores only 768 bytes after the configuration and
authentication data.
Memory on the Mifare card is split into 16 sectors of 4
blocks, three 16 byte blocks of data, one 16 byte block for
security configuration.
Heres a diagram which I have stolen from this PDF
| Sector 1 | Block 0 - read only block reserved for card UID | Block 1 | Block 2 |
Block 3 - sector trailer and security configuration |
| Sector 2 | Block 4 | Block 5 | Block 6 | Block 7 - sector trailer and security configuration |
| Sector n | Block (n*4)-4 | ... | ... | Block (n*4)-1 - sector trailer and security configuration |
| Sector 15 | Block 60 | Block 61 | Block 62 | Block 63 - sector trailer and end of card |
Mifare Classic has been cracked several times over the years,
not because of a paltry 48 bit key length, but because of severe
weaknesses of its Crypto-1 ("Crapto1") encryption algorithm.
See Wikipedia for a history of Mifare
Classic hacking.
Newer systems, such as Myki in Melbourne, use the Mifare
DESfire platform, which is a lot "smarter" than Classic in that its
a full microprocessor and memory system.
First steps: install libnfc
The following sections assume you are familiar with installing
Unix programs from source code. If not, read the INSTALL file in
the source code distribution first.
You can download libnfc from its Google code site. At the time of writing the current version is
1.3.2.
You will need libusb. The best version to use is libusb-1.0
(new API) combined with the compatibility module for older
applications.
If you are using a PN53x USB key, add --disable-pcsc-lite to
get out of the libpcsclite requirement, needed for another type of
hardware.
I used OS X 10.6.2 as my development platform, and ran into
some compile troubles. To fix this, just add:
"#define _DARWIN_C_SOURCE 1" to config.h. By the time you read
this article, the issue might be fixed.
After you have installed libnfc in the usual fashion, plug in
the reader and run the command "nfc-list". You should get something
like this:
$ nfc-list
nfc-list use libnfc 1.3.2 (r294)
INFO: Sorry, serial auto-probing have been disabled at compile time.
Connected to NFC reader: Philips / USB TAMA - PN531 v3.4
The following (NFC) ISO14443A tag was found:
ATQA (SENS_RES): 00 04
UID (NFCID1): 5a 12 04 dd
SAK (SEL_RES): 08
Everything working? Good, lets move on.
Your first libnfc application
We all learn best by example, don't we? So lets start with the
libnfc example provided in its documentation.
Save this example to a C file and use the following command to
compile it:
gcc libnfc_example.c -o libnfc_example -l nfc
If you are using your own IDE's or Makefiles, like I was using
XCode, add -lnfc to LDFLAGS and any relevant paths to -I for
CFLAGS.
One you run your compiled example, the output should be the
same as nfc-list.
Aside: Mifare security model
Each sector on a Mifare card is secured by two 48-bit keys: A
and B. The last block in the sector (the trailer) contains these
keys, as well as a security configuration that details what each
key can do with each block, i.e block 0 could be configured so that
key A could read and write, but if a reader authenticates with key
B, the reader would only be able to read that block.
Fresh, empty Mifare cards have all their sectors configured
with a pair default keys, usually some trivial configuration, like
FFFFFFFFFFFF or 000000000000.
Here is a list from nfc-mfclassic.c in libnfc's example tools
dir:
static byte_t keys[] = {
0xff,0xff,0xff,0xff,0xff,0xff,
0xd3,0xf7,0xd3,0xf7,0xd3,0xf7,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,
0x4d,0x3a,0x99,0xc3,0x51,0xdd,
0x1a,0x98,0x2c,0x7e,0x45,0x9a,
0xaa,0xbb,0xcc,0xdd,0xee,0xff,
0x00,0x00,0x00,0x00,0x00,0x00
};
'Breaking in'
With that in mind, take the block of default keys above and
add it to your source code before the main() function. With that in
mind, we also need to move and define some structs outside the main
function.
Delete the first two lines of main() and then paste in
this:
The top of your file should now look like this:
#include <nfc/nfc.h>
#include <string.h>
static mifare_param mp;
nfc_device_t* pnd;
nfc_target_info_t nti;
static byte_t keys[] = {
0xff,0xff,0xff,0xff,0xff,0xff,
0xd3,0xf7,0xd3,0xf7,0xd3,0xf7,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,
0x4d,0x3a,0x99,0xc3,0x51,0xdd,
0x1a,0x98,0x2c,0x7e,0x45,0x9a,
0xaa,0xbb,0xcc,0xdd,0xee,0xff,
0x00,0x00,0x00,0x00,0x00,0x00
};
Now, lets make an authentication function:
bool authenticate(int block, int keynum, bool keyB) {
memcpy(mp.mpa.abtUid,nti.nai.abtUid,4);
memcpy(mp.mpa.abtKey, &keys[keynum], 6);
bool res = nfc_initiator_mifare_cmd(pnd, (keyB ? MC_AUTH_B : MC_AUTH_A), block, &mp);
if (res) {
printf("Authentication succcessful on block %d\n",block);
} else {
printf("Authentication failed on block %d\n",block);
}
return res;
}
And something to read a few blocks at once:
void readblocks(int start, int end) {
int block;
for (block=start; block<(end+1); block++) {
bool res = nfc_initiator_mifare_cmd(pnd, MC_READ, block, &mp);
if (res) {
printf("Block %d data: ",block);
print_hex(mp.mpd.abtData,16);
} else {
printf("Reading block %d failed\n",block);
}
}
}
I advise only reading one sector (4 blocks) at a time using
the above function, remember that you have to authenticate to the
sector first.
You should also add an #include <string.h> for
memcpy.
With our necessary functions in, add this after the initial
polling block:
......
print_hex(nti.nai.abtAts,nti.nai.szAtsLen);
}
} else {
goto DISCONNECT;
}
// Try to login to sector 0. Use the default key FFFF, for key A
bool authenticated = authenticate(3,0,false);
if (!authenticated) {
goto DISCONNECT;
}
readblocks(0,3);
DISCONNECT:
// Disconnect from NFC device
nfc_disconnect(pnd);
return EXIT_SUCCESS;
What we have done is authenticate to sector 0, using block 3
(the sector trailer). You can authenticate to any block in a sector
to be able to perform operations, I have authenticated to the
trailer block as a matter of convention.
Once you compile and run the code, you should get something
like this
./nfcdemo use libnfc 1.3.2 (r294)
Connected to NFC reader: Philips / USB TAMA - PN531 v3.4
The following (NFC) ISO14443A tag was found:
ATQA (SENS_RES): 00 04
UID (NFCID1): 5a 12 04 dd
SAK (SEL_RES): 08
Authentication succcessful on block 3
Block 0 data: 5a 12 04 dd 91 88 04 00 46 51 75 52 4d 10 13 08
Block 1 data: ca fe ba be de ad be ef de ad de ed fa ce fe ed
Block 2 data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3 data: 00 00 00 00 00 00 ff 07 80 69 ff ff ff ff ff ff
Congratulations! You have successfully read from a Mifare
card!
Block 0 is a read only block containing the cards UID among
other things. Block 3, as said before, is the sector
trailer.
Writing data
Lets add a function to write data:
void writeblock(int block, byte_t *data) {
memcpy(mp.mpv.abtValue,data,16);
bool res = nfc_initiator_mifare_cmd(pnd, MC_WRITE, block, &mp);
if (res) {
printf("Wrote to block %d successfully\n", block);
} else {
printf("Could not write to block %d\n",block);
}
}
This function will write an array of data to the specified
block. The byte array should be 16 bytes, the size of the
block.
And here is some code to write data using that function:
byte_t data[16] = {0xCA,0xFE,0xBA,0xBE,
0xDE,0xAD,0xBE,0xEF,
0xDE,0xAD,0xDE,0xED,
0xFA,0xCE,0xFE,0xED};
writeblock(1,&data);
Compile and run the program twice. The first time will write
the new data to block 1, and hopefully, on the second invocation,
you see something like this:
./nfcdemo use libnfc 1.3.2 (r294)
Connected to NFC reader: Philips / USB TAMA - PN531 v3.4
The following (NFC) ISO14443A tag was found:
ATQA (SENS_RES): 00 04
UID (NFCID1): 5a 12 04 dd
SAK (SEL_RES): 08
Authentication succcessful on block 3
Block 0 data: 5a 12 04 dd 91 88 04 00 46 51 75 52 4d 10 13 08
Block 1 data: ca fe ba be de ad be ef de ad de ed fa ce fe ed
Block 2 data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3 data: 00 00 00 00 00 00 ff 07 80 69 ff ff ff ff ff ff
Wrote to block 1 successfully
Cool, huh?What is on the card, anyway?
Now that we know what is on one sector of the card, it would
be nice to see what other sectors we can access and use as
well.
All we need to do is write a loop.
int sector;
for (sector=1; sector<64; sector++) {
printf("Getting sector %d\n",sector);
int authBlock = (sector*4)-1;
int startBlock = authBlock-3;
bool auth = authenticate(authBlock, 0, false);
if (auth) {
readblocks(startBlock,authBlock);
}
}
On my card, it showed that 31 of the 63 sectors were
available, meaning I could only use 384 out of the 768 bytes of
data space myself. Oh well, not bad for a card I got out a vending
machine. Guess I'll order some blanks now.
Complete source
Your finished application should resemble something
like this.
Topics not covered
- Changing the keys required to access a sector
-
-
Question for MiFare experts: Given the sector trailer above, why doesn't key 000000000000 work?
-
- Value blocks
- Playing with newer cards, i.e DESfire
The micmd project implements a
"shell" to manipulate MiFare cards and has the ability to analyse
trailer sectors and manipulate value blocks.
Applications
Now that I have a RFID reader, and the ability to read and
write a card, what could I use it for? Some sort of digital wallet,
or key store, say start an ssh session, and "tap your card" to
authenticate.
Acknowledgements
- libnfc community
- proxmark.org
- The micmd code proved very useful

Sun 03 Jul 05:59:25
Hey: Could you please tell me where MC_AUTH_A, MC_AUTH_B, MC_READ, MC_WRITE are defined. I get a compile error saying all of them are undeclared. I am definitely missing some header file here!