The code has undefined behavior and is invalid.

Anyway, the statement:

printf ("%16u%n", 7350, (int *) &foo[0]);

is basically doing:

*(int *)&foo[0] = 16;

The biggest issue is with the last one:

printf("%128u%n", 7350, (int *) &foo[3]);

it's doing:

*(int *)&foo[3] = 128;

but foo[3] is an unsigned char. Assuming sizeof(int) = 4, ie. int has 4 bytes, then this writes 3 bytes out of bounds to foo + 3. x86 stores stack in reverse order - the memory reserved for canary is put after the memory for foo. The stack memory looks like this:

   <-- foo ---><--- canary ----->
   [0][1][2][3][0][1][2][3][4][5]
            ^^^^^^^^^^^^ 
                 storing (int)128 here in **little endian**

Because x86 is little endian, foo[3] is assigned the value of 128, and canary[0..2] are zeroed (because 128 = 0x00000080).

You can do:

// I want it to print 0xDEAD
// I swap bytes for endianess I get 0xADDE
// I then shift it left by 8 bytes and get 0xADDE00
// 0xADDE00 = 11394560
// The following printf will do foo[3] = 0x00
// but also: canary[0] = 0xDE, canary[1] = 0xAD and canary[2] = 0x00
fprintf("%11394560u%n", 7350, (int *) &foo[3]);
printf("0x%02x%02x\n", canary[0], canary[1]);
// will output 0xDEAD
Answer from KamilCuk on Stack Overflow
Top answer
1 of 3
1

The code has undefined behavior and is invalid.

Anyway, the statement:

printf ("%16u%n", 7350, (int *) &foo[0]);

is basically doing:

*(int *)&foo[0] = 16;

The biggest issue is with the last one:

printf("%128u%n", 7350, (int *) &foo[3]);

it's doing:

*(int *)&foo[3] = 128;

but foo[3] is an unsigned char. Assuming sizeof(int) = 4, ie. int has 4 bytes, then this writes 3 bytes out of bounds to foo + 3. x86 stores stack in reverse order - the memory reserved for canary is put after the memory for foo. The stack memory looks like this:

   <-- foo ---><--- canary ----->
   [0][1][2][3][0][1][2][3][4][5]
            ^^^^^^^^^^^^ 
                 storing (int)128 here in **little endian**

Because x86 is little endian, foo[3] is assigned the value of 128, and canary[0..2] are zeroed (because 128 = 0x00000080).

You can do:

// I want it to print 0xDEAD
// I swap bytes for endianess I get 0xADDE
// I then shift it left by 8 bytes and get 0xADDE00
// 0xADDE00 = 11394560
// The following printf will do foo[3] = 0x00
// but also: canary[0] = 0xDE, canary[1] = 0xAD and canary[2] = 0x00
fprintf("%11394560u%n", 7350, (int *) &foo[3]);
printf("0x%02x%02x\n", canary[0], canary[1]);
// will output 0xDEAD
2 of 3
0

why does it overwrite to the adjacent memory?

Undefined behavior (UB).

All lines below exhibit UB. What OP sees to a potential result, but not specified by C.

/* 0 * before */ strcpy (canary, "AAAA");           // Writing out of bounds
/* 1 */  printf ("%16u%n", 7350, (int *) &foo[0]);  // Writing out of bounds, alignment issues.
/* 2 */  printf ("%32u%n", 7350, (int *) &foo[1]);
/* 3 */  printf ("%64u%n", 7350, (int *) &foo[2]);
/* 4 */  printf ("%128u%n", 7350, (int *) &foo[3]);
🌐
Codearcana
codearcana.com › posts › 2013 › 05 › 02 › introduction-to-format-string-exploits.html
Introduction to format string exploits
May 2, 2013 - If we were to pass the string AAAA$n, we would write the value 4 to the address 0x41414141! We can use another printf feature to write larger values: if we do printf("AAAA0x"), 104 characters will be output (because 0x prints the argument padded to at least 100 characters). We can do AAAA%<value-4>x$n to write an arbitrary value to 0x41414141.
🌐
Gts3
tc.gts3.org › cs6265 › tut › tut05-fmtstr.html
Tut05: Format String Vulnerability - CS6265: Information Security Lab
To do that, we need to use another useful printf() format specifier: %[len]d (e.g., d). This prints an integer (we don't care which one) using at minimum len characters. This can be used to quickly raise the value that %n will write, without requiring an excessively long format string.
🌐
Stanford
cs155.stanford.edu › papers › formatstring-1.2.pdf pdf
Exploiting Format String Vulnerabilities scut / team teso September 1, 2001
September 1, 2001 - For the counter in the format function we can control the least significant · byte, the first byte stored in memory by using dummy ‘%nu’ parameters to ... Returns the output “10204080” and “canary: 00000041”. We over- write four times the least significant byte of an integer we point to. By · increasing the pointer each time, the least significant byte moves through · the memory we want to write to, and allows us to store completely arbitrary
🌐
Null Byte
null-byte.wonderhowto.com › how-to › exploit-development-write-specific-values-memory-with-format-string-exploitation-0182112
Exploit Development: How to Write Specific Values to Memory with Format String Exploitation :: Null Byte
March 9, 2018 - During our last adventure into the realm of format string exploitation, we learned how we can manipulate format specifiers to rewrite a program's memory with an arbitrary value. While that's all well and good, arbitrary values are boring. We want to gain full control over the values we write, and ...
🌐
OWASP Foundation
owasp.org › www-community › attacks › Format_string_attack
Format string attack | OWASP Foundation
In this case, if a Format String ... the string is parsed by the Format Function, and the conversion specified in the parameters is executed. However, the Format Function is expecting more arguments as input, and if these arguments are not supplied, the function could read or write the stack. In this way, it is possible to define a well-crafted input that could change the behavior of the format function, permitting the attacker to cause denial of service or to execute arbitrary ...
🌐
Medium
nikhilh20.medium.com › format-string-exploit-ccefad8fd66b
Format String Exploit. One of the most commonly used functions… | by ka1d0 | Medium
February 12, 2019 - We now know the address to which we are going to write. We need to find a way to get it on the stack. This is easy and intuitive. If we pass the address to printf() as an argument, it'll be placed on the stack. This should be obvious to those who understand the C function call mechanism. If we form a format string which consists of multiple %x and the address of flag, the program is bound to output the address of flag back to us at some point. This follows the concept of arbitrary read that we discussed before.
Find elsewhere
🌐
BreakInSecurity
axcheron.github.io › exploit-101-format-strings
Exploit 101 - Format Strings - BreakInSecurity
April 22, 2018 - The problem lies into the use of ... the stack or other locations in memory. You can also write arbitrary data to arbitrary locations using the %n format specifier, but we will see that later....
🌐
InfoSec Blog
kevinalmansa.github.io › application security › Format-Strings
Introduction to Format Strings - InfoSec Blog
August 7, 2017 - In this post we presented basic examples of how format string vulnerabilities can be leveraged to achieve arbitrary reads and writes to memory.
Top answer
1 of 1
6

It's a lot of questions, here are a few answers:

How can we write something in memory with a format string vulnerability ?

For this, you need to know two specific features used in the printf format string specifications. First, %n is a format specifier that has the following effect (according to the manual page):

%n The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted.

Now, the second format string feature will allow us to select a specific argument from the format string. The main selection operator is $, and the following code means that we select the second argument (here the outcome will be to display 2):

printf("%2$x", 1, 2, 3)

But, in the general case, we can do printf("%<some number>$x") to select an arbitrary argument of the current printf function (format string argument does not count).

If we could pass the string AAAA%10$n to the program and make it appear as a format string, then we could write the value 4 to the address 0x41414141.

So, format string bugs may offer a full access to the memory and you can decide what to write and where in the memory.

I really advise you to read "Exploiting Format String Vulnerabilities" from Scut (2001) to get a whole grasp on these kind of manipulations.

Are they other languages than C/C++ that are vulnerable to these bugs ?

Well, format string bugs are tied up to the printf function family and the way format strings may be passed to the function. It's a whole class of security issue itself. So, you might find ways to exploit similar problems in some other languages. Though, you may not find the exact same features as the format string capabilities in other languages may differ a lot.

I do think about languages such as Perl, Python, and so on, that all offer similar access to format string features.

How can I spot format string vulnerabilities if I have only the binary ?

First, you have to locate the calls to procedure of the printf family. Then, I would say that fuzz-testing (fuzzing) should be a good way to find the vulnerabilities. Especially if you can forge a few entries with seeds such as AAAA%10$n.

If you want a more accurate and exhaustive way to find it, you will probably need to do some binary analysis and taint analysis on every call to a procedure of the printf family.

🌐
Unipi
lettieri.iet.unipi.it › hacking › format-strings.pdf pdf
8. Format Strings The a’s at the beginning are just for alignment, the %u’s
October 18, 2023 - arguments in the same format string “program”. This is rather useless for instructions like “%x”, but consider the “%s” instruction, instead. Normally, this prints a string, but when reinterpreted as in instruction for our printf()machine, it prints the · contents of memory starting from the address specified by its argument and stopping at the first null · byte. If the attacker can choose the address that the instruction will use, it is an arbitrary memory read
🌐
Null Byte
null-byte.wonderhowto.com › how-to › security-oriented-c-tutorial-0x14-format-string-vulnerability-part-buffer-overflows-nasty-little-brother-0167254
Security-Oriented C Tutorial 0x14 - Format String Vulnerability Part I: Buffer Overflow's Nasty Little Brother :: Null Byte
December 26, 2015 - Remember that the static keyword places the variable into the data segment which is out of our reach from the stack and because of this, we cannot do a simple buffer overflow to overwrite it. What we will perform is an arbitrary memory write using the format string vulnerability.
🌐
Tum
sec.in.tum.de › i20 › publications › blind-format-string-attacks › @@download › file › formatstring.pdf pdf
Blind Format String Attacks Fatih Kilic, Thomas Kittel, and Claudia Eckert
memory leakage to exploit a format string vulnerability. Using our approach, we · can exploit an FSA blindly without having any output channel to the attacker · or access to the local system. Our concept extends the classical FSAs to write to · arbitrary memory locations even in cases where the format string is not stored ·
🌐
Exploit-DB
exploit-db.com › docs › english › 28476-linux-format-string-exploitation.pdf pdf
Format String Exploitation-Tutorial By Saif El-Sherei www.elsherei.com
Now to write address 0xDDCCBBAA 4 writes 1 byte at a time are required shown below: ... The width of the format string “%8x” for example is the minimum which will pad the output of the
🌐
MetaCPAN
metacpan.org › pod › String::Format
String::Format - sprintf-like string formatting capabilities with arbitrary format definitions - metacpan.org
String::Format lets you define arbitrary printf-like format sequences to be expanded. This module would be most useful in configuration files and reporting tools, where the results of a query need to be formatted in a particular way.
🌐
HackTricks
book.hacktricks.xyz › binary-exploitation › format-strings › format-strings-arbitrary-read-example
Format Strings - Arbitrary Read Example - HackTricks
July 18, 2024 - from pwn import * p = process("./fs-read") def leak_heap(p): # At offset 25 there is a heap leak p.sendlineafter(b"first password:", b"%$p") p.recvline() response = p.recvline().strip()[2:] #Remove new line and "0x" prefix return int(response, 16) heap_leak_addr = leak_heap(p) print(f"Leaked heap: {hex(heap_leak_addr)}") # Offset calculated from the leaked position to the possition of the pass in memory password_addr = heap_leak_addr + 0x1f7bc print(f"Calculated address is: {hex(password_addr)}") # At offset 14 we can control the addres, so use %s to read the string from that address payload = f"$s|||".encode() payload += p64(password_addr) p.sendline(payload) output = p.clean() print(output) p.close()
🌐
Synacktiv
synacktiv.com › en › publications › exploiting-a-blind-format-string-vulnerability-in-modern-binaries-a-case-study-from
Exploiting a Blind Format String Vulnerability in Modern Binaries: A
The print_debug_msg function allows an attacker to control the format string passed to vsnprintf, leading to potential arbitrary memory writes.
🌐
ScienceDirect
sciencedirect.com › topics › computer-science › format-specifier
Format Specifier - an overview | ScienceDirect Topics
These vulnerabilities are often found when the format specifier string is created based on data supplied by the user. If the user data contains a conversion specifier, such as “%s” or “%d”, the function will try to process arguments beyond what was provided by the programmer.