Just another pwn task. Break in!
nc pwnie.2015.volgactf.ru 7777
I solve the challenge after the end of the CTF, because I think this is a great challenge for practicing format string and sprintf BOF vulnerability. Special thanks to Lays for putting the exploit on the trello and let me have time to study the challenge.
We got a 32 bit ELF, with stack guard enabled, but no NX.
It's a simple echo server. Whenever someone connect to it, it fork a process to handle the request. First it ask us to input some string, and then it echo the string back to us.
Launch it with IDA Pro and take a look at it:
start_echo() will call
echoing(v4, fd) , which v4 is a pointer to char
Let's take a look at the
So we found that there's a format string vulnerability at the line
sprintf(a1 + 6, user_input);
But notice that the program will filter out the character 'n', which means we can't use %n to write the memory.
Fortunately, the vulnerability's happened in
sprintf(a1 + 6, user_input) means the
user_input will be output to buffer
a1 (the one that
start_echo pass into
This behavior can be view as the program copy
user_input's content to
a1. If we input string
"%45c", the program will output 45 characters to
a1, which is, copy 45 characters to
So if we construct the payload string carefully, we can overwrite the return address in
start_echo(). First we'll have to leak the stack canary. After checking the memory by using gdb, we can found that the canary is at
%26$p. We also need to leak
ebp too, since we need to calculate the address of
a1 ( which is on the stack in function
start_echo() ). We found that
ebp is at
So by leaking canary &
start_echo()'s ebp , we can construct our payload now. But there're some details we need to be aware of:
sprintf will stop at null byte. That is, when
sprintf encounter a null byte, it will write the null byte to the buffer and stop.
2. So, since the first byte of the canary is always null byte, we'll have to modify the canary first, so
sprintf will continue writing bytes to buffer and let us overwrite the return address.
3. After we overwrite the return address, we'll have to change the canary back to its correct value, by sending another payload string to
4. Don't forget to write the
fd too, or else it will be overwritten to null byte (thanks to
sprintf apparently )
Here's the payload string:
So now we overwrite the return address of function
start_echo() and let it jump to the buffer
a1, which we can put our shellcode on it. But again, there're some limitations:
1. It's a fork server, so we can't simply just execute
execve("/bin/sh"). The problem cause by file descriptor will make us fail to execute our own command after we get the shell.
2. So, we'll have to construct a shellcode, which execute
dup2(4, 1) and
dup2(4, 2) before executing
3. Don't forget the program will filter out the character 'n', so we'll have to do something with it.
For problem 3, my solution was construct a shellcode without having character 'n'. Or, you can just overwrite the return address to
user_input, since the program doesn't modify the input, the original input string will still remain on the buffer.
Anyway, I choose to construct a shellcode without having character 'n'. Typically, during the shellcode construction for executing
execve("/bin/sh"), we'll have the following assembly:
Which has 'n' in it. However, we can simply use
xor to eliminate the character 'n'. Since
0x6e69622f = 0x91969dd0 ^ 0xffffffff, we can modify the assembly into:
So here's the entire assembly:
Finally, we have the exploit:
Great challenge, learn a lot from it!