Many of the systems that power the modern world are supposed to be beyond the reach of mere mortals. Developers naively assume that these systems will never give up their secrets to attackers and eagle-eyed researchers.
ATMs are a perfect case in point. Thefts with malware of the likes of Cutlet Maker, as well as unpublicized incidents when unknown attackers plugged in their laptop to an ATM and stole cash without leaving any system logs behind, confirm what the security community has long known. There is no such thing as a hack-proof system, merely one that has not been sufficiently tested.
Even now, many people think that the only way to rob an ATM involves the brutest of brute force: pulling up in a pickup, attaching a hook, and pushing hard on the gas pedal, before savaging the ATM with a circular saw, crowbar, and welding kit.
But there is another way.
After a brief search on eBay, I obtained the board for a NCR USB S1 Dispenser with firmware. I had two objectives:
- Bypass the encryption used for commands (such as \”dispense banknotes\”) that are sent by the ATM computer via USB to the dispenser.
- Bypass the requirement for physical access to the safe in order to complete authentication (which must be performed by toggling the bottom cassette in the safe), which is needed for generating the encryption keys for the commands mentioned above.
The firmware is an ELF file for the NXP ColdFire processor (the Motorola 68040, my favorite CPU!) running on VxWorks v5.5.1.
There are two main sections of interest in the ELF file, .text and .data:
- The first contains code that loops continuously most of the time (we\’ll call it the \”main firmware\”) when the dispenser is connected to the system in the upper part of the ATM.
- The second contains a zlib-compressed bootloader (locally named \”USB Secure Bootloader\”), which is responsible for uploading firmware and running the main code.
And best of all (for researchers, anyway), is that the debug symbols in the ELF file were all there and easily searchable.
Inner workings of the main firmware
We can divide the code into four main levels, from top to bottom in the hierarchy:
- USB Receive Thread, which accepts USB packets and distributes them to the different services.
- Services are the main units of execution. Each service has a particular role and corresponding tasks (classes).
- Classes, here, are tasks that can be performed by a particular service using controllers.
- Controllers are the workers that validate tasks, perform tasks, and generate result packets.
There was a lot of firmware code, so I decided to start by finding all possible services and only then trying to figure out where tasks are transferred.
Here are the services I found that were responsible for the actions of interest:
1) DispTranService (Dispenser Transaction Service): Handles encrypted commands, generates bundle of banknotes, authenticates, and much more. Sure, the interesting stuff.
2) securityService: After authentication, a session key is generated on the dispenser. When requested by the ATM computer, the session key is sent to it in encrypted form. This key is then used to encrypt all commands designated important by the vendor, such as dispensing cash and banknotes bundle forming.
But then another service, UsbDownloadService, caught my eye. The job of this service is, when the dispenser is connected to the computer and the firmware version on the dispenser doesn\’t match the version on the computer, switch to the bootloader in order to upload the firmware needed to work (which is stored in the folder with the vendor\’s software on the computer) with the OS. This service can also give us information about the current firmware version.
Physical authentication is in fact implemented extremely well, with the mission of protecting the ATM from unauthorized USB commands. The ATM safe with cash must be open in order to perform either of the following actions:
- Remove and insert the lower cassette.
- Toggle the switch on the dispenser main board.
But this all is required only if the access level is set to the maximum. There are a total of three access levels: USB (0), logical (1), and physical (2). The first two are used by firmware developers for debugging and testing. The vendor, of course, strongly urges selecting the third one by default.
Here I will describe a critical vulnerability (now fixed by the vendor) that with physical access to the service zone of the ATM but not to the safe zone (such as through a hole drilled in the ATM front panel), allowed the dispenser execute any command – even if the command is \”give me cash now!\”
I found that UsbDownloadService accepts commands that don\’t require encryption. That sounds tempting, but shouldn\’t Secure Bootloader prevent any further mischief, as its name implies?
Spoiler: …it doesn\’t!
We need to go deeper
As mentioned already, the .data section contains compressed bootloader code that didn\’t initially catch my attention or that of my colleagues.
As long as the bootloader remained a secret, there was no way to answer the question: \”How does the software on the computer upload the dispenser’s firmware?\” The main firmware did not reveal any clues.
So the bootloader is unpacked and loaded into the IDA at offset 0x100000, from where investigation can start… except there are no debug symbols there!
But after comparing the main firmware with the bootloader code and reading the controller datasheet, I started to get a better idea of what was happening.
Although the process of firmware uploading seemed to be secure, in reality it was not. The trick was just to upload the firmware in the right way 🙂
Fully understanding this process took a lot of time and dedication (details can be learned from \”Blackbox is dead – Long live Blackbox!\” at Black Hat USA 2018 in Las Vegas). These efforts included re-soldering NVRAM and copying the backup to it in order to unbrick the controller… and other easy-peasy stuff like that.
Thank you to my colleague Alexey for his patience!
Here is the method for uploading firmware to the dispenser:
1) Generate an RSA key pair and upload the public key to the controller.
2) Write .data and .text from the ELF in sequence to their physical addresses, taken from the section headers:
3) Calculate the SHA-1 checksum for the newly written data, encrypt that value with the private key, and send the result to the controller.
4) Calculate and send the sum of all firmware words that have been written.
At which point, if everything has been calculated and written correctly, the main firmware will boot without a hitch.
Only one restriction was found for the firmware writing process: the version of the \”new\” firmware cannot be less than the version of the current firmware. But there\’s nothing to stop you from tinkering with the firmware number in the data that you write yourself.
So my special firmware with anti-security \”secret sauce\” was uploaded and run successfully!
By now I had a good knowledge of the main firmware, commands used to dispense cash, and more. All that remained was to send (unencrypted) commands, which the dispenser would eagerly obey.
This successful result was a worthy intellectual (although not monetary) reward for all the travails of research, such as bricking a real ATM (oops!). My curiosity almost inspired me to try repeating this trick with another major ATM vendor.
Ultimately, a very real ATM began to whirr and spit out very not-real dummy bills (vendors\’ shiny equivalent of Hollywood prop money). No magic was necessary: just a laptop, brainpower, and a USB cord.
\”Security through obscurity\” is no security at all. Merely keeping code or firmware proprietary will not stop an attacker from finding a way in and taking advantage of vulnerabilities. Curiosity and an initial financial outlay are all that is required.
Just as development is best handled by developers, security should be the job of security professionals. The most productive approach for vendors is to work closely with dedicated security companies, which have teams possessing the necessary experience and qualifications to assess flaws and ensure a proper level of protection on a case-by-case basis.
The vendor has confirmed the vulnerability (which was also found in the S2 model) and declared it fixed as of the February 2018 patch.
- CVE-2017-17668 (NCR S1 Dispenser)
- CVE-2018-5717 (NCR S2 Dispenser)
Before I had even set to work on the firmware, Dmitry Sklyarov and Mikhail Tsvetkov had already discovered a lot about it (even without having a dispenser board). Their findings were of enormous assistance! And as concerns everything hardware-related, Alexey Stennikov\’s help was absolutely invaluable.
Author: Vladimir Kononovich, Positive Technologies