tcache_stashing_unlink_attack
요약
- calloc의 취약점?을 이용하여 공격
- calloc은 할당했을때 tcache bin에서 chunk를 가져가지 않는다.
- calloc은 여러 bin들을 탐색하고, 대응하는 size의 bin이 tcache bin에 비어있지 않다면 이러한 bin을 사용
0x90 크기의 청크를 9개를 할당
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Used None None
0x405330 0x0 0xa0 Used None None
0x4053d0 0x0 0xa0 Used None None
0x405470 0x0 0xa0 Used None None
0x405510 0x0 0xa0 Used None None
0x4055b0 0x0 0xa0 Used None None
0x405650 0x0 0xa0 Used None None
0x4056f0 0x0 0xa0 Used None None
0x405790 0x0 0xa0 Used None None
index 3~8까지 free, 총 6개의 tcache bin에 chunk가 들어감을 확인하였음
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Used None None
0x405330 0x0 0xa0 Used None None
0x4053d0 0x0 0xa0 Used None None
0x405470 0x0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
ef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=6 ← Chunk(addr=0x4057a0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=PREV_INUSE)
다음 index 1의 chunk를 해제 -> 총 tcache bin은 7개로 꽉 차게 됨
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Used None None
0x405330 0x0 0xa0 Freed 0x4057a0 None
0x4053d0 0x0 0xa0 Used None None
0x405470 0x0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x405830 (size : 0x207d0)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0xa0) tcache_entry[8](7): 0x405340 --> 0x4057a0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
index 0의 chunk를 해제 -> tcache bin은 꽉 차고, 크기는 0x90(144)이기 때문에 unsorted bin에 들어가게 됨
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x405830 (size : 0x207d0)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x405290 (size : 0xa0)
(0xa0) tcache_entry[8](7): 0x405340 --> 0x4057a0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Freed 0x7ffff7fa9be0 0x7ffff7fa9be0
0x405330 0xa0 0xa0 Freed 0x4057a0 None
0x4053d0 0x0 0xa0 Used None None
0x405470 0x0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
index 2 해제, index 0과 2가 서로 가리킴
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Freed 0x7ffff7fa9be0 0x4053d0
0x405330 0xa0 0xa0 Freed 0x4057a0 None
0x4053d0 0x0 0xa0 Freed 0x405290 0x7ffff7fa9be0
0x405470 0xa0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x405830 (size : 0x207d0)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x4053d0 (size : 0xa0) <--> 0x405290 (size : 0xa0)
(0xa0) tcache_entry[8](7): 0x405340 --> 0x4057a0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
아까 처음에 할당했던 0x90보다 큰 size인 0xa0을 할당 -> small bin에 들어가게 됨
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Freed 0x7ffff7fa9c70 0x4053d0
0x405330 0xa0 0xa0 Freed 0x4057a0 None
0x4053d0 0x0 0xa0 Freed 0x405290 0x7ffff7fa9c70
0x405470 0xa0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
0x405830 0x0 0xb0 Used None None
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 <--> 0x405290
(0xa0) tcache_entry[8](7): 0x405340 --> 0x4057a0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
0x90사이즈의 청크를 재할당, tcache_entry의 번호가 하나 줄어듬
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 <--> 0x405290
(0xa0) tcache_entry[8](6): 0x4057a0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
gef➤ parseheap
addr prev size status fd bk
0x405000 0x0 0x290 Used None None
0x405290 0x0 0xa0 Freed 0x7ffff7fa9c70 0x4053d0
0x405330 0xa0 0xa0 Freed 0x4057a0 None
0x4053d0 0x0 0xa0 Freed 0x405290 0x7ffff7fa9c70
0x405470 0xa0 0xa0 Freed 0x0 None
0x405510 0x0 0xa0 Freed 0x405480 None
0x4055b0 0x0 0xa0 Freed 0x405520 None
0x405650 0x0 0xa0 Freed 0x4055c0 None
0x4056f0 0x0 0xa0 Freed 0x405660 None
0x405790 0x0 0xa0 Freed 0x405700 None
0x405830 0x0 0xb0 Used None None
gef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=6 ← Chunk(addr=0x4057a0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=! PREV_INUSE)
─────────────────────────────────────────── Fastbins for arena 0x7ffff7fa9b80 ───────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────────────────────────────────────── Unsorted Bin for arena 'main_arena' ──────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
─────────────────────────────────────────── Small Bins for arena 'main_arena' ───────────────────────────────────────────
[+] small_bins[9]: fw=0x4053d0, bk=0x405290
→ Chunk(addr=0x4053e0, size=0xa0, flags=PREV_INUSE) → Chunk(addr=0x4052a0, size=0xa0, flags=PREV_INUSE)
한번 더 0x90사이즈의 chunk 할당 -> 총 5개의 tcache bin과 2개의 smallbin이 존재
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 <--> 0x405290
(0xa0) tcache_entry[8](5): 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
gef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=5 ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=! PREV_INUSE)
─────────────────────────────────────────── Fastbins for arena 0x7ffff7fa9b80 ───────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────────────────────────────────────── Unsorted Bin for arena 'main_arena' ──────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
─────────────────────────────────────────── Small Bins for arena 'main_arena' ───────────────────────────────────────────
[+] small_bins[9]: fw=0x4053d0, bk=0x405290
→ Chunk(addr=0x4053e0, size=0xa0, flags=PREV_INUSE) → Chunk(addr=0x4052a0, size=0xa0, flags=PREV_INUSE)
[+] Found 2 chunks in 1 small non-empty bins.
victem->bk의 포인터 주소를 fake_chunk의 주소로 덮는다
gef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=5 ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=! PREV_INUSE)
─────────────────────────────────────────── Fastbins for arena 0x7ffff7fa9b80 ───────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────────────────────────────────────── Unsorted Bin for arena 'main_arena' ──────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
─────────────────────────────────────────── Small Bins for arena 'main_arena' ───────────────────────────────────────────
[+] small_bins[9]: fw=0x4053d0, bk=0x405290
→ Chunk(addr=0x4053e0, size=0xa0, flags=PREV_INUSE) → Chunk(addr=0x4052a0, size=0xa0, flags=PREV_INUSE)
[+] Found 2 chunks in 1 small non-empty bins.
─────────────────────────────────────────── Large Bins for arena 'main_arena' ───────────────────────────────────────────
[+] Found 0 chunks in 0 large non-empty bins.
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 (doubly linked list corruption 0x4053d0 != 0x0 and 0x4053d0 is broken)
(0xa0) tcache_entry[8](5): 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
그리고 Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 (doubly linked list corruption 0x4053d0 != 0x7 and 0x4053d0 is broken)
(0xa0) tcache_entry[8](7): 0x7fffffffdee0 --> 0x4053e0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
gef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=7 ← Chunk(addr=0x7fffffffdee0, size=0x0, flags=! PREV_INUSE) ← Chunk(addr=0x4053e0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=PREV_INUSE)
─────────────────────────────────────────── Fastbins for arena 0x7ffff7fa9b80 ───────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────────────────────────────────────── Unsorted Bin for arena 'main_arena' ──────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
─────────────────────────────────────────── Small Bins for arena 'main_arena' ───────────────────────────────────────────
[+] small_bins[9]: fw=0x4053d0, bk=0x7fffffffdee0
[!] Command 'heap bins small' failed to execute properly, reason: Cannot access memory at address 0x8
Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\n\n",(void*)stack_var[2],(void*)stack_var[4]
gef➤ heap bins
──────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────
Tcachebins[idx=8, size=0xa0] count=6 ← Chunk(addr=0x4053e0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405700, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405660, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x4055c0, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405520, size=0xa0, flags=PREV_INUSE) ← Chunk(addr=0x405480, size=0xa0, flags=PREV_INUSE)
─────────────────────────────────────────── Fastbins for arena 0x7ffff7fa9b80 ───────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────────────────────────────────────── Unsorted Bin for arena 'main_arena' ──────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
─────────────────────────────────────────── Small Bins for arena 'main_arena' ───────────────────────────────────────────
[+] small_bins[9]: fw=0x4053d0, bk=0x7fffffffdee0
[!] Command 'heap bins small' failed to execute properly, reason: Cannot access memory at address 0x8
─────────────────────────────────────────── Large Bins for arena 'main_arena' ───────────────────────────────────────────
[+] Found 0 chunks in 0 large non-empty bins.
gef➤ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x4058e0 (size : 0x20720)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x0a0) smallbin[ 8]: 0x4053d0 (doubly linked list corruption 0x4053d0 != 0x6 and 0x4053d0 is broken)
(0xa0) tcache_entry[8](6): 0x4053e0 --> 0x405700 --> 0x405660 --> 0x4055c0 --> 0x405520 --> 0x405480
printf("As you can see, next malloc(0x90) will return the region our fake chunk: %p\n",(void*)target);
As you can see, next malloc(0x90) will return the region our fake chunk: 0x7fffffffdee0