Tcache poisoning

exploit techstudy
avatar
2025.06.15
·
6 min read

libc > 2.25 이후부터 적용된 heap 관리 방법이며, 2.29부터는 패치 되었다고 합니다.


두번의 32 byte heap 영역 할당을 통해 아래와 같이 데이터가 들어있다고 가정하자.

gef➤ heap chunks 
Chunk(addr=0x602010, size=0x290, flags=PREV_INUSE) 
	[0x0000000000602010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................] 
Chunk(addr=0x6022a0, size=0x30, flags=PREV_INUSE) 
	[0x00000000006022a0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa] 
Chunk(addr=0x6022d0, size=0x30, flags=PREV_INUSE) 
	[0x00000000006022d0 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb] 
Chunk(addr=0x602300, size=0x20d10, flags=PREV_INUSE) 
	[0x0000000000602300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................] 
Chunk(addr=0x602300, size=0x20d10, flags=PREV_INUSE) ← top chunk 

gef➤ x/40gx 0x602010 
0x602010: 0x0000000000000000 0x0000000000000000 
... 
0x602290: 0x0000000000000000 0x0000000000000031 
0x6022a0: 0x6161616161616161 0x6161616161616161 
0x6022b0: 0x6161616161616161 0x0061616161616161 
0x6022c0: 0x0000000000000000 0x0000000000000031 
0x6022d0: 0x6262626262626262 0x6262626262626262 
0x6022e0: 0x6262626262626262 0x0062626262626262 
0x6022f0: 0x0000000000000000 0x0000000000020d11
  • 0x602010 : tcache

  • 0x6022a0 : 청크 1

  • 0x6022d0 : 청크 2

이후 가장 마지막 heap 영역을 free 하면 tcache에 fd 주소가 저장된다.

gef➤  heap chunks
Chunk(addr=0x602010, size=0x290, flags=PREV_INUSE)
    [0x0000000000602010     00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x6022a0, size=0x30, flags=PREV_INUSE)
    [0x00000000006022a0     61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61    aaaaaaaaaaaaaaaa]
Chunk(addr=0x6022d0, size=0x30, flags=PREV_INUSE)
    [0x00000000006022d0     02 06 00 00 00 00 00 00 10 20 60 00 00 00 00 00    ......... `.....]
Chunk(addr=0x602300, size=0x20d10, flags=PREV_INUSE)
    [0x0000000000602300     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602300, size=0x20d10, flags=PREV_INUSE)  ←  top chunk
gef➤  x/40gx 0x602010
0x602010:       0x0000000000010000      0x0000000000000000
...
0x602090:       0x0000000000000000      0x00000000006022d0
0x6020a0:       0x0000000000000000      0x0000000000000000
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6161616161616161      0x6161616161616161
0x6022b0:       0x6161616161616161      0x0061616161616161
0x6022c0:       0x0000000000000000      0x0000000000000031
0x6022d0:       0x0000000000000602      0x0000000000602010
0x6022e0:       0x6262626262626262      0x0062626262626262
0x6022f0:       0x0000000000000000      0x0000000000020d11

tcache의 첫 8byte에는 free된 횟수가 입력되어 있고, 이후에는 free 된 청크(0x6022d0)의 주소가 들어있습니다.

그리고 해제된 청크 bk에는 tcache의 시작 주소가 입력되어 있습니다.

만약 heap 영역을 다시 할당받고 데이터를 넣으려고 하면 0x6022d0이 tcache에 있기 때문에 이 청크부터 할당 될 것입니다.
즉, tcache는 다음 heap 영역 할당 시 사용될 청크 주소를 저장하는 곳입니다.


tcache에서는 free시 double free를 확인하기 위해 tcache 영역에 저장된 해제된 청크 주소와 bk 값을 비교 합니다.

만일 bk 값을 변조가 가능하다면 어떻게 될까요

아래와 같이 현재 bk 값인 0x602010에서 1byte만 변조하고

gef➤  x/40gx 0x602010
...
0x6022d0:       0x6363636363636363      0x000000000060200a
...

다시 free해보면 free가 가능한 것을 확인 할 수 있고, tcache 첫번째 8byte 내에 2가 입력되어 있는 것으로 청크 2개가 free된 것으로 인식하고 있는 것을 알 수 있습니다.

gef➤  x/40gx 0x602010
0x602010:       0x0000000000020000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
...
0x602080:       0x0000000000000000      0x0000000000000000
0x602090:       0x0000000000000000      0x00000000006022d0
0x6020a0:       0x0000000000000000      0x0000000000000000
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6161616161616161      0x6161616161616161
0x6022b0:       0x6161616161616161      0x0061616161616161
0x6022c0:       0x0000000000000000      0x0000000000000031
0x6022d0:       0x00000000006024d2      0x0000000000602010
0x6022e0:       0x6262626262626262      0x0062626262626262
0x6022f0:       0x0000000000000000      0x0000000000020d11

만약 최초 free 및 bk 값 변경 후 다시 청크를 할당받고 값을 넣으려하면 어떻게 될까요?

32byte 청크 할당 및 데이터를 넣은 후 free 한 뒤 상태는 아래와 같습니다.

gef➤  x/40gx 0x602010
0x602010:       0x0000000000010000      0x0000000000000000
...
0x602090:       0x0000000000000000      0x00000000006022a0
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x0000000000000602      0x0000000000602010
0x6022b0:       0x6161616161616161      0x0061616161616161
0x6022c0:       0x0000000000000000      0x0000000000020d41
...

이후 bk 영역의 값을 수정하고

0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6262626262626262      0x6363636363636363 #여기 8byte가 bk?
0x6022b0:       0x616161616161610a      0x0061616161616161
0x6022c0:       0x0000000000000000      0x0000000000020d41

다시 heap 할당을 해보면

gef➤  x/40gx 0x602010
0x602010:       0x0000000000000000      0x0000000000000000
...
0x602090:       0x0000000000000000      0x6262626262626460
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6464646464646464      0x6464646464646464
0x6022b0:       0x6464646464646464      0x0064646464646464
0x6022c0:       0x0000000000000000      0x0000000000020d41

tcache에 청크의 값이 일부 삽입된 것을 볼 수 있으며 이는 다음에 heap 영역이 할당될 주소를 가지고 있음을 의미합니다.


다만 위의 값에서는 tcache의 첫 8byte가 0이기에 tcache 내에 다음에 사용할 주소 값이 있어도 사용하지는 않습니다.

만일 여기서 tcache 첫 8byte에 값이 1이상으로 남아있다면 어떻게 될까요.

heap 할당 -> free -> bk 변조 -> free -> bk 변조 후 heap 모양은 아래와 같습니다.

gef➤  x/40gx 0x602010
0x602010:       0x0000000000020000      0x0000000000000000
...
0x602090:       0x0000000000000000      0x00000000006022a0
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6262626262626262      0x6262626262626262
0x6022b0:       0x6262626262626262      0x0062626262626262
0x6022c0:       0x0000000000000000      0x0000000000020d41

이후 heap 할당을 1회 하면 heap 모양은 아래와 같습니다.

gef➤  x/40gx 0x602010
0x602010:       0x0000000000010000      0x0000000000000000
...
0x602090:       0x0000000000000000      0x6262626262626460 #여긴가..?
...
0x602290:       0x0000000000000000      0x0000000000000031
0x6022a0:       0x6363636363636363      0x6363636363636363
0x6022b0:       0x6363636363636363      0x0063636363636363
0x6022c0:       0x0000000000000000      0x0000000000020d41

여기서 만약 heap 할당을 한번 더 하면, tcache에 값이 남아있기에 해당 주소에 값을 쓰게 될 것입니다.







- 컬렉션 아티클