Chapter 27 Β· Buffer Overflows

The Overflow Demo

Yusuf, a software security instructor, shows his class exactly how writing one extra character into memory can hand an attacker complete control β€” no password required.

Scene 1: Too Much into Too Little

Yusuf wrote two words on the board: buffer and overflow. He turned to face the class of twelve students. "You already know what a buffer is," he said. "Every program that runs on a computer uses memory to store data temporarily while it is being processed. That reserved area of memory β€” the space the program sets aside to hold incoming data β€” is the buffer."

He drew a small rectangle on the board, labelled it buffer, and wrote the number 8 next to it. "This buffer holds 8 bytes. The program was designed expecting that nobody will ever send more than 8 bytes into it. The developer built the program this way."

"Now β€” what happens if an attacker sends 9 bytes?"

He paused. Several students started to answer, then hesitated. Yusuf continued. "The first 8 bytes fill the buffer exactly as designed. The 9th byte has nowhere to go in the intended space. But it does not simply disappear. It overflows. It spills into whatever memory sits directly next to the buffer β€” the adjacent memory."

"That," he said, pointing at the word he had written, "is a buffer overflow. Writing more data into a buffer than it was allocated to hold, so the excess spills into adjacent memory areas."

A buffer overflow occurs when a program writes more data into a buffer than the buffer was designed to hold. The excess data spills over into adjacent memory β€” overwriting whatever is stored there.

Scene 2: The Developer's Job

A student in the front row raised her hand. "Couldn't the developer just stop this from happening?"

"Exactly the right question," Yusuf said. "And yes β€” the developer can. The technique is called bounds checking. Before the program writes data into a buffer, it checks whether the incoming data actually fits. If the data is 8 bytes and the buffer holds 8 bytes β€” fine, proceed. If the data is 9 bytes and the buffer holds 8 bytes β€” reject it. Stop. Don't write."

He wrote it on the board: Is the data smaller than or equal to the buffer size? If yes: write it. If no: reject it.

"That check is the developer's responsibility," Yusuf said. "If a developer performs proper bounds checking everywhere that data is written into memory, buffer overflows cannot occur. The overflow is stopped before it starts."

"The problem," he continued, "is that developers make mistakes. They forget to add the check. They check the wrong size. They use a function that doesn't check at all. Attackers know this, and they spend a significant amount of time going through applications looking for exactly these openings β€” places where data flows into memory without a proper bounds check in place."

Bounds checking is the developer practice that prevents buffer overflows β€” verifying that incoming data fits within the allocated buffer before writing. Without it, overflows are possible wherever untrusted data reaches memory.

Scene 3: Harder Than It Looks

"Now," Yusuf said, "you might think: if I find a buffer overflow, I instantly own the system. That's not how it works." He put down the marker. "Buffer overflows are not simple vulnerabilities to exploit. Finding one is the beginning of a long process, not the end."

He explained the problem. When an attacker writes more data than a buffer holds, the overflow corrupts adjacent memory. But adjacent memory may contain anything. If the overflow damages something the program relies on to run correctly, the application crashes. Not a useful outcome for the attacker β€” a crashed application cannot do anything the attacker wants it to do.

"Attackers spend a lot of time trying different inputs, different amounts of overflow, different positions," he said. "They are trying to find the overflow that corrupts memory in a controlled, predictable way β€” not one that just crashes everything. The gap between 'I found an overflow' and 'I have a working exploit' is often enormous."

The same student spoke again. "So most overflows just crash things?" "Many of them do, yes," Yusuf said. "An attacker can make an application crash repeatedly. That might be a denial-of-service β€” stopping the application from working. But if what they really want is to make the application do something specific, crashes are worthless. They need something more precise."

Buffer overflows are not simple exploits. Writing too much data into memory often causes the application to crash rather than produce a useful result. Attackers need to find the specific overflow that produces a controlled outcome β€” and that takes considerable time and effort.

Scene 4: The Requirement for Repeatability

"There is one requirement," Yusuf said, "that separates a truly dangerous buffer overflow from an interesting curiosity. The exploit must be repeatable."

He wrote it on the board and underlined it twice.

"Repeatable means: every time the attacker sends this specific input to this specific application, the same thing happens. Not sometimes. Not most of the time. Every time. The overflow always lands in the same place, always changes memory in the same way, always produces the same outcome." He paused. "When an attacker finds an overflow that is repeatable, the system can be compromised reliably. That overflow can be turned into a tool. It can be automated. It can be deployed against every machine running that software."

"A buffer overflow that crashes the application 60% of the time and does something useful 40% of the time is not particularly dangerous. A buffer overflow that does exactly what the attacker wants 100% of the time β€” every single attempt β€” that is what makes an application vulnerable in a serious way."

Repeatability is the key requirement for a dangerous buffer overflow exploit. A useful buffer overflow always produces the same outcome β€” reliably, every time. When an attacker achieves repeatability, the system can be compromised at will.

Scene 5: The Variable A and Variable B Demo

Yusuf turned back to the board and drew a new diagram. Two rectangles, side by side in memory.

"Let me show you a concrete example of what this looks like β€” and what an attacker can achieve with it." He labelled the first rectangle Variable A and wrote 8 bytes β€” currently empty (all zeros) beneath it. He labelled the second rectangle Variable B and wrote 2 bytes β€” current value: 1,979.

"These two variables sit next to each other in memory. Variable A is 8 bytes and is currently empty. Variable B is 2 bytes and currently holds the value 1,979." He turned to the class. "Variable B controls how this application grants rights and permissions. If variable B's value is below 2,000 β€” user gets guest rights. If variable B's value is above 24,000 β€” user gets administrator rights."

"Variable B currently sits at 1,979. This user has guest rights. And under normal operation, there is no way for the user to change variable B's value. It is not exposed through the application's interface. It can only be changed from inside the program itself β€” or so the developer believed."

He circled variable A. "But the attacker has found a vulnerability here. The program allows input to be written into variable A β€” and it performs no bounds checking. And the attacker knows that if they can write 9 bytes into variable A, the first 8 bytes fill variable A, and the 9th byte overflows into variable B."

"So the attacker chooses their input carefully. They want to write a word into variable A that is exactly 9 characters long. The first 8 characters fill the buffer. The 9th character is the one that matters β€” it will overflow into variable B and change its value." He wrote on the board: "excessive". "Nine characters. E-X-C-E-S-S-I-V-E."

"The first 8 characters β€” E, X, C, E, S, S, I, V β€” are stored in variable A. Everything looks fine so far. Then comes the 9th character: the letter E." He pointed at the diagram. "That E overflows past the end of variable A and lands in variable B."

"In memory, the letter E has a value. Its hexadecimal value is 65. In decimal, that is 101. But that single byte landing in variable B changes variable B's value to 25,856." He wrote the number on the board. "25,856. Is that above 24,000?" He looked at the class. Several students nodded. "The attacker now has administrator rights. No username. No password. No admin credentials of any kind. Just the word 'excessive' sent as input."

The variable A/B example: variable A (8 bytes) and variable B (2 bytes) sit adjacent in memory. Storing "excessive" (9 characters) fills variable A and overflows the letter "E" (hex 65) into variable B, changing its value from 1,979 to 25,856 β€” above the 24,000 threshold for administrator rights. Privilege escalation achieved without credentials.

Scene 6: The Point

Yusuf set down his marker. "What this example shows is not just how a buffer overflow works mechanically. It shows what the consequence can be. An attacker who finds a repeatable, controlled buffer overflow can change values in memory that the developer never intended to be changeable. In this case, the value that changed controlled administrative access."

"Adjacent memory can hold anything. Another variable. A value that controls permissions. An instruction that determines what the program does next. Every one of those is a potential target if a buffer sits nearby and an attacker can overflow it in a controlled way."

He wrote the three requirements for a dangerous buffer overflow on the board:

1. A buffer with no bounds checking β€” somewhere data is written without verifying it fits.

2. Something valuable in adjacent memory β€” a variable, a permission flag, a control structure worth overwriting.

3. Repeatability β€” the overflow consistently changes adjacent memory in the desired way.

"When all three are present," he said, "the developer has created a door that they didn't know was there. And the attacker walks right through it."

The three conditions for a dangerous buffer overflow: (1) a buffer with no bounds checking, (2) something valuable in adjacent memory, (3) a repeatable overflow that reliably produces the desired outcome. Proper bounds checking by the developer eliminates condition one β€” and without condition one, the rest cannot happen.