If you're using the example code from the book (below), at some point you should reach the "AAAAAAAA" pattern (0x41). Note that, since you're running it on a 64-bit machine that stores elements in the stack with 8 bytes each, you should run it with $ ./fmtstr "AAAAAAAA %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x" instead, or you will miss part of each element on the stack.

#include <stdlib.h>
int main(int argc, char *argv[]){
        static int canary=0;   // stores the canary value in .data section
        char temp[2048];       // string to hold large temp string
      strcpy(temp, argv[1]);   // take argv1 input and jam into temp
      printf(temp);            // print value of temp
      printf("\n");            // print carriage return
      printf("Canary at 0x%08x = 0x%08x\n", &canary, canary); //print canary
}

You should pay attention to the quote in the book that states:

The fact that the fourth item shown (from the stack) was our format string depends on the nature of the format function used and the location of the vulnerable call in the vulnerable program. To find this value, simply use brute force and keep increasing the number of %08x tokens until the beginning of the format string is found. For our simple example (fmtstr), the distance, called the offset, is defined as 4.

Remember that the parameter being parsed to printf isn't the string itself, but the address of the string. So it's position on the memory layout of the program in relation to the printf stack is what will define how further you'll have to search to find it.

Answer from murphsghost on Stack Exchange
Top answer
1 of 1
6

If you're using the example code from the book (below), at some point you should reach the "AAAAAAAA" pattern (0x41). Note that, since you're running it on a 64-bit machine that stores elements in the stack with 8 bytes each, you should run it with $ ./fmtstr "AAAAAAAA %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x %016x" instead, or you will miss part of each element on the stack.

#include <stdlib.h>
int main(int argc, char *argv[]){
        static int canary=0;   // stores the canary value in .data section
        char temp[2048];       // string to hold large temp string
      strcpy(temp, argv[1]);   // take argv1 input and jam into temp
      printf(temp);            // print value of temp
      printf("\n");            // print carriage return
      printf("Canary at 0x%08x = 0x%08x\n", &canary, canary); //print canary
}

You should pay attention to the quote in the book that states:

The fact that the fourth item shown (from the stack) was our format string depends on the nature of the format function used and the location of the vulnerable call in the vulnerable program. To find this value, simply use brute force and keep increasing the number of %08x tokens until the beginning of the format string is found. For our simple example (fmtstr), the distance, called the offset, is defined as 4.

Remember that the parameter being parsed to printf isn't the string itself, but the address of the string. So it's position on the memory layout of the program in relation to the printf stack is what will define how further you'll have to search to find it.

🌐
nixhacker
nixhacker.com › case-of-format-string-in-64-bit-is-it-still-critical
Impact of x64 calling convention in format string exploitation
October 19, 2020 - The exploitation of format string ... to remote code execution. In 64 bit system the format strings exploitation is still present but the basics get changed a little due to 64 bit calling convention....
🌐
Reddit
reddit.com › r/liveoverflow › format string exploitation on 64 bit. how about the null bytes?
r/LiveOverflow on Reddit: Format string exploitation on 64 bit. How about the null bytes?
April 26, 2020 -

I'm new to the argument, but I'm trying to do some very simple exercises on format string vulnerability. I'm unfortunately incurring in a problem: when I write the target address in which I will write with %_c (for example \x38\xdb\xff\xff\xff\x7f for 0x00007fffffffdb38) im not able to replicate the 0x0000 right before 7fffffffdb38. So when I try to run it the program will try to write with %c in the address : 0x[ _ _ _ --> random values that were previously there]7fffffffdb38. How can I actually write there 0x00007fffffffdb38? Obviously by doing : \x38\xdb\xff\xff\xff\x7f\x00\x00 won't solve the problem, as it will not overwrite the first 4 slots of the address.

Thanks to anyone that will try to help me.

🌐
Seedsecuritylabs
seedsecuritylabs.org › Labs_20.04 › Files › Format_String_x64 › Format_String_x64.pdf pdf
SEED Labs – Format String Vulnerability Lab (64-bit) 1
supports 64-bit address space, only the address from 0x00 through 0x00007FFFFFFFFFFF is allowed. That means for every address (8 bytes), the highest two bytes are always zeros. This causes a problem. In the attack, we need to place addresses inside the format string.
🌐
Stack Overflow
stackoverflow.com › questions › 30408045 › format-string-vulnerability-exploit-in-64-bits-systems
linux - Format string vulnerability exploit in 64 bits systems - Stack Overflow
September 12, 2016 - However there seems to be other issues too trying to exploit this weakness. That is, you can't write zeroes into memory with this method, as the printf will treat any zeroes as end-of-string and will stop there. Thus the address where you want to write to must not have leading zero bytes. This issue is already mentioned in http://www.thenewsh.com/~newsham/format-string-attacks.pdf.
🌐
DEF CON
defcon.org › images › defcon-18 › dc-18-presentations › Haas › DEFCON-18-Haas-Adv-Format-String-Attacks.pdf pdf
Advanced Format String Attacks Presented by Paul Haas
The largest hacking and security conference with presentations, workshops, contests, villages and the premier Capture The Flag Contest.
🌐
Reddit
reddit.com › r/liveoverflow › 64-bit format string exploitation.
r/LiveOverflow on Reddit: 64-bit Format String Exploitation.
May 5, 2017 -

Hey LiveOverflow,

I've recently come across a few 64-bit Format String challenges in CTF's and they always seem to stump me. This is mainly because there seems to be very little on the web for 64-bit format strings exploitation (most is x86).

Are you aware of any content that could help me in learning how to pwn 64-bit format strings?

Thanks!

🌐
Blogger
ret2rop.blogspot.com › 2018 › 08 › format-string-defeating-stack-canary-nx-aslr-remote.html
Format String Exploits: Defeating Stack Canary, NX and ASLR Remotely on 64 bit
July 14, 2021 - So our strategy will be first to send format strings then read output and extract libc address and stack canary. Then calculate libc base from the address and generate a return to libc payload. We will call setuid(0); first, as we know modern systems drop privileges when not required in setuid binaries. Since this is 64 bit we will pop 0x0 to rdi register and it will be passed to setuid.
Find elsewhere
🌐
Infosec Institute
infosecinstitute.com › resources › secure-coding › how-to-exploit-format-string-vulnerabilities
How to exploit format string vulnerabilities | Infosec
September 21, 2020 - Let us pass multiple %llx strings as user input separated by a colon. %llx is to print long hex values since we are working on a 64-bit processor.
🌐
Barrebas
barrebas.github.io › blog › 2015 › 02 › 22 › maximum-overkill-two-from-format-string-vulnerability-to-remote-code-execution
Maximum Overkill Two - From Format String Vulnerability to Remote Code Execution - staring into /dev/null
The strncpy takes care of that; any null byte it encounters will make it stop copying. Because we’re on 64-bit, the gadget addresses will for sure contain null bytes.
🌐
tripoloski blog
tripoloski1337.github.io › ctf › 2020 › 06 › 11 › format-string-bug.html
Exploiting Format String bug | tripoloski blog
June 11, 2020 - on 64bit there is null char on the address , so we have to place the address at the end of the payload because if printf found a null char ‘\x00’ it will simply terminate to print , i assume you already know about little endian · for example to exploit format string and doing arbitrary ...
🌐
Stanford
cs155.stanford.edu › papers › formatstring-1.2.pdf pdf
Exploiting Format String Vulnerabilities scut / team teso September 1, 2001
September 1, 2001 - Until now we have assumed that the format string always lies on the stack. But however, there are cases, where it is stored on the heap. If there is · another buffer on the stack we can influence we can use that one to supply the · addresses to write to, but if there is no such buffer we have few alternatives ... Beside the exploitation itself there are some things to consider.
🌐
Jaybosamiya
jaybosamiya.com › blog › 2017 › 04 › 06 › adv-format-string
"Advanced" Format String Exploitation · Jay Bosamiya
April 6, 2017 - As for overwriting the __malloc_hook, it works even if the application doesn't call malloc, since it is calling printf (or similar), and internally, if we pass a width specifier greater than 64k (say p000c), then it will call malloc, and thus whatever address was specified at the global variable __malloc_hook. If we have our format string buffer not on the stack, then we can still gain a write-what-where primitive, though it is a little more complex.
🌐
University of Virginia
cs.virginia.edu › ~cr4bd › 4630 › S2017 › slides › 20170322-slides-1up-animated.pdf pdf
1
April 1, 2017 - format string exploit pattern (x86-64) goal: write big 8-byte number at 0x1234567890ABCDEF: write 1000 (short) to address 0x1234567890ABCDEF · write 2000 (short) to address 0x1234567890ABCDF1 · buffer starts 16 bytes above printf return address · %c%c%c%c%c%c%c%c%c%c%c%.991u%hn%.1000u%hn…
🌐
Medium
infosecwriteups.com › exploiting-format-string-vulnerability-97e3d588da1b
EXPLOITING FORMAT STRING VULNERABILITY | by AidenPearce369 | InfoSec Write-ups
January 18, 2024 - You can “directly” reference the location in format string vulnerability to call the values from stack at your desired location · To print the stack values from 11th location you can use ‘$p’ to represent the 11th location of the stack ... Lets pass the address of the ‘data’ as our second input so that it gets stored in stack next to the first input · Pass the address in reverse order if “LITTLE ENDIAN” · Always remember to fill with 8 bytes / 4 bytes (64bit/32bit) or else the address / expected value may get broken
🌐
Infosec Institute
resources.infosecinstitute.com › topic › how-to-exploit-format-string-vulnerabilities
How to exploit format string vulnerabilities - Infosec Resources
September 21, 2020 - Let us pass multiple %llx strings as user input separated by a colon. %llx is to print long hex values since we are working on a 64-bit processor.
🌐
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 - As always, the first thing we should do is take a look at the source code available at Exploit Exercises: As we've seen in previous challenges, an integer variable called "target" is defined. I'd bet every dime I have that modifying this variable is going to have something to with our objective in this challenge. Looking at line 21, this is confirmed. We seem to be tasked with using a format string exploit to make the target variable hold the value "0x01025544."
Top answer
1 of 5
24

I think that the paper provides its printf() examples in a somewhat confusing way because the examples use string literals for format strings, and those don't generally permit the type of vulnerability being described. The format string vulnerability as described here depends on the format string being provided by user input.

So the example:

printf ("\x10\x01\x48\x08_%08x.%08x.%08x.%08x.%08x|%s|");

Might better be presented as:

/* 
 * in a real program, some user input source would be copied 
 * into the `outstring` buffer 
 */
char outstring[80] = "\x10\x01\x48\x08_%08x.%08x.%08x.%08x.%08x|%s|";

printf(outstring);

Since the outstring array is an automatic, the compiler will likely put it on the stack. After copying the user input to the outstring array, it'll look like the following as 'words' on the stack (assuming little endian):

outstring[0c]               // etc...
outstring[08] 0x30252e78    // from "x.%0"
outstring[04] 0x3830255f    // from "_%08"
outstring[00] 0x08480110    // from the ""\x10\x01\x48\x08"

The compiler will put other items on the stack as it sees fit (other local variables, saved registers, whatever).

When the printf() call is about to be made, the stack might look like:

outstring[0c]               // etc...
outstring[08] 0x30252e78    // from "x.%0"
outstring[04] 0x3830255f    // from "_%08"
outstring[00] 0x08480110    // from the ""\x10\x01\x48\x08"
var1
var2
saved ECX
saved EDI

Note that I'm completely making those entries up - each compiler will use the stack in different ways (so a format string vulnerability has to be custom crafted for a particular exact scenario. In other words, you won't always use 5 dummy format specifiers like in this example - as the attacker you'd need to figure out how many dummies the particular vulnerability would need.

Now to call printf(), the argument (the address of outstring) is pushed on to the stack and printf() is called, so the argument area of the stack looks like:

outstring[0c]               // etc...
outstring[08] 0x30252e78    // from "x.%0"
outstring[04] 0x3830255f    // from "_%08"
outstring[00] 0x08480110    // from the ""\x10\x01\x48\x08"
var1
var2
var3
saved ECX
saved EDI
&outstring   // the one real argument to `printf()`

However, printf doesn't really know anything about how many arguments have been placed on the stack for it - it goes by the format specifiers it finds in the format string (the one argument it's 'sure' to get). So printf() gets the format string argument and starts processing it. When it gets to the 1st "%08x" that will correspond to the 'saved EDI' in my example, then next "%08x" will print the saved ECX' and so on. So the "%08x" format specifiers are just eating up data on the stack until it gets back to the string the attacker was able to input. Determining how many of those are needed is something an attacker would do by a kind of trial and error (probably by a test run that has a whole slew of "%08x" formats until he can 'see' where the format string starts).

Anyway, when printf() gets to processing the "%s" format specifier, it has consumed all the stack entries up to where the outstring buffer resides. The "%s" specifier treats its stack entry as a pointer, and the string that the user has put into that buffer has been carefully crafted to have a binary representation of 0x08480110, so printf() will print out whatever is at that address as an ASCIIZ string.

2 of 5
9

You have 6 format specifiers (5 lots of %08x and one of %s), but you do not provide values for those format specifiers. You immediately fall into the realm of undefined behaviour - anything could happen and there is no wrong answer.

However, in the normal course of events, the values passed to printf() would have been stored on the stack, so the code in printf() reads values off the stack as if the extra values had been passed. The function return address is on the stack, too. There is no guarantee that I can see that the value 0x08480110 will actually be produced. This sort of attack very much depends on the the specific program and faulty function call, and you might well get a very different value. The example code is most likely written assuming a 32-bit Intel (little-endian) CPU - rather than a 64-bit or big-endian CPU.


Adapting the code fragment, compiling it into a complete program, ignoring the compilation warnings, using a 32-bit compilation on MacOS X 10.6.7 with GCC 4.2.1 (XCode 3), the following code:

#include <stdio.h>

static void somefunc(void)
{
    printf("AAAAAAAAAAAAAAAA.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.|%s|\n");
}

int main(void)
{
    char buffer[160] =
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz01234";
    somefunc();
    return 0;
}

produces the following result:

 AAAAAAAAAAAAAAAA.0x000000A0.0xBFFFF11C.0x00001EC4.0x00000000.0x00001E22.0xBFFFF1C8.0x00001E5A.|abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz01234|

As you can see, I eventually 'found' the string in the main program from the printf() statement. When I compiled it in 64-bit mode, I got a core dump instead. Both results are perfectly correct; the program invokes undefined behaviour, so anything the program does is valid. If you're curious, search for 'nasal demons' for more information on undefined behaviour.

And get used to experimenting with these sorts of issues.


Another variation

#include <stdio.h>

static void somefunc(void)
{
    char format[] =
        "AAAAAAAAAAAAAAAA.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X\n"
        ".0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X\n"
        ".0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X.0x%08X\n";
    printf(format, 1);
}

int main(void)
{
    char buffer[160] =
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz012345"
        "abcdefghijklmnopqrstuvwxyz01234";
    somefunc();
    return 0;
}

This produces:

AAAAAAAAAAAAAAAA.0x00000001.0x00000099.0x8FE467B4.0x41000024.0x41414141
.0x41414141.0x41414141.0x2E414141.0x30257830.0x302E5838.0x38302578.0x78302E58
.0x58383025.0x2578302E.0x2E583830.0x30257830.0x2E0A5838.0x30257830.0x302E5838

You might recognize the format string in the hex output - 0x41 is capital A, for example.

The 64-bit output from that code is both similar and different:

AAAAAAAAAAAAAAAA.0x00000001.0x00000000.0x00000000.0xFFE0082C.0x00000000
.0x41414141.0x41414141.0x2578302E.0x30257830.0x38302578.0x58383025.0x0A583830
.0x2E583830.0x302E5838.0x78302E58.0x2578302E.0x30257830.0x38302578.0x38302578
🌐
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 - This is where we will be placing our exploit once it is finished. For now, let's just try to answer the question we posed above: What happens when you have a format specifier with no variable to replace it with? We can see from the above source code that whatever string we pass as a command-line argument to the program will be printed on line 10 with the call to the printf function.