Playing with NFC for fun and coffeeMon 28 November 2011 by trance
RFID (Radio Frequency IDentification) and NFC (Near Field Communication) technologies are more and more widespread in our daily life. They can be found in various fields such as access control, tracking systems (objects, animal), and vending machines. Security of these technologies has been the subject of various research work presented and illustrated at conferences like HAR2009, DefCon and Hack.lu. This article is a practical introduction to NFC security by showing how one could abuse a RFID coffee machine. For evident reasons, we will disclose neither the name of the vendor, nor sensitive technical details such as authentication keys. This work has been done for research purpose only, and shall not be used for profit.
Identifying the technology
The first step of RFID security testing is to identify which technology is used by the device. The bad news about RFID is there are heaps! But the good news is that some are way more common than others. NFC can be viewed as a subset of RFID which is based on ISO/IEC 14443 Type A and communicates at 13.56 MHz. The most widespread NFC technology is probably Mifare, which is used by several transportation systems all around the world. It is itself divided into several technologies, such as Mifare Classic, Mifare DESFire, Mifare Ultralight, etc.
# nfc-list -v nfc-list uses libnfc 1.5.1 (rexported) Connected to NFC device: SCM Micro / SCL3711-NFC&RW - PN533 v2.7 (0x07) 1 ISO14443A passive target(s) found: ATQA (SENS_RES): 00 04 * UID size: single * bit frame anticollision supported UID (NFCID1): aa bb cc dd SAK (SEL_RES): 88 * Not compliant with ISO/IEC 14443-4 * Not compliant with ISO/IEC 18092 Fingerprinting based on ATQA & SAK values: * Mifare Classic 1K Infineon * SmartMX with Mifare 1K emulation [...]
As we can see, the coffee tag is a Mifare Classic 1k. It means it has a 1 kb embedded storage. Each card of this type is identified by a 4-byte UID (we replaced it to "aa bb cc dd" for this article) that can be read easily with no authentication.
Dumping the tag content
Mifare Classic tags are more or less like wireless memory cards. Their memory structure is pretty simple: the card is divided into N sectors which are composed of 4 blocks. A block is a 16-byte sequence. The last block of each sector is called the sector trailer and contains control data: - key A - access bytes - key B
The access bytes of each sector controls how the reader can read or write to the sector, and especially which key has to be used for reading and writing. These keys can be different for every sector. The following document (source) shows the data layout on a Mifare Classic 4k; the 1k layout is pretty similar.
Mifare Classic tags use the the Crypto-1 proprietary stream cipher in order to enforce the mutual authentication between the tag and reader. It requires 6-byte (48-bit) keys and generates 4-byte challenges. This algorithm has been reversed and multiple vulnerabilities have been found, among which one called Mifare offline nested attack. This attack requires one known key (such as a default key) in at least one sector of a card, and is able to retrieve all the other keys of all sectors. This attack is implemented into the mfoc tool, which relies on libnfc. Let's try it on our coffee card:
# mfoc -O dump.mfd ATQA (SENS_RES): 00 04 * UID size: single * bit frame anticollision supported UID (NFCID1): aa bb cc dd SAK (SEL_RES): 08 * Not compliant with ISO/IEC 14443-4 * Not compliant with ISO/IEC 18092 Fingerprinting based on ATQA & SAK values: * Mifare Classic 1K * Mifare Plus (4-byte UID) 2K SL1 * SmartMX with Mifare 1K emulation [Key: ffffffffffff] -> [xxx..xxxxxxxxxxx] [Key: a0a1a2a3a4a5] -> [xxx..xxxxxxxxxxx] [Key: d3f7d3f7d3f7] -> [xxx..xxxxxxxxxxx] [Key: 000000000000] -> [xxx..xxxxxxxxxxx] [Key: b0b1b2b3b4b5] -> [xxx..xxxxxxxxxxx] [Key: 4d3a99c351dd] -> [xxx..xxxxxxxxxxx] [Key: 1a982c7e459a] -> [xxx..xxxxxxxxxxx] [Key: aabbccddeeff] -> [xxx..xxxxxxxxxxx] [Key: 714c5c886e97] -> [xxx..xxxxxxxxxxx] [Key: 587ee5f9350f] -> [xxx..xxxxxxxxxxx] [Key: a0478cc39091] -> [xxx..xxxxxxxxxxx] [Key: 533cb6c723f6] -> [xxx..xxxxxxxxxxx] [Key: 8fd0a4f256e9] -> [xxx..xxxxxxxxxxx] Sector 00 - FOUND_KEY [A] Sector 00 - FOUND_KEY [B] Sector 01 - FOUND_KEY [A] Sector 01 - FOUND_KEY [B] Sector 02 - FOUND_KEY [A] Sector 02 - FOUND_KEY [B] Sector 03 - UNKNOWN_KEY [A] Sector 03 - UNKNOWN_KEY [B] Sector 04 - UNKNOWN_KEY [A] Sector 04 - UNKNOWN_KEY [B] Sector 05 - FOUND_KEY [A] Sector 05 - FOUND_KEY [B] Sector 06 - FOUND_KEY [A] Sector 06 - FOUND_KEY [B] Sector 07 - FOUND_KEY [A] Sector 07 - FOUND_KEY [B] Sector 08 - FOUND_KEY [A] Sector 08 - FOUND_KEY [B] Sector 09 - FOUND_KEY [A] Sector 09 - FOUND_KEY [B] Sector 10 - FOUND_KEY [A] Sector 10 - FOUND_KEY [B] Sector 11 - FOUND_KEY [A] Sector 11 - FOUND_KEY [B] Sector 12 - FOUND_KEY [A] Sector 12 - FOUND_KEY [B] Sector 13 - FOUND_KEY [A] Sector 13 - FOUND_KEY [B] Sector 14 - FOUND_KEY [A] Sector 14 - FOUND_KEY [B] Sector 15 - FOUND_KEY [A] Sector 15 - FOUND_KEY [B] [...]
Mfoc first tries to read each sector with default keys, such as 0xffffffffffff or 0xa1a2a3a4a5 and prints the results. Here we can see that the 0xffffffffffff works for most of sectors, except 2 of them: sectors 3 and 4. If none of the default keys worked, we could have used the mfcuk implementing the dark_side attack.
Based on the known key, mfoc then performs the offline nested attack in order to find the read key of sectors 3 and 4, as well as write keys of all sectors:
[...] Using sector 00 as an exploit sector Sector: 3, type A, probe 0, distance 15400 ..... Sector: 3, type A, probe 1, distance 14618 ..... Sector: 3, type A, probe 2, distance 19642 ..... Found Key: A [c0cac0ffee42] Sector: 4, type A Found Key: A [c0cac0ffee42] Sector: 3, type B Found Key: B [c0cac0ffee42] Sector: 4, type B Found Key: B [c0cac0ffee42] Auth with all sectors succeeded, dumping keys to a file! Block 63, type A, key ffffffffffff :00 00 00 00 00 00 ff 07 80 0f ff ff ff ff ff ff Block 62, type A, key ffffffffffff :ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [...]
In less than a minute, mfoc finds all the remaining keys. Sectors 3 and 4 are protected with the key 0xc0cac0ffee42 (it has been replaced for reasons we already mentioned). With all these keys, mfoc is enable to dump the whole content of the card into dump.mfd. MFD stands for MiFare Dump; it is a binary file format representing the previously described data layout of a Mifare tag. Let's have a look at the raw data:
# xxd demo.mfd 0000000: aabb ccdd 00de adbe efde adbe efde adbe ................ # sector 0 0000010: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000020: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000030: ffff ffff ffff ff07 80f0 ffff ffff ffff ................ # sector 0 keys 0000040: ffff ffff ffff ffff ffff ffff ffff ffff ................ # sector 1 0000050: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000060: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000070: ffff ffff ffff ff07 80e1 ffff ffff ffff ................ # sector 1 keys 0000080: ffff ffff ffff ffff ffff ffff ffff ffff ................ # sector 2 0000090: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000a0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000b0: ffff ffff ffff ff07 80d2 ffff ffff ffff ................ # sector 2 keys 00000c0: 0026 002d 51be 03f6 0001 0539 0000 0276 ................ # sector 3 00000d0: b421 0000 0000 0000 0000 0000 0000 01ff .!.............. 00000e0: b421 0000 0000 0000 40be 0000 0000 02ff .!......@....... 00000f0: c0ca c0ff ee42 0f00 ff00 c0ca c0ff ee42 .....B.........B # sector 3 keys 0000100: be01 0000 0000 0000 0000 0000 0000 00ff ................ # sector 4 0000110: 0000 0000 ffe8 0000 0000 0000 0000 00ff ................ 0000120: 0034 002d 92be 03f6 0001 0539 0000 0276 .4.-.......9...v 0000130: c0ca c0ff ee42 0f00 ff00 c0ca c0ff ee42 .....B.........B # sector 4 keys [...]
Block 0 contains the tag UID as well as manufacturer data (pretty useless for us), and blocks 0x1 to 0xb are empty. The most interesting blocks are 0xc (sector 3) and 0x12 (sector 4) because they seem to hold some data.
Differential analysis of sectors 3 and 4
Since the data format of our coffee card is undocumented and we didn't want to reverse the coffee machine hardware, we had to perform some black-box analysis in order to identify which data was stored on the card, and where. The easiest way to do was to analyze differences between those cards based on two factors: the card owner and the amount of money on the card. Thus we made a series of dumps of several cards, before and after performing several transactions such as buying a coffee, or crediting the card by inserting a coin into the machine. We then highlighted differences between those dumps. Here is an example using meld:
Both dumps were made with the same card, before and after buying a coffee. The only differences are on blocks 0xC and 0x12. More, the first dump was made when the card balance was 0.38#, and the second when it had 0.31# (one coffee costs 7 cts). Let's have a look at the first words (1 word = 2 bytes) of these blocks:
>>> 0x26 # 1st word of block 0xC in dump 1 38 >>> 0x2d # 2nd word of block 0xC in dump 1 45 >>> 0x34 # 1st word of block 0x12 in dump 1 52
We can see that 52 - 45 = 45 - 38 = 7 = price for one coffee, in cents. Thus, those words correspond to the balance of the card in cents. More precisely, three balances are stored:
- the current balance (0.38 #)
- the "n-1" balance (0.45 #)
- the "n-2" balance (0.52 #)
The current and n-1 balances are stored on block 0xC, while block 0x12 holds the n-2 and n-1 again. When looking at the second dump we can see that the position of those balances are switched: block 0xC holds the n-1 balance (0x26 = 38 cts) then current balance (0x1f = 31 cts), and block 0x12 holds n-1 then n-2 balances.
We can then infer the following:
- Block 0xC holds both current and n-1 balances;
- Block 0x12 holds the previous state of block 0xC, i.e. as it was before the last transaction;
- After each transaction, block 0xC is saved into block 0x12. Within block 0xC, the current balance is written into the old n-1 balance.
The previous screenshot also shows that the 5th byte of block 0xC is updated at each transaction. Probably some kind of checksum in order to find which word correspond to the actual current balance. And last, but not least, we noticed that the 6th word of block 0xC stores the card number, which is here 0x539 = 1337. This number is written at the back of the card.
Attack #1: Double spending
Now that we know where the current balance is located on the card, we can perform a very simple attack: the double spending. This attack is based on the fact that the machine won't store the card balance on its side to prevent fraud. The scenario is pretty simple:
- Dump the content of the card
- Buy a coffee
- Restore the dump
n order to try this attack, we can use the previous dump with 0.38â¬. After buying a coffee, the dump can be restored with nfc-mfclassic fom libnfc. This tool should be used as follow:
# nfc-mfclassic w a dump_to_be_written.mfd previous_dump.mfd
Two dumps are necessary: the one that will be written on the card, and another one that contain the current keys used for writing to the card. Of couse, the same dump can be used twice if the keys are unchanged. The 2nd parameter "a" indicates which key should be used for writing to the card; it can be "a" or "b" depending on the access conditions of each sectors. In our case, we had to use this tool twice: first with "a" and a 2nd time with "b".
After writing the previous dump back to the card, we tested it on the coffee machine. It worked. So our hypothesis was correct: the machine doesn't hold any balance, but relies on the fact that each card stores its own balance. This security issue could be compared to a Web application where authentication would be done on the client side...
Attack #2: Card generation
We are now able to spend a given amount of money - at least - twice by saving the card content and restoring it after spending. But it would be even better if we could craft a specific dump, in order to credit the card balance with an arbitrary (big) amount of money.
Previously, we found that the current balance is stored on block 0xC, either on the first or second word. More, a 1-byte field is located after those two words and seems to be re-written after each transaction. A few tests show that tempering with this field can make the card unusable, i.e. themachine is unable to read it anymore. It looked pretty much like a checksum, so we performed a series of tests in order to understand how this checksum was computed. This tested consisted in tempering with a dump by modifying either the checksum, or the balances stored on the card, and checking if the machine could read the card. Here are our conclusions: - The current and n-1 balances can be swapped. Bits 7 and 6 of the checksum (its two most significant bits) indicate the position of the current balance. They are 01 when the current balance comes first (bytes 0-1 of block 0xC) and 10 when the n-1 balance comes first; - The n-1 and n-2 balances do not have any influence on the checksum. Only the current balance matters; - Bit 6 of the checksum corresponds to the parity of the current checksum, i.e. 0 if the number of 1 in the binary representation of the balance is even, 1 if it is odd.
Although we did not find the exact algorithm implemented to generate the first 6 bits of the checksum, we found out that it is a bit fault-tolerant. Indeed, we can switch the most significant byte of the balance from 0x00 to 0x10 without changing the checksum and the card is still accepted by the machine. One could use this technique to multiply the balance of a card by 16 (approximately) without changing anything else on the card.
More, the card number (6th word of block 0xC), as well as the card UID, do not matter. Thus we can spoof them in order to prevent a possible fraud detection technique implemented in the machine based on the identity of the card owner.
Using all our previous deductions, we generated a card with a balance equal to 0x10FF (43,51#) using a previous dump with 2,55# (0x00FF).
We picked one espresso, and it worked :). The machine got fooled by altering only one byte of an original dump. And of course, it also works with chocolate bars, soda and snacks.
This article was a short demonstration of what can be accomplished with a few NFC hardware. We showed two simple attacks and their methodology, but other types of attacks could be possible. For instance, we did not try altering the access rights of the sector holding the current balance so the machine cannot write the new balance back after delivering the item. Once again, we did not earn any profit; this study was conducted for the sake of research only. What else?