This demonstration shows another fun exploitation of a programmer error: a format string vulnerability lets you read and write memory that you aren’t supposed to be able to access.
See also:
Download: formatstring.c
Compilation
gcc -o formatstring formatstring.c
# You may also need to disable ASLR system-wide
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Source Code
/*
* Format String Vulnerability Demonstration
*
* Copyright (c) 2025 Martin Domig <martin@domig.net>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This program demonstrates a format string vulnerability.
* The user can control the format string passed to printf.
*
* EDUCATIONAL PURPOSE ONLY - DO NOT USE IN PRODUCTION
*/
#include <stdio.h>
#include <stdlib.h>
/***********************************************************************
FORMAT STRING VULNERABILITY
This demonstrates a format string vulnerability: the user controls the format
string to printf.
To compile: gcc -o formatstring formatstring.c
Note: Modern systems increase the difficulty of memory attacks using ASLR
(Address Space Layout Randomization).You can disable ASLR (Address Space Layout
Randomization) and other security features to test this, but you must never do
this in production:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Possible exploits:
1. Leaking Stack Information:
If the user inputs a format string like "%x %x %x", it can leak stack
information, including addresses and possibly sensitive data.
./formatstring "%08x %08x %08x %08x"
2. Arbitrary Memory Access:
If the user inputs a format string like "%s", it can read arbitrary memory
locations, potentially exposing sensitive information or crashing the
program.
./formatstring "%s"
This can be used to read values from stack. For example, this prints the
stack contents, including the given format string itself as string:
./formatstring "BEGIN>%08x %08x %08x %08x %08x %08x %08x %08x %s<END"
Reading from stack addresses as strings:
./formatstring "%s %s %s %s"
Note: These examples may crash the program or show limited results due to
modern security measures. The vulnerability is demonstrated by the ability
to read stack values and potentially crash the program.
Now, let's try to read the secret message from the stack, the secret string
pointer appears in stack position 8:
./formatstring "%8\$s"
This directly displays our secret string!
Alternatively, you can first find where the secret is located by examining
stack values:
./formatstring "%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x"
Look for ASCII-like hex values (20756f59 = " ouY" in reverse byte order).
3. Attempting to Write to Memory:
If the user inputs a format string like "%n", it can attempt to write to a
memory location, which can lead to arbitrary code execution or crashes.
./formatstring "%n"
To overwrite our secret, we can use the pointer at stack position 8.
The %n format specifier writes the number of characters output so far
to the memory address:
./formatstring "HACKED%8\$n"
This writes the value 6 (length of "HACKED") to our secret buffer,
effectively overwriting it! Check the checksum before and after.
For a more targeted attack, you could construct the secret's address and
use it with %n to overwrite the secret buffer (this requires knowing the
exact memory layout and is complex with modern protections).
***********************************************************************/
// The compiler will only catch obvious format string vulnerabilities
// like this one. This directive disables that warning.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
static unsigned int calculate_checksum(const char *str) {
unsigned int checksum = 0;
while (*str) {
checksum += *str++;
}
return checksum;
}
int main(int argc, char *argv[]) {
char our_secret[] = "You are not supposed to know this!";
char *secret_ptr = our_secret; // Put a pointer to the secret on the stack
// Even though secret_ptr is used in calculate_checksum(), we still need this
// asm directive to ensure consistent stack layout, especially with
// optimization
asm volatile("" : : "r"(our_secret), "r"(secret_ptr) : "memory");
if (argc < 2) {
fprintf(stderr, "Usage: %s <name>\n", argv[0]);
return EXIT_FAILURE;
}
const char *name = argv[1];
printf("Format string vulnerability demonstration\n");
printf("Hello, ");
printf(name); // Vulnerable to format string attack
printf("!\n");
printf("Secret checksum: %08x\n", calculate_checksum(secret_ptr));
return EXIT_SUCCESS;
}