In June 2021, Andy Greenberg of Wired reported about the findings of security researcher Josep Rodriguez. Rodriguez developed an Android app that allowed him to exploit vulnerabilities within the firmware of near-field communication (NFC) reader chips used by millions of ATMs and point-of-sale devices. NFC is a set of communication protocols for wirelessly transmitting data between devices that are at most 4 centimeters apart. It is used for credit card transactions and authorization for smart ID cards. By waving his phone around the NFC readers of ATMs and point-of-sale devices, Rodriguez could crash devices, collect credit card data, secretly change the value of transactions, lock devices, and even cause one ATM brand to dispense cash (known as “jackpotting”). The Android app on his NFC-enabled phone worked by sending application protocol data units (APDUs) large enough to exploit buffer overflow vulnerabilities within the firmware of the NFC readers.
Although buffer overflows are not a new type of vulnerability, their presence in the firmware of NFC readers is very alarming. Buffer overflows (also called buffer overruns) have been a common software problem for decades. They arise from the oversight and carelessness of programmers. When used to discuss buffer overflows, the term buffer typically refers to a region of computer memory that has a fixed size. It might also be a buffer in the traditional sense: a region of computer memory that temporarily stores data while it is being transferred from one place to another. Data provided by the user of a program—such as names, passwords, blocks of text, documents, etc.—are often stored in memory regions with fixed lengths. If the program does not check to ensure that the size of the user-provided data does not exceed the size of the buffer before writing it to memory, the excess data will overflow the buffer and be written into adjacent sections of memory. If those adjacent sections of memory contained important data that was needed for the program to function, and those sections are overwritten by overflowing data, the program would act in unexpected ways. Typically, the program might crash, but if an attacker wanted to do more harm, then they would insert their own malicious code into the data they feed into the program. This data would be structured so that the return address on the stack is overwritten to point to the start of the malicious code, which the program would proceed to execute.
The Morris worm is a famous example of how buffer overflow vulnerabilities could be exploited. Launched on July 2, 1988 by Robert Tappan Morris as an experiment, the worm infected 6,000 UNIX machines. At the time, this was equivalent to infecting roughly 10% of the internet. One of the vulnerabilities exploited by the worm was a buffer overflow vulnerability within fingerd, a program that could be used to display information about all the users of a system, including who is logged on, their name, and their email address.
Buffer overflow vulnerabilities can be prevented by good programming practices. Buffer overflows are common in programs written in C and C++. If either language is used to write software, then programmers must write proper bounds checking to ensure that data written to a buffer does not exceed the boundaries. Some programming languages perform bounds checking automatically. These languages include Ada, C#, Haskell, Java, JavaScript, Lisp, PHP, Python, Ruby, and Visual Basic. Although the tools chosen to write software can help with security, it is ultimately the responsibility of programmers to ensure that the final product does not contain serious vulnerabilities.