%dwith the value,
%fwith the float value and
%xwith the hex representation.
printfexpects as many parameters as format string specifiers, and in 32-bit it grabs these parameters from the stack. If there aren't enough parameters on the stack, it'll just grab the next values - essentially leaking values off the stack. And that's what makes it so dangerous.
1$between tells printf to use the first parameter. However, this also means that attackers can read values an arbitrary offset from the top of the stack - say we know there is a canary at the 6th
%p- instead of sending
%p %p %p %p %p %pwe can just do
%6$p. This allows us to be much more efficient.
%sformat specifier, it's the pointer that gets passed to it. That means instead of reading a value of the stack, you read the value in the memory address it points at.
%x. That's because we're reading the buffer. Here it's at the 4th offset - if we can write an address then point
%sat it, we can get an arbitrary write!
%s- if all goes well, it should read the first bytes of the file, which is always
\x7fELF. Start with the basics:
0x8048000, so let's replace the
0x41424344with that and read it with
printfstops at null bytes, and the very first character is a null byte. We have to put the format specifier first.
|because we want the address we write to fill one memory address, not half of one and half another, because that will result in reading the wrong address
%8$pbecause the start of the buffer is generally at
%6$p. However, memory addresses are 4 bytes long each and we already have 8 bytes, so it's two memory addresses further along at
It still stops at the null byte, but that's not important because we get the output; the address is still written to memory, just not printed back.
%swill also stop at a null byte as strings in C are terminated with them. We have worked out, however, that the first bytes of an ELF file up to a null byte are
%n. This specifier takes in a pointer (memory address) and writes there the number of characters written so far. If we can control the input, we can control how many characters are written an also where we write them.
0x8048000to a memory address, we would have to write that many characters - and generally buffers aren't quite that big. Luckily there are other format string specifiers for that. I fully recommend you watch this video to completely understand it, but let's jump into a basic binary.
authwith the value 10. Format string vulnerability is obvious, but there's also no buffer overflow due to a secure
readelfto check for symbols.
%nformat string exploits:
offsetin this case is
7because the 7th
%pread the buffer; the location is where you want to write it and the value is what. Note that you can add as many location-value pairs into the dictionary as you want.
authsymbol with pwntools:
Check out the pwntools tutorials for more cool features