(0ctf) BABYHEAP - fastbin attack, chunk overlap
https://github.com/umbum/Python/blob/master/exploit/0ctf_babyheap.py
- Fill에서 Size를 지정할 때, chunk의 size보다 크게 지정할 수 있기 때문에 다음 chunk의 fd를 덮어 쓸 수 있고, 이를 이용해 원하는 곳에 chunk를 만들어 반환하도록 할 수 있다. ( fastbin attack )
- Allocate로 원하는 곳(execution point)에 chunk를 만든 다음, 2. Fill로 원하는 데이터를 쓴다.
execution point로는 GOT는 못쓰고(FULL RELRO), hooking function pointer인 \_\_malloc\_hook
을 사용할 수 있다.
execution point addr을 알아내기 위해 leak이 필수적이다. free’d small bin chunk의 fd
를 leak해서 libc_base를 얻어내야 한다. 그러기 위해서는 small bin chunk와 다른 chunk를 overlap시키고 나서 small bin chunk를 free해야 한다. small bin chunk의 위치를 모르기 때문에, heap이 00
으로 시작한다는 점과 chunk들의 크기를 이용해 small bin chunk의 위치를 예측하고 fastbin attack을 사용해 fastbin chunk를 overlap해야 한다. leak한 주소를 기점으로 \_\_malloc\_hook
의 주소를 알아낸다. * 어차피 offset으로 접근하는 거니까, libc 주소를 구한 다음 거기에 \_\_malloc\_hook
offset을 더해도 된다. * 테스트 환경에서는 main_arena를 알아내기 위해 $ find top chunk ptr
을 사용해도 좋다.
그 다음 smallbin의 fd를 overwrite하고 두 번 1. Allocate 해서 \_\_malloc\_hook
근처 size field를 만족하는 적절한 곳에 chunk를 생성하고, one gadget을 가리키도록 3. Fill 하면 one gadget이 실행된다. * shellcode를 넘기려면 fastbin chunk를 하나 생성하고 거기다 shellcode를 넘긴 후 one gadget 대신 mprotect()
를 가리키도록 해서 chunk가 포함된 page에 x
권한을 준 다음, 다시 shellcode를 가리키게 하는 방법을 생각해 볼 수 있지만 파라미터 푸시가 어렵다.
로컬 환경에서 테스트
main : 0x55555555511d
allocated 직후 : 0x555555554dd1
heap : 0x555555757000
fastbin, fastbin, smallbin, fastbin(brk방지) 네 개를 Allocate하고, smallbin을 free해주면 다음과 같이 fd/bk
가 생성된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0x555555757000: 0x0000000000000000 0x0000000000000031
0x555555757010: 0x0000000000000000 0x0000000000000000
0x555555757020: 0x0000000000000000 0x0000000000000000
0x555555757030: 0x0000000000000000 0x0000000000000031
0x555555757040: 0x0000000000000000 0x0000000000000000
0x555555757050: 0x0000000000000000 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000091
0x555555757070: 0x00007ffff7dd37b8 0x00007ffff7dd37b8
0x555555757080: 0x0000000000000000 0x0000000000000000
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000090 0x0000000000000030
gdb-peda$ x/4x 0x00007ffff7dd37b8
0x7ffff7dd37b8 <main\_arena+88>: 0x00005555557571b0
main\_arena
를 기점으로 fd
를 overwrite할 주소인 \_\_malloc\_hook
의 근처에 size field로 쓸만한 적당한 곳을 모색한다.
1
2
3
4
5
6
7
8
9
0x7ffff7dd3730 <\_\_realloc\_hook>: 0x00007ffff7a98b40 0x0000000000000000
0x7ffff7dd3740 <\_\_malloc\_hook>: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/4x 0x00007ffff7dd372d
0x7ffff7dd372d: 0xfff7a98b40000000 0x000000000000007f
0x7ffff7dd373d: 0x0000000000000000 0x0000000000000000
실제로 remote에서 수행할 때는 0x7ffff7dd372d
의 주소를 특정할 수 없기 때문에 leak한 <main\_arena+88>
의 주소와 0x7ffff7dd372d
주소의 차이를 계산해두거나, libc base로부터 얼마나 떨어져 있는지 offset을 게산해 두어야 한다. 어차피 나중에 one gadget 사용하기 위해서는 libc base addr이 있어야 하므로 libc base를 기준으로 offset을 구하면 libc base addr : 0x00007ffff7a15000
leak으로 얻는 주소 : libc base addr + 0x3be7b8
\_\_malloc\_hook
근처 mchunkptr : libc base addr + 0x3be72d
* malloc()
이 반환하는 주소는 0x3d
이며 \_\_malloc\_hook
주소는 0x40
이라는 점 주의. one_gadget offset : libc base addr + 0xe66bd
Note) smallbin chunk의 fd
를 \_\_malloc\_hook
주소로 변경하고, Allocate해도 chunk가 생성되지 않는다. smallbin chunk의 size는 0x91
인데 execution point chunk의 size는 0x7f
로 fastbin의 size이기 때문. 따라서 또 다시 fastbin attack을 수행해야 한다. 아니면, 처음부터 71
size의 fast bin을 small bin chunk와 overlap 해서 추가적인 fastin attack을 수행하지 않도록 하는 방법도 있다.
Size : 96
짜리 chunk를 두 개 Allocation하고, 다시 두 개를 Free하여 생성된 fd
를 \_\_malloc\_hook
근처 mchunkptr로 덮어 쓴 다음 다시 두 번 Allocation해서 \_\_malloc\_hook
근처 mchunkptr에 chunk를 생성하고 여기에 one_gadget 주소를 Fill 한 다음[ Fill 할 때 align이 안된 곳에서 부터 쓰기 때문에 주의. ]
Allocation 아무 주소 하면 malloc()
이 호출되면서 hooking function이 동작해 one_gadget이 실행된다.