Okiok

NorthSec 2025 Challenge Writeup – Vending Machine NFC

My colleague François Caron and I, Étienne Ducharme, took on an interesting challenge at the NorthSec 2025 cybersecurity competition (CTF).

On the CTF floor, there was a vending machine, and it was there for a good reason: it was its own challenge. The challenge was designed by the creative Maxime Nadeau, a well-known challenge designer at NorthSec.

When we spotted the vending machine, we knew we had to give it a go. Unusual challenges like this are exactly why we love NorthSec!

We found the challenge description on the CTF portal. It was called “Gift Shop”:

There were several elements on the front of the vending machine:

  1. The selection panel, with a screen to display messages
  2. The NFC card reader used to pay for an item
  3. Chocolate bars, enough for every team to have one
  4. A bag of chips with a visual challenge
  5. The vending machine’s info panel, including a support number

There were six flags to find around the machine, but in this article, we focus on how we solved the main challenge: buying a chocolate bar.

The Card

The organizers captains (let’s not forget the cruise theme!) gave every team a key card, as mentioned in the challenge description. It said the card contained a $1.24 credit, but the item cost $1.25. We clearly needed to figure out how to get more credit.

Armed with our Flipper Zeros (one of them won by our president at NorthSec 2023!), we scanned the key card.

A “MIFARE Classic” card was detected. Reading it took several minutes, and in the end, it was only partially successful. We could only read 2 out of 32 keys and 1 out of 16 sectors. The card was encrypted, and the Flipper couldn’t decrypt it.

As expected, we could not read the interesting data. Only the card’s header information was readable, and the card balance was definitely not in there.

The Attack

Knowing the card type, we learned about the MFKey32 attack.

This attack takes advantage of a well-known weakness in MIFARE Classic cards. The Crypto-1 encryption protocol has vulnerabilities in the way it handles random numbers (“nonces”) during authentication. By capturing just a few exchanges between the card and a reader (in our case, the vending machine), you can analyze these nonces to recover the card’s encryption key. In short, this attack lets you uncover the key just by listening to a few reads between card and reader.

Equipped with our Flipper Zero, we launched the attack against the vending machine’s card reader. In just a few minutes, we collected the ten required nonces. With this information, we could calculate the missing keys and unlock our card.

We then ran a fresh card scan with the Flipper. This time, in just a few seconds, all the keys and sectors were recovered and accessible. At last, the full inner structure of the key card was at our fingertips.

The Dump

We transferred the Flipper’s dump to our computer and opened it in a text editor. Here’s how it started:

Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB
Device type: Mifare Classic
# UID is common for all formats
UID: 20 47 B0 20
# ISO14443-3A specific data
ATQA: 00 04
SAK: 08
# Mifare Classic specific data
Mifare Classic type: 1K
Data format version: 2
# Mifare Classic blocks, '??' means unknown data
Block 0: 20 47 B0 20 F7 88 04 00 C8 06 00 20 00 00 00 24
Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
Block 4: 08 E6 83 02 12 16 43 56 53 53 42 6F 6E 73 65 63
Block 5: 6F 75 72 73 47 69 66 74 53 68 6F 70 18 6D 25 52
Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00
Block 7: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4
Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4
Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[...]

The dump continued until block 63.

Analysis

This is a MIFARE Classic 1K card with 16 sectors, each sector containing 4 blocks.

Sector #1

Block 0: 20 47 B0 20 F7 88 04 00 C8 06 00 20 00 00 00 24
Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF

The first sector starts with block 0, the manufacturer block. Then two blocks of zeros, and finally the sector trailer at block 3. The structure of the sector trailer is:

[KeyA (6)] [Access Bits (3)] [User Byte (1)] [KeyB (6)]

From the dump, both keys are “FF FF FF FF FF FF”, which is the default key for MIFARE Classic cards. When we read the encrypted card, these were the two keys we found. It’s a bit like leaving your application authentication to “admin:admin”, which would be something that we’ve never seen…

Sectors #3 to #16

Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4

The following sectors were all the same. The data area was empty, and the sector trailer had the key found through MFKey32: “03 90 F4 24 21 22”.

Sector #2

Block 4: 08 E6 83 02 12 16 43 56 53 53 42 6F 6E 73 65 63
Block 5: 6F 75 72 73 47 69 66 74 53 68 6F 70 18 6D 25 52
Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00
Block 7: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4

With no other sectors looking promising, the key to the challenge had to be here.

Block 7 is the sector trailer, just like in other sectors. So we focused on blocks 4, 5, and 6. When analyzing the values, we spotted a range of hex values (41 to 7A) that match ASCII characters. In CTFs, you always watch out for long runs of ASCII.

From the seventh byte of block 4 through the twelfth byte of block 5, we found the ASCII string “CVSSBonsecoursGiftShop”. That fits—the name of the cruise ship for the CTF and the challenge name.

So our dump looked like this:

Block 4: 08 E6 83 02 12 16 C V S S B o n s e c
Block 5: o u r s G i f t S h o p 18 6D 25 52
Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00
Block 7: [Sector trailer]

The CTF Method – Bruteforcing

At this point, we searched through the unknown bytes for the value “1.24” but didn’t find it. Our first hypothesis was that the value would after the ASCII header, and not before. Our second hypothesis was that it could be stored as a 32-bit float (which would be “3F9E147B” in big endian). That value did not appear anywhere in the dump.

Running out of ideas, we moved from refined and polite methods to more desperate ones, such as bruteforcing. Isn’t funny how CTFs make us behave?

We figured there were 15 unknown bytes, and maybe changing just one would do the trick. We tried incrementing each hex value by 1, one byte by one byte.

So we made 15 data dumps, each with one incremented byte. We loaded these onto the Flipper so they could be emulated as virtual NFC cards.

Chocolated Glory

In front of the nefarious vending machine, we emulated our 15 cards, one by one.

Each image was named based on the block and byte changed.

The vending machine’s screen responded with various messages like “Insufficient funds”, “Auth failed”, and “Invalid card”. In a way, these were all good signs!

On the seventh card, we finally saw the message we wanted: “$4.96”. We now had more than enough credit to buy the chocolate bar!

We ate the chocolate, it was delicious, and the sugar rush really helped us keep crushing challenges! THE END

On the chocolate, there was a boat decoration, and on the back was the good stuff:

The Intellectuals’ Method

Having completed the challenge, we hopped on other challenges. But, after the competition, we looked at our NFC image with a clear head.

Reminder, sector #2 looked like this:

Block 4: 08 E6 83 02 12 16 C V S S B o n s e c
Block 5: o u r s G i f t S h o p 18 6D 25 52
Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00
Block 7: [Sector trailer]

The incremented byte that pushed the balance from 1.24 to 4.96 was “3F” which we changed to “40”.

By reverse engineering the process, we realized we were not far off in our previous guesses. The balance was, in fact, stored as a 32-bit float. Starting from the location of the changed byte and reading backwards for endianness, you get “3F9EB852”.

The calculations work out, and incrementing “3F” to “40” gave us $4.96.

And by reading up on floating-point structure, we learned that changing the exponent byte explained the big jump in value when we only incremented by one.

Exit mobile version