Disclaimer: This article is published solely for information purposes and is in no way a guide to action. The vulnerabilities described in the article were discovered by the author as part of the project approved by the ATM manufacturer. At this point, they have been fixed by Diebold Nixdorf, which was notified by Positive Technologies in accordance with the principles of responsible disclosure. As an additional element of protection, the vendor was recommended to enable physical authentication for the operator during firmware installation in order to make sure that changes to the ATM are made by an employee and not by an attacker.
Hi there! A while ago, Positive Technologies published the news that ATMs manufactured by Diebold Nixdorf (previously known as Wincor), or more specifically, the RM3 and CMDv5 cash dispensers, contained a vulnerability which allowed attackers to withdraw cash and upload modified (vulnerable) firmware. And since my former colleague Alexei Stennikov and I were directly involved in finding this vulnerability, I would like to share some details.
First, I think I should briefly tell you what an ATM is, how it works, and what exactly dispensers are. In general, an ATM is a rather complex system, which consists of two major parts: upper and lower.
The upper part is a usual PC, which most often lies flat in this part. There are plenty of USB devices plugged into it: cameras, printers, card readers, and, finally, the dispenser, which is located in the lower part. The PC in the upper part (which, by the way, is considered insecure) is usually the main target for malware attacks. But it’s not about us — malware is bad! So, let’s go down the USB cable to the lower part of the ATM, where we can see…
The safe zone (a.k.a. safe or dispenser) — a securely protected part of the ATM. It is locked with a key, which only cash couriers have; it is not made of plastic (unlike the upper part) and is often reinforced with concrete or mounted into a wall. And there is something in the safe which is the reason why ATMs are hacked — banknotes. They are arranged by denomination in cassettes, each of which is also locked with a key. The money is taken out of the cassettes using a belt system (and NCRs use suction cups), goes along the conveyor to the shutter, and then is extracted by the user.
So, this whole complex system of sensors (used to evaluate the thickness, number, and crumpling of bills), conveyors, shutters, and cassettes is controlled by the dispenser control board (or more simply, the dispenser). The board has a processor, memory, and other elements of an independent device; also, there’s code that controls everything. And that’s where we just came down the USB cable.
Why criminals hack the dispenser instead of the PC, and what do blackbox attacks have to do with it?
A good question, you know. If we look from an attacker’s perspective (which we’d better not), the disconnection of one USB device (the dispenser) from the ATM computer will be displayed only as a line in the computer logs, and the logs of the dispenser itself will not contain any meaningful information (if compared with the commands run from the attacker’s console).
Some time ago, dispensers were often hacked using blackbox attacks. During such as attack, the criminal, having once captured the USB/COM cash withdrawal traffic on one device, could use it on another one, hack it, and get the money. Today, no one is likely to be able to pull this off, because anti-blackbox techniques are used everywhere: for example, session rolling keys for encrypting USB traffic and the use of cryptography at almost every stage. Now only an ATM computer can work with this encryption, so most often, an attacker can no longer get into the traffic.
But then this article wouldn’t exist, and neither would the news I mentioned. There was one small vulnerability in our project (fortunately, it has already been fixed). Read about it below.
What made it vulnerable?
Before we begin, I’ll briefly explain what the vulnerability is about and then I’ll move on to the details.
First of all, it should be said that the firmware is encrypted with keys that are known only to the vendor. An attacker can exploit the vulnerabilities described by Positive Technologies (one in each device) to upload firmware to the dispenser without knowing the encryption keys (they will be mentioned below as KEY0 and KEY1). That is, having a clean code, an attacker can modify it however he likes, encrypt it again, upload it to the ATM, and then withdraw cash bypassing the existing USB traffic encryption algorithms. Well, now let’s move on to the details…
End of 2017, the project begins
As part of the project in a bank, Alexei and I got two files: one had the .BTR (bootloader) extension, and the second had the .FRM (firmware). The files had clear headers, but at the same time the content was completely incomprehensible. We tried all kinds of ways to decrypt the content (it became obvious that it was encrypted), but to no avail. The project could have ended there, and the files remained on the hard drive for several months until the wise guy Alexei decided to look for a dispenser control board on eBay. As you might have guessed, Alexei did find the board there, and after a while it was lying on his table.
To have a controller is a blessing, not to have one is a curse
After ringing up the board pins, we found the JTAG connector, which was located right next to the main CPU. In addition to the main one, there were two more processors on the board, the purpose of which was not very clear to us: CollectorBooter and DispenseBooter. Nevertheless, the memory of all the three processors was dumped for further analysis.
Well, it’s not a big deal. But what was next? We decided to try doing something about the firmware — we had its encrypted files, remember? And although we had the dumps, it was a lot more pleasant to work with the firmware in pure form.
Quite often, either for diagnostic purposes or for work with an ATM, manufacturers write specialized programs that can be used to perform both standard tasks and some
undocumented ones that are rarely used. For example, updating the dispenser firmware. Such software was in our project as well. And it was not written in some kind of BASIC or C++, but in the good old (and respected by any reverse engineer) Java language. With it, you can decompile or re-assemble things if need be.
After digging into JAR files, we discovered a function that uploaded firmware files to the controller. Unfortunately, it did not decrypt the files, but gave us a good opportunity to debug this process. By connecting the debugger over the JTAG and running the firmware upload, we somehow managed to find the place responsible for decryption. Let’s talk about it.
Encryption of firmware files
The encryption algorithm was a regular XTEA, but with a delta that was rather unusual for it: 0xF27716BA. We don’t know how it was generated or whether it was safe or not. But one thing was for sure: it can’t be googled.
The following input data was used as the XTEA encryption key:
● First five dwords coming immediately after the firmware name specified in the header (we called them Header-Dwords)
● External key KEY0, 8 bytes long (it’s still unclear where it came from)
● External key KEY1, 16 bytes long (we didn’t know where to find it either)
After a while, we did find a piece of code that extracted both KEY0 and KEY1. It turned out that they were located in a special area at offset 0x64000000, the offset in which was hard-coded by default. Next, we made a dump of this area and extracted both foreign keys from there. By using these keys in a slop-built decryptor, we got… No, not the firmware.
It was aPLib. Those whose everyday job is to reverse malware will surely recognize this name, since this compression algorithm is (was) very often used by malware developers.
So, we obtained a lot of archives, and they all went one after another. If you join the unpacked data blocks together, you will get the complete firmware. Seems like there’s reason to celebrate? Not so fast. When trying to change something in the firmware (for example, a string), after packing and encrypting it, the device refused to accept our firmware. So, there was an integrity check somewhere.
The name of this section speaks for itself. Yes, we found the code that verified the firmware signature, and, funnily enough, the firmware itself had everything that was needed to verify its integrity, namely the public key and the signed data itself.
As a signature verification algorithm, RSA was used with an exponent equal to 7, and the bit count of the key was determined by the size of the public part N. It turned out that if you fitted into the offsets at which the signature and public key were written, you could set almost any length.
That’s what we did. Instead of the original 2048 bits (I believe so), we successfully inserted a 512-bit key.
Uploading the modified firmware
Yes, we could do that now. Here I think I should tell in more detail what the firmware uploading is. To begin with, let’s open the device manager and see what is displayed there during normal operation of the controller:
And this is how the list looks when the device is being updated:
This last device accepts the files. And it uses the Device Firmware Update (DFU) protocol, which we could have understood even without having the control board. The thing is that at the end of each firmware file there is a certain structure that contains the UFD tag.
First, the protocol command DFU_DETACH is sent to the device, after that the USB command UsbCyclePort is executed, which enables the interface that accepts the firmware. Anyone who has worked with this protocol will say that it also has the DFU_UPLOAD command (in this case, «download»), but it was not implemented in our project.
Running the firmware
After uploading the firmware to the dispenser, it is decrypted, unpacked, its signature is verified, and then the firmware is written to flash memory.
No, not yet. Before getting the money, we had to figure out how to send commands to the dispenser. After digging into the Java code, we managed to find special JAR files responsible for self-test, that is, for testing the dispenser components. From this code, we managed to find out the following:
- A smartcard was involved in the interaction of the ATM computer and the dispenser (it had long been visible on the board).
- A trusted platform module (TPM) was used.
- The keystorage was used to store session counters (read more below), certificates, and the basekey.
- There were four directions, each of which used its own session key and counter: PC → Firmware IN/OUT, Firmware → PC IN/OUT
The smartcard turned out to be an interesting object for research. I had never dealt with the APDU(the protocol used when communicating with smartcards), so I had to investigate. After that, I found a class responsible for working with a smartcard in Java software, from which I was able to extract most of the APDU commands. They could also be found in the firmware code, and then some other commands could be found by reference.
Armed with a smartcard reader, I bruteforced various commands and their parameters. Unfortunately, I discovered no big secrets. However, I did find commands that allowed setting our own session counter values (except for the values that had been already used before).
Session keys (no thanks)
In fact, this whole scheme with session keys, four directions of communication, the TPM, and keystorage did not cause anything but respect. The scheme was designed very skillfully. An attacker couldn’t just get into the traffic, repeat a captured session, or do something like that. Therefore, as a reverse engineer, I didn’t really want to get into the weeds.
As part of the project, we changed the firmware so that it stopped accessing the smartcard to get the session keys, using a dummy array of zeros instead.
Now we had to do the same in Java software, so as not to bother with writing code from scratch. By the way, it became clear for us from the code that the Aes-EAX encryption algorithm was used.
Here I would like to make an important remark: all the tests were still carried out on the dispenser controller board that was lying on my desk. And this meant that there were no peripheral devices and, accordingly, there was nowhere to get banknotes from. Therefore, when Alexei and I had full confidence that we would manage to withdraw cash, we went to the vendor’s laboratory to conduct the final tests.
During these tests, it turned out that in order to withdraw the money, we needed to specify how much cash we had and in which cassettes and skip the configuration of devices that were not connected to the laptop (well, there were a lot of USB ports in the computer, and only two in the laptop).
Only after that, a command with the numbers of the cassettes and the number of banknotes we wished to withdraw could be sent to the dispenser. The ATM began to buzz merrily, preparing to share cash with fellow researchers, but… we never saw the money. We just
didn’t know forgot that we needed to open the shutter. A second try — and we were holding a bunch of crisp banknotes (not real ones, of course, but special vendor bills) in our hands. Hurray!
The project was damn interesting! While doing it, we managed to work with new technologies, beep the speaker installed on the board, and, of course, withdraw cash. At the time of writing, the vulnerabilities (one in each device) have already been fixed by ATM manufacturer Diebold Nixdorf. They have the following identifiers (the identifiers in Mitre are also on the way):
That’s all, I guess. I would like to make special mention of Alexei’s work on the hardware part of the project as well as the dispenser board he purchased, which is still lying on my desk.🥳Playing the Astronomia song on a CMDv5 Wincor dispenser controller: