tcache
libc > 2.25 이후부터 적용된 heap 관리 방법이며, 2.29부터는 패치 되었다.
사실 아래 내용 다 필요 없이 같은 공간에 free가 두번 들어가며, free 시 앞에 사용한 heap 영역의 주소를 가져오기 때문에 tcache 영역에 임의의 주소를 덮어씌울 수 있다.
우선 두번의 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
이후 가장 마지막 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의 첫번째 8 byte 내에는 free된 횟수가 들어있고, 이후 free 된 heap 영역의 주소가 들어있다.
그리고 heap 영역에는 tcache의 시작 주소가 들어가 있다.
만일 heap 영역을 다시 확보하고 데이터를 넣으려고하면 0x6022d0이 tcache에 있기에 여기부터 할당되려고 할 것이다.
즉, tcache는 다음에 heap 영역 할당 시 사용될 주소를 저장한 곳이다.
tcache에서는 free 시 double free를 확인하기 위해
tcache 영역에 저장된 free heap 영역 주소와 bk 영역 값을 확인한다.
만일 bk 값이 변조 가능하다면 어떻게 될까.
즉, 아래와 같이 현재 bk 값인 0x602010에서 1 byte만 변조하고
gef➤ x/40gx 0x602010
...
0x6022d0: 0x6363636363636363 0x000000000060200a
...
다시 free 해보면 free가 가능한 것을 확인할 수 있고, tcache 첫번째 8 byte 내에 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 값 변경 후 다시 heap 영역 할당 및 값을 넣으려하면 어떻게될까?
32 byte heap 할당 및 데이터를 넣은 후 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
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 영역의 값이 일부 삽입된 것을 볼 수 있으며 이는 다음에 heap 영역이 할당될 주소를 가지고 있음을 이야기 한다.
다만 위의 값에서는 tcache 첫 8 byte가 0이기에 tcache 내에 다음에 사용할 주소 값이 있어도 사용하지는 않는다.
만일 여기서 tcache 첫 8 byte에 값이 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에 값이 남아있기에 해당 주소에 값을 쓰려할 것이다.
다만 로컬 환경의 libc 버전이 높아 double free를 check하는 부분이 추가되었으며 이에 대한 시연을 하기는 어려우니 다음기회에...
아래 예제를 참조하자.
'Tips & theory' 카테고리의 다른 글
함수의 offset은 왜 strings로 찾아지지 않는가? (0) | 2022.08.03 |
---|---|
pwntools - elf, symbol을 가져올때 (0) | 2022.08.03 |
보호 기법 및 공격 시나리오 요약 (0) | 2022.08.01 |
heap tcache & main_arena (0) | 2022.08.01 |
fsb (0) | 2022.07.30 |