blackbearwow 2022. 5. 13. 15:02

1. 문제를 보기 전에 원리를 살펴보자.

드림핵 Exploit Tech: Tcache Poisoning을 보면 같은 청크를 두번 해제한 후 재할당하면 그 청크는 할당된 청크이면서 헤제된 청크이므로 해당 청크의 fd(tcache의 next), bk(tcache의 key)를 조작할 수 있다는 것이다.

그렇다면, 이것을 조작할 수 있다면 왜 임의의 주소에 청크를 할당할 수 있다는 것일까? 드림핵에서는 자세히 설명하지 않아서 이해되지 않았다.

tcache로 예를 들어보자. tcache는 next부분이 0이어야 tcachebins list에 해제된 청크가 하나도 없다는 뜻이다(아마도). next부분이 0이면 리스트의 끝으로 본다. 반면에 next부분을 조작하여 임의의 주소를 넣으면 해당 주소+0x10주소의 값(x64일경우)을 읽어서, 해당 값이 0인지 아닌지를 판단하여 0이 아니라면 리스트에 추가된 것으로 간주하는 것이다. 그 말은 해당 주소+0x10의 값이 0일때까지 리스트로 간주한다. 아래 그림을 보며 이해하자.

code1.c
code1.c를 디버그한것

보다시피 tcache의 next에는 0이 들어있다.

Tcache Poisoning 워게임 중간부분 gdb.attach(p)를 하여 heap, bins를 본 결과

아래 워게임 중간 gdb를 실행하여 bins를 본다면 tcachebins에 연쇄적으로 list가 있는것을 볼 수 있다.

 

2. Tcache Poisoning 워게임

Double free bug 함께실습 문제이다.

소스코드

할당, 해제, 값 출력, 청크 값 변조 4가지를 할 수 있다.

free이후 chunk를 null로 바꾸지 않았기 때문에 취약점이 있다.

1. 청크 할당한다.

2. 청크 해제

3. tcache의 key부분 값을 변경하여 double free 감지를 우회한다.

4. 청크 또 해제 -> tcache에 같은 주소의 청크가 2개가 있는 상태가 된다.

5. 청크를 할당하는데 stdout의 주소를 데이터로 넣어, 다음 청크를 할당할 때 stdout에 써있는 주소에 청크를 할당하게 한다.

5번 이후 gdb.attach(p)를 이용해 bins를 분석한 화면

stdout의 주소를 넣어주면 tcachebins의 리스트에 연쇄적으로 청크들이 추가된것처럼 된다.

(libc-2.27.so이후의 버전에는 tcache청크를 count하는 변수가 생겨서 6번을 할 수 없다. 참고: https://dreamhack.io/forum/qna/1780)

6. 5번 이후에 아직 tcachebins에는 1개의 청크가 남아있는 상태이다. 해당 청크를 할당하면, 0x20672a0청크가 할당되고 값도 바뀌었지만, tcachebins의 리스트에는 0x601010(stdout@@GLIBC_2.2.5)부터 남아있는 상태가 된다.

6번 이후 gdb.attach(p)를 한 결과

7. 이후에 청크를 할당하면 0x601010(stdout@@GLIBC_2.2.5) 주소에 청크가 할당된다. 뒷자리는 해당 libc버전에 맞는 stdout심볼 적절한 아래 1바이트를 쓰면 된다.(libc마다 아래 1.5바이트씩인가 고정이기 때문에 버전에 맞게 잘 쓰자)

(libc-2.27.so이후의 버전에는 tcache청크를 count하는 변수가 생겨서 7번을 할 수 없다. 참고: https://dreamhack.io/forum/qna/1780)

8. 해당 주소값을 읽어 libc와 free_hook, one_gadget의 주소를 구한다.

9. 다른 크기의 청크를 할당해 tcachebins에 같은 청크를 리스트에 2개 넣는다. (1,2,3,4과정을 다른 크기로 하면 된다)

10. 청크를 할당하여 free_hook의 주소를 쓴다.

11. 청크를 할당하여 의미없는 값을 쓴다.

12. 청크를 할당하여 one_gadget의 주소를 쓴다.

(10, 11, 12는 위 5, 6, 7과 똑같은 과정에 해당한다)

13. free()를 호출하여 shell을 얻는다.

1~13 전 과정의 파이썬 스크립트