본문 바로가기

워게임/protostar

heap3

개고생했는데 일단 아직못풀었습니다.. 그냥풀이과정적어놨습니다 일단


(gdb) disas main


Dump of assembler code for function main:

0x08048889 <main+0>: push   ebp

0x0804888a <main+1>: mov    ebp,esp

0x0804888c <main+3>: and    esp,0xfffffff0

0x0804888f <main+6>: sub    esp,0x20


0x08048892 <main+9>: mov    DWORD PTR [esp],0x20

0x08048899 <main+16>: call   0x8048ff2 <malloc>



0x0804889e <main+21>: mov    DWORD PTR [esp+0x14],eax

0x080488a2 <main+25>: mov    DWORD PTR [esp],0x20

0x080488a9 <main+32>: call   0x8048ff2 <malloc>



0x080488ae <main+37>: mov    DWORD PTR [esp+0x18],eax

0x080488b2 <main+41>: mov    DWORD PTR [esp],0x20

0x080488b9 <main+48>: call   0x8048ff2 <malloc>



0x080488be <main+53>: mov    DWORD PTR [esp+0x1c],eax

0x080488c2 <main+57>: mov    eax,DWORD PTR [ebp+0xc]

0x080488c5 <main+60>: add    eax,0x4

0x080488c8 <main+63>: mov    eax,DWORD PTR [eax]

0x080488ca <main+65>: mov    DWORD PTR [esp+0x4],eax

0x080488ce <main+69>: mov    eax,DWORD PTR [esp+0x14]

0x080488d2 <main+73>: mov    DWORD PTR [esp],eax

0x080488d5 <main+76>: call   0x8048750 <strcpy@plt>



0x080488da <main+81>: mov    eax,DWORD PTR [ebp+0xc]

0x080488dd <main+84>: add    eax,0x8

0x080488e0 <main+87>: mov    eax,DWORD PTR [eax]

0x080488e2 <main+89>: mov    DWORD PTR [esp+0x4],eax

0x080488e6 <main+93>: mov    eax,DWORD PTR [esp+0x18]

0x080488ea <main+97>: mov    DWORD PTR [esp],eax

0x080488ed <main+100>: call   0x8048750 <strcpy@plt>


0x080488f2 <main+105>: mov    eax,DWORD PTR [ebp+0xc]

0x080488f5 <main+108>: add    eax,0xc

0x080488f8 <main+111>: mov    eax,DWORD PTR [eax]

0x080488fa <main+113>: mov    DWORD PTR [esp+0x4],eax

0x080488fe <main+117>: mov    eax,DWORD PTR [esp+0x1c]

0x08048902 <main+121>: mov    DWORD PTR [esp],eax

0x08048905 <main+124>: call   0x8048750 <strcpy@plt>


0x0804890a <main+129>: mov    eax,DWORD PTR [esp+0x1c]

0x0804890e <main+133>: mov    DWORD PTR [esp],eax

0x08048911 <main+136>: call   0x8049824 <free>


0x08048916 <main+141>: mov    eax,DWORD PTR [esp+0x18]

0x0804891a <main+145>: mov    DWORD PTR [esp],eax

0x0804891d <main+148>: call   0x8049824 <free>


0x08048922 <main+153>: mov    eax,DWORD PTR [esp+0x14]

0x08048926 <main+157>: mov    DWORD PTR [esp],eax

0x08048929 <main+160>: call   0x8049824 <free>


0x0804892e <main+165>: mov    DWORD PTR [esp],0x804ac27

0x08048935 <main+172>: call   0x8048790 <puts@plt>


0x0804893a <main+177>: leave  

0x0804893b <main+178>: ret    

End of assembler dump.


일단 우리의 목적은 winner()를 실행시키는거임.


winner()의 위치는


(gdb) p winner 

$1 = {void (void)} 0x8048864 <winner>


이렇게 나타나있음. 이제 이걸 어떻게 호출하느냐인데..


우선 코드를 분석해볼 필요가 있다.


void winner(){

printf("성공 블라블라");

}


int main(int argc, char *argv[]){

char *buf1, *buf2, *buf3;


buf1 = malloc(32);

buf2 = malloc(32);

buf2 = malloc(32);


strcpy(buf1, argv[1]);

strcpy(buf2, argv[2]);

strcpy(buf3, argv[3]);


free(buf1);

free(buf2);

free(buf3);


printf("다이너마이트 어쩌고");

}


어셈이 간단하니 금방 c코드로 바꿀 수 있었다.


아무튼 우리는 여기서 금방 찾을 수 있는 취약점이 있다.


길이 체크도 하지 않고 strcpy를 통해 동적 할당된 버퍼에 복사한다는 점이다.


하지만 이걸가지고 뭘 할 수 있을까?


우선..malloc의 과정을 살펴볼 필요가 있다.


메모리가 동적 할당될 경우 어떤 일이 발생할까?


http://expointer.tistory.com/38 에 간단하게 적어놨긴하지만 다시 한 번 짚어보자..



기본적으로 메모리 할당 함수들은 할당 단위가 보통 4kb(페이지 사이즈)이기 때문에 작은 메모리를 할당해주기 위해서 heap management가 필요하다.


리눅스에선 이러한 management를 위해 sbrk나 brk 시스템 콜을 사용해 크기를 확장하거나 변경할 수 있다.


아무튼 잘게 쪼개진 메모리 블록들은 최소 단위가 8byte이고(부가 정보를 저장하기 위한 데이터 사이즈) 실제 할당되는 사이즈까지 합하면 최소 16byte의 사이즈를 가지는데, 이말은 5바이트 할당해도 8바이트가 할당된다 그말이다. 부가 정보까지 합하면 8바이트 더해서 16바이트가 되겠지?

(실 예제로 예를 들자면 32byte를 할당했기 때문에 40byte가 할당된다)


여기서 이 '부가 정보'라고 칭하는 것은 이 블록이 사용 중인 블록인지, 얼마나 할당되었는지, 현재 이전 블록의 크기는 몇인지가 기록되어있다.


이 블록을 가리켜 'chunk'라고 부른다..



이 chunk라는건 구조를 가지고 있는데


메모리가 할당되면 [prev size] [size(+flag)] [data] 이런식으로 저장되었다가


free가 되면 [prev size] [size] [fd pointer] [bk pointer] [unused space] 식으로 바뀐다.


할당됫을때 size 뒤에 flag라는게 보일텐데, 저건 그냥 맨 마지막 bit가 0으로 되어있으면 사용되지 않는 chunk라고 봐도 된다.


#define PREV_INUSE       0x1

#define IS_MMAPPED       0x2

#define NON_MAIN_ARENA   0x4


#define SIZE_BITS        (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)

#define chunksize(p)     ((p)->size & ~(SIZE_BITS))


뭐 이렇게 생겼는데 자세한건 검색하시고.. free 프로세스상 인접한 chunk가 사용 중이 아닐 경우 해당 chunk와 병합을 수행하는데


이 때 중요하게 사용되는게 fd pointer랑 bk pointer이다(fd는 forward, bk는 backward pointer를 의미한다).


이 병합과정에서 unlink라는 매크로를 통해 free의 더블 링크드 리스트의 각 이전 이후의 포인터를 바꾼다.


#unlink (P, BK, FD){

BK = P -> bk;

FD = P-> fd;

FD-> bk = BK;

BK-> fd = FD;

}


이 과정을 통해 FD가 가리키는 BK를 P가 가리켰던 BK로, BK가 가리키는 FD를 P가 가리켰던 FD로 변환하게 된다.


그럼 만약, FD와 BK를 변조시킨 후, flag를 0으로 만들어서 사용하지 않는 chunk로 인식되게 하면 어떨까?



우선 `perl -e 'print "A"x32'` `perl -e 'print "B"x32'` `perl -e 'print "C"x32'`를 통해 버퍼를 구성하고 free를 통해 할당 해제를 하면 다음과 같은 모습을 볼 수 있다.


(gdb) x/12x 0x804c000

0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141

0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141

0x804c020: 0x41414141 0x41414141 0x00000000 0x00000029


(gdb) x/12x 0x804c028

0x804c028: 0x00000000 0x00000029 0x0804c050 0x42424242

0x804c038: 0x42424242 0x42424242 0x42424242 0x42424242

0x804c048: 0x42424242 0x42424242 0x00000000 0x00000029


(gdb) x/12x 0x804c050

0x804c050: 0x00000000 0x00000029 0x00000000 0x43434343

0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343

0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89


각각 buf1, buf2, buf3이 저장되는 위치이다. 각각 풀어서 설명하면 다음과 같다..


buf1

 prev_size : 0

 size,flag : 0x29

 FD Pointer : 0x804c028

 BK Pointer : ?

 

buf2

 prev_size : 0

 size,flag : 0x29

 FD Pointer : 0x804c050

 BK Pointer : ?


buf3

 prev_size : 0

 size,flag : 0x29

 FD Pointer : ?

 BK Pointer : ?


이정도?.. bk pointer랑 prev_size는 왜 설정이 안된걸까? 혹시나 하고 사이즈 변경을 시도해보았다.


`perl -e 'print "A"x32, "D"x4, "\x28"'` `perl -e 'print "B"x32'` `perl -e 'print "C"x32'`


(gdb) x/12x 0x804c000

0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141

0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141

0x804c020: 0x41414141 0x41414141 0x44444444 0x00000028

(gdb) x/12x 0x804c028

0x804c028: 0x44444444 0x00000028 0x42424242 0x42424242

0x804c038: 0x42424242 0x42424242 0x42424242 0x42424242

0x804c048: 0x42424242 0x42424242 0x00000000 0x00000029

(gdb) x/12x 0x804c050

0x804c050: 0x00000000 0x00000029 0x43434343 0x43434343

0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343

0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89


끙 무튼 삽질 진짜 오래해봤는데 애매모호한 결과만 나옴..


1. prev_size는 왜 null인가?

2. bk는 왜없는가?

3. 할당 해제를 해도 왜 flag는 변경되지않는가?


이 세가지 의문점이 드는데 내가 멍청한건지 이게 이상한건지 모르겠다 물론 해외 풀이가 있는걸 보면 전자의 경우겠지만..

하여튼 확실한건 인자를 통해 fake chunk를 구성해줘야하고 free 과정에서 변경된 사이즈를 잘못 참조하게 만들어서 fake chunk의 fd와 bk(공격자 의도대로 설정된)를 병합 시도한 청크의 fd랑 bk로 바꿔야하는데 잘 안되네

개념 이해를 잘못한건지 흠

http://coffeenix.net/data_repository/txt/Corezine-3-2.txt
http://studyfoss.egloos.com/5206220
http://www.hackerschool.org/HS_Boards/data/Lib_system/doublefree.txt
https://conceptofproof.wordpress.com/2013/11/19/protostar-heap3-walkthrough/
http://egloos.zum.com/minjang/v/1232908
http://www.hackerschool.org/HS_Boards/data/Lib_system/dfb_leon.txt
http://studyfoss.egloos.com/5206979
https://www.cs.uoregon.edu/groups/uosec/files/slides/HeapExploitationSlides.pdf
http://secwriteups.blogspot.kr/2014/12/protostar-heap-3.html
https://csg.utdallas.edu/2012/12/protostar-heap3-solution-writeup/

참고한 레퍼런스들...


'워게임 > protostar' 카테고리의 다른 글

format1  (0) 2015.11.03
format0  (0) 2015.11.03
heap2  (0) 2015.10.30
heap1  (0) 2015.10.30
heap0  (0) 2015.10.30