Breaking NFC Based Travel Cards (Updated)

NFC is always hard to work with. Imagine reverse engineering an entire travel authorization system and exploiting it.

a man standing in front of a row of parking meters
Photo by Steph Gray / Unsplash
Disclaimer: Do not attempt on any real system. This blog is intended solely for educational purposes.
Note: This post contains some ambiguity to avoid disclosing specific targets. The issue hasn’t been fixed yet, and the target hasn’t acknowledged it as a bug. However, entering a station for free likely isn’t intended. 👀

Before we start …

Hey, I’m currently job hunting. If you think I’d be a fit for your team, feel free to connect on LinkedIn or email me at nk_mason@protonmail.com.

Before we start you should know that I’m NOT an expert in NFC or RFID technology. I just know enough about the software and hardware to break it. I’ll be more than happy to answer your questions about the stack but expect some hiccups in the answer.

This is the second iteration of this blog. I wanted to write about more stuff in the previous blog but didn’t due to my lack of understanding. If you’ve heard the talk or read the previous blog, please jump to the Updates section.

I had the opportunity to present this talk across India and in Germany. A recording is available on Chaos Computer Club’s (CCC) YouTube page —

Now with all that said, Let’s start with the part you’re here for —


Reconnaissance (Non Technical)

Even though you’d think that you know everything about the train station and the authorization system, it’s always better to go through everything again and ask yourself questions starting with “Why?” and “How?”. Deepening your understanding of the target is essential. Here’s what I learned —

To start a journey, you have x number of ways to enter the station premise. Out of which only 2 are NFC based — a one way ticket or a card that you buy from them and top-up money to. There are two ways of top-up your travel card —

  1. At the counter in the station
    1. You pay the amount and give your card to them.
    2. They put your card on their NFC reader. Followed by some NFC business.
    3. You get the card and the receipt back.
  2. Online top-up (broken in 2 phases)
    1. Phase 1: You recharge your card using their website.
    2. Phase 2: You go to any station premise and use a machine (“Mogambo” from now on) to complete the recharge.

Now ask this question - Why is tapping the travel card into a reader in station premise a necessity in either way of top-up? The answer is simple, the balance is stored in the card itself.

Reconnaissance (Technical)

NFC is a peer-to-peer (P2P) communication protocol. While MiTM or proxy logging might come to mind, it's not that simple in this context. Before talking about methods of eavesdropping NFC communication, let’s recon more.

1. Recon: Travel card

I used TagInfo application to scan the travel card and found that the card is made by NXP Semiconductors (they made TagInfo app as well) and is called MiFare DESFire EV1 card. The card is full of “applications” with “files” inside of them holding on to the actual data.

Upon reading the datasheet, I figured that there’s a file called a Value file (Section 8.5, Page 8) that more or less behaves like a wallet. You can perform certain transactions on this file like Credit or Debit (Section 9.6, Page 11). This is where Mogambo is writing to while recharging the card. Everything that Mogambo does during a session is transactional (deduced using row “Commit/Abort Transaction”, Section 9.6 Page 12).

The communication between the card and the reader can be Plain, MAC’d or Encrypted (Section 8.6, Page 8). Every application is encrypted inside of the card and has different permission level (deduced by reading the properties of applications using TagInfo). This means that even if you can eavesdrop the communication, there’s a chance that everything will be encrypted in the logs.

Even the encrypted data in the files won’t be available to read for any reader. So, NO, you cannot clone this card and call it a day.

2. Recon: Mogambo

While trying different things during phase 2 of online top-up, I stumbled upon a bug. If you remove your card just before you see the top-up confirmation screen on Mogambo, there’s a chance that your top-up will be successful from your card’s POV but will be still pending from Mogambo’s POV.

Why did this happen? The assumption I made at that time was that maybe I removed the card at the time when the card was sending an acknowledgement to the commit command sent (to close the transaction) by Mogambo. This means that the communication during committing a transaction is not MAC’d, so it’s possibly encrypted or plain text. Let’s try to automate this.


Eavesdropping

Coming back to this. Why is it not so simple? The travel card is a passive NFC card. It means that it has no battery inside of it and it relies on induced power from the reader to participate in the communication. You can put a coiled oscilloscope probe (grounded to itself) and eavesdrop but it won’t give you the power to change packets in real time. Although this sounds good, how are you planning to take a DSO into a station premise which is full of station officials and law enforcement guards.

Let’s think of the situation differently. Ask yourself if at any given point during the communication does Mogambo or the travel card have any mechanism to know who they are talking to? The answer is NO. Think about it, Mogambo can ask for a special token from the card to know the authenticity but this can be faked as well. So what if we can just relay the communication? This problem is nothing new and is famously called as Chess Grandmaster Problem. The Chess Grandmaster problem (JH Conway’s On numbers and games, Page 75) is a scenario where a young girl wins a game of chess against two grandmasters by copying their moves. Here the young girl is just relaying one grandmaster’s move to the other and vice-versa. We can have 2 devices acting like Mogambo and travel card and perform something similar.

Eavesdropping: Relay attack

Fig. 1: Flowchart for NFC based relay attacks. Edit the chart here.

Fig. 1 shows how the flow should be. We are missing 3 things: 2 devices and a server. I took a cardboard box, hotglue, RPi Zero W, 2 UART-TTL and 2 PN532s to make this monstrosity —

The idea was there but it won’t work as PN532s don’t really emulate 7 byte UIDs (DESFire cards have it) that well. The half written server was based on libnfc and was similar to the example relay executable. Now you must wonder, why it took me this late to realise this small thing?

The issue is that nearly all NFC documentation is behind a paywall. To even get DESFire developer documentation, you need to buy the actual device and then visit their store and then purchase the documentation there after signing a NDA. Even the ISO standard defining guidelines for NFC communication (ISO-14443) is behind a paywall. One of the major reason why hacking around NFC is kinda hard (but very enjoyable).

I had my eyes on a project called NFCGate which promises relay attack by having your NFC enabled android phones as the actor devices and it ships with a Python based relay server. It has low chances of working on newer devices so I bought an old Nexus 6P. Now while testing it locally I figured that I probably won’t work because of FWT (Frame Wait Time).

FWT defines a set time in which if the card fails to reply to the reader, the reader will close the connection. In this case FWT was 77ms. A normal read of all applications, files and their permissions takes ~2 seconds on my phone. With this setup running locally, the same read took ~35 seconds. FWT is not enforced by default, the vendor can enable it. So there was still a chance that this slow setup will still work.

I packed my bags. My laptop running the server in my bag. Both phones in my hand and ready to run to log the communication. Aaaaand… it failed. They have enforced FWT.

Eavesdropping: Fixing the relay setup

The easiest way to do it is to rewrite the server in a faster (compiled) language. I also wanted to drop some server/app features that I won’t use. I rewrote the entire thing in Golang and out of the box the ~35 second read time was down to ~16 seconds. I packed my bags again and this time it worked. After that, I quickly started an online top-up and completed phase 2 of it while recording the communication. Finally, I had a log file that I could do something with.


Understanding MiFare DESFire

The log file has supposed encrypted stuff in it. I had to look for the transaction getting committed, but how? Everything in the log was alien to me. While researching a bit more, I stumbled upon libfreefare. It had all the commands (that the card can understand) implemented. They have implemented API around DESFire card as well.

The implementation of commit command tells us that 0xC7 is what commits the transaction and the response is going to be 1 byte long. Cross checking with the log, I was able to find it and response was 0x00 which means everything is ok and the card has succesfully ran the command. Credit command is 0x0C followed by the file number and then 4 bytes denoting the amount to credit.

The actual command bytes (0xC7, 0x0C, etc.) are always unencrypted, the following data maybe encrypted (it depends on file’s configuration).

I wrote a small script to explain the meaning of important commands. It will parse logs made by NFCGate.


Writing the exploit

If you go through my rewrite of the server, you’d see that you can write rules to change packets in real time. I wrote a rule to send 0x7E (random byte, instead of 0x00) as reply to 0xC7. This would tell the reader that something went wrong and will probably won’t mark the top-up as done. I again packed my bags and relayed the session during phase 2 of online top-up. It worked. This is how it looks like.

You can be really creative with these attacks. Like changing the debit command (0xDC, has same signature as credit command) by just replacing the first byte to 0x0C (credit command). This will mean that upon exit you’ll get the amount for which you’ve traveled. The more you travel the more balance you have in your travel card.


Updates!

While logging ~5 communication while completing phase 2 of online top-up, I recognized that even though the session key is different every-time (which means that encrypted commands will be different in every transaction). Every command related to value file remained the same. Which means that the file is mis-configured and the amount that I though is encrypted was actually plain text. The amount was just in big endian. A new exploit can be to just sniff for 0x0C during phase 2 of top-up and replace it with a bigger number (max value is 0x7FFFFFFF) and let the recharge go through.

Another thing I noticed was that in order to show the available balance you have (it’s the first thing Mogambo shows you when you tap your card), Mogambo will have to decrypt the application which contains the value file. This means that you don’t even have to initiate any online top-up. The moment it you see 0x0A from Mogambo, it means that the reader is trying to authenticate and trying to unlock an application. After this stage you can inject 0x0C (it is plaintext) and then inject 0xC7 to commit your changes.

One last thing, upon further inspection, I figured that out of 7 defined application in this travel card, only 2 were getting used. Rest of the applications were at their default settings with their keys set to 0x00. What if Mogambo still tries to authenticate with default keys if the original key fails? It’d mean that you can clone the skeleton applications and their files to a new magic card and that’d work as well. If you’ve a proxmark3 with you and a flash card, this gist contains the command to mimic a travel card.


To the reader

I hope you learned something from this blog. You can ask your doubts by dropping me a mail at nk_mason@protonmail.com.

Cya. 🫡