7.1 헥스 에디터를 이용한 원초적 수정 방법
기존의 코드와 동일한 사이즈만 수정이 가능함
무의미한 코드가 있을 경우 해당 영역도 포함해서 수정이 가능.
7.1.1 한 바이트 버그 탐구하기
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
void
die(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
exit(1);
}
int
main(int argc, char *argv[])
{
FILE *f;
char *infile, *outfile;
unsigned char *key, *buf;
size_t i, j, n;
if(argc != 4)
die("Usage: %s <in file> <out file> <key>\n", argv[0]);
infile = argv[1];
outfile = argv[2];
key = (unsigned char*)argv[3];
f = fopen(infile, "rb");
if(!f) die("Failed to open file '%s'\n", infile);
fseek(f, 0, SEEK_END);
n = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(n);
if(!buf) die("Out of memory\n");
if(fread(buf, 1, n, f) != n)
die("Failed to read file '%s'\n", infile);
fclose(f);
j = 0;
for(i = 0; i < n-1; i++) { /* Oops! An off-by-one error! */
buf[i] ^= key[j];
j = (j+1) % strlen(key);
}
f = fopen(outfile, "wb");
if(!f) die("Failed to open file '%s'\n", outfile);
if(fwrite(buf, 1, n, f) != n)
die("Failed to write file '%s'\n", outfile);
fclose(f);
return 0;
}
C
복사
반복문에서 마지막 1바이트가 빠지게 됨.(index가 0 일 경우 -1과 등호 둘중 하나만 사용해야 함)
00000410: 0a06 4106 094f 1810 0806 034f 090b 0d17 ..A..O.....O....
00000420: 4648 4a11 462e 084d 4342 0e07 1209 060e FHJ.F..MCB......
00000430: 045b 5d65 6542 4114 0503 0011 045a 0046 .[]eeBA......Z.F
00000440: 5468 6b52 461d 0a16 1400 084f 5f59 6b0f ThkRF......O_Yk.
00000450: 6c0a
00000410: 6564 2074 6f20 7772 6974 6520 6669 6c65 ed to write file
00000420: 2027 2573 275c 6e22 2c20 6f75 7466 696c '%s'\n", outfil
00000430: 6529 3b0a 0a20 2066 636c 6f73 6528 6629 e);.. fclose(f)
00000440: 3b0a 0a20 2072 6574 7572 6e20 303b 0a7d ;.. return 0;.}
00000450: 0a0a
C
복사
마지막 바이트가 암호화가 안 된것을 확인할 수 있다.
7.1.2 한바이트 버그 수정하기
4007cb: 4d 8d 24 06 lea r12,[r14+rax*1]
4007cf: 74 2e je 4007ff <main+0xdf>
4007d1: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]
4007d8: 41 0f b6 04 17 movzx eax,BYTE PTR [r15+rdx*1]
4007dd: 48 8d 6a 01 lea rbp,[rdx+0x1]
4007e1: 4c 89 ff mov rdi,r15
4007e4: 30 03 xor BYTE PTR [rbx],al
4007e6: 48 83 c3 01 add rbx,0x1
4007ea: e8 a1 fe ff ff call 400690 <strlen@plt>
4007ef: 31 d2 xor edx,edx
4007f1: 48 89 c1 mov rcx,rax
4007f4: 48 89 e8 mov rax,rbp
4007f7: 48 f7 f1 div rcx
4007fa: 49 39 dc cmp r12,rbx
4007fd: 75 d9 jne 4007d8 <main+0xb8>
C
복사
r12(n)의 값을 기준으로 같지 않을 경우 점프를 수행하여 암호화를 진행함
jne(jmp not equal) 을 jae(jmp above or equal)로 변조하면 r12가 rbx보다 크거나 같을 경우 점프하기 때문에 jne과 비교해서 반복문을 한번 더 수행이 가능하다.
jne(0x75)를 jae(0x73)으로 변조 후 암호화하여 다시 확인해보면
00000410: 0a06 4106 094f 1810 0806 034f 090b 0d17 ..A..O.....O....
00000420: 4648 4a11 462e 084d 4342 0e07 1209 060e FHJ.F..MCB......
00000430: 045b 5d65 6542 4114 0503 0011 045a 0046 .[]eeBA......Z.F
00000440: 5468 6b52 461d 0a16 1400 084f 5f59 6b0f ThkRF......O_Yk.
00000450: 6c65
C
복사
정상적으로 마지막까지 진행 된 것을 볼 수 있다.
7.2 LD_PRELOAD를 사용해 공유 라이브러리 동작 변경하기
공유 라이브러리 함수의 동작을 변경하고자 하는 경우 LD_PRELOAD를 이용하여 더 수월하게 가능하다.
LD_PRELOAD는 동적 링커의 동작과 관련 있는 환경 변수의 이름이다.
해당 환경 변수에 라이브러리를 명시하면 해당 라이브러리가 최 우선적으로 로드되며, 함수 명이 같을 경우 오버라이드 하여 변조한 함수를 호출 가능하다.
윈도우에도 비슷한 기법이 존재하는데, 윈도우에서는 DLL을 로드하여 모든 실행파일에 해당 DLL의 메인을 실행하는 용도로 사용된다.
7.3.1 힙 오버플로우 취약점
아래 소스코드는 첫번째 인자만큼 메모리를 할당하며, 두번째 만큼 할당한 버퍼에 채운다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
char *buf;
unsigned long len;
if(argc != 3) {
printf("Usage: %s <len> <string>\n", argv[0]);
return 1;
}
len = strtoul(argv[1], NULL, 0);
printf("Allocating %lu bytes\n", len);
buf = malloc(len);
if(buf && len > 0) {
memset(buf, 0, len);
strcpy(buf, argv[2]);
printf("%s\n", buf);
free(buf);
}
return 0;
}
C
복사
그러나 할당을 작게하고 문자열을 길게 할 경우 strcpy에서 검증이 되지 않아 힙오버플로우가 발생한다.
binary@binary-VirtualBox:~/code/chapter7$ ./heapoverflow 13 "Hello World"
Allocating 13 bytes
Hello World
binary@binary-VirtualBox:~/code/chapter7$ ./heapoverflow 13 `perl -e 'print "A"x100'`
Allocating 13 bytes
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** Error in `./heapoverflow': free(): invalid next size (fast): 0x00000000014bd420 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f381d9327e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f381d93b37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f381d93f53c]
./heapoverflow[0x4007aa]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f381d8db830]
./heapoverflow[0x400609]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 2361724 /home/binary/code/chapter7/heapoverflow
00600000-00601000 r--p 00000000 08:01 2361724 /home/binary/code/chapter7/heapoverflow
00601000-00602000 rw-p 00001000 08:01 2361724 /home/binary/code/chapter7/heapoverflow
014bd000-014de000 rw-p 00000000 00:00 0 [heap]
7f3818000000-7f3818021000 rw-p 00000000 00:00 0
7f3818021000-7f381c000000 ---p 00000000 00:00 0
7f381d6a5000-7f381d6bb000 r-xp 00000000 08:01 2233513 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f381d6bb000-7f381d8ba000 ---p 00016000 08:01 2233513 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f381d8ba000-7f381d8bb000 rw-p 00015000 08:01 2233513 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f381d8bb000-7f381da7b000 r-xp 00000000 08:01 2228538 /lib/x86_64-linux-gnu/libc-2.23.so
7f381da7b000-7f381dc7b000 ---p 001c0000 08:01 2228538 /lib/x86_64-linux-gnu/libc-2.23.so
7f381dc7b000-7f381dc7f000 r--p 001c0000 08:01 2228538 /lib/x86_64-linux-gnu/libc-2.23.so
7f381dc7f000-7f381dc81000 rw-p 001c4000 08:01 2228538 /lib/x86_64-linux-gnu/libc-2.23.so
7f381dc81000-7f381dc85000 rw-p 00000000 00:00 0
7f381dc85000-7f381dcab000 r-xp 00000000 08:01 2228536 /lib/x86_64-linux-gnu/ld-2.23.so
7f381de8e000-7f381de91000 rw-p 00000000 00:00 0
7f381dea9000-7f381deaa000 rw-p 00000000 00:00 0
7f381deaa000-7f381deab000 r--p 00025000 08:01 2228536 /lib/x86_64-linux-gnu/ld-2.23.so
7f381deab000-7f381deac000 rw-p 00026000 08:01 2228536 /lib/x86_64-linux-gnu/ld-2.23.so
7f381deac000-7f381dead000 rw-p 00000000 00:00 0
7ffdf12bf000-7ffdf12e0000 rw-p 00000000 00:00 0 [stack]
7ffdf1382000-7ffdf1384000 r--p 00000000 00:00 0 [vvar]
7ffdf1384000-7ffdf1386000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted (core dumped)
binary@binary-VirtualBox:~/code/chapter7$
C
복사
#define _GNU_SOURCE //필수
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <dlfcn.h> // 함수 포인터 제공
void* (*orig_malloc)(size_t);
void (*orig_free)(void*);
char* (*orig_strcpy)(char*, const char*);
typedef struct {
uintptr_t addr;
size_t size;
} alloc_t;
#define MAX_ALLOCS 1024
alloc_t allocs[MAX_ALLOCS];
unsigned alloc_idx = 0;
void*
malloc(size_t s)
{
if(!orig_malloc) orig_malloc = dlsym(RTLD_NEXT, "malloc"); //다음번의 malloc 함수(원본함수) 획득.
void *ptr = orig_malloc(s);
if(ptr) {
allocs[alloc_idx].addr = (uintptr_t)ptr; // 원본 함수 호출결과(주소)
allocs[alloc_idx].size = s; // 사이즈
alloc_idx = (alloc_idx+1) % MAX_ALLOCS; // 맥스 사이즈 넘을 경우 다시 인덱스 초기화
}
return ptr;
}
void
free(void *p)
{
if(!orig_free) orig_free = dlsym(RTLD_NEXT, "free");
orig_free(p);
for(unsigned i = 0; i < MAX_ALLOCS; i++) {
if(allocs[i].addr == (uintptr_t)p) {
allocs[i].addr = 0;
allocs[i].size = 0;
break;
}
}
}
char*
strcpy(char *dst, const char *src)
{
if(!orig_strcpy) orig_strcpy = dlsym(RTLD_NEXT, "strcpy");
for(unsigned i = 0; i < MAX_ALLOCS; i++) {
if(allocs[i].addr == (uintptr_t)dst) {
if(allocs[i].size <= strlen(src)) {
printf("Bad idea! Aborting strcpy to prevent heap overflow\n");
exit(1);
}
break;
}
}
return orig_strcpy(dst, src);
}
C
복사
//컴파일
gcc -fPIC -c heapcheck.c
gcc -shared -o heapcheck.so heapcheck.o -lc -ldl
binary@binary-VirtualBox:~/code/chapter7$ ls
elfinject.c heapcheck.c heapoverflow.c hello-got.bin ls.entry xor_encrypt.c
encrypted heapcheck.o hello.bin hello-got.s ls.got xor_encrypt.fixed
encrypted.fixed heapcheck.so hello-ctor.bin hello.s ls.plt xor_encrypt.original
encrypted.original heapoverflow hello-ctor.s ls.ctor Makefile
//실행
binary@binary-VirtualBox:~/code/chapter7$ LD_PRELOAD=`pwd`/heapcheck.so ./heapoverflow 13 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Allocating 13 bytes
Bad idea! Aborting strcpy to prevent heap overflow
C
복사
7.3 코드 섹션 끼워 넣기
1.
Hex 수정으로 인한 바이너리 개조는 매우 제한적
2.
LD_PRELOAD를 이용한 방식은 동적 라이브러리만 가능함
7.3.1 ELF 섹션 끼워넣기: 전체적 맥락
새로운 섹션을 삽입하려면,
1.
섹션이 차지할 바이트 수 추가.
2.
섹션 헤더 추가
3.
프로그램 헤더 추가
프로그램 해더의 경우 ELF 파일 해더 바로 뒤에 존재하기 때문에 추가하려면 헤더 뒤로 데이터를 다 밀어야 하기 때문에 좀 더 편한 방법인
기존에 존재하는 섹션 중 쓰이지 않는 섹션을 수정하여 사용한다.
PT_NOTE 세그먼트 덮어쓰기
PT_NOTE 세그먼트는 바이너리에 대한 부가적인 정보를 포함한 섹션이다.
그림 7-2에서는 임의로 .note.ABI-tag 섹션을 변조하여 사용한다.
거기에 프로그램 헤더와 섹션 헤더에서 해당 섹션이 실행 가능하도록 변경한다.
ELF 엔트리 포인트로 전환하기
그림 7-2의 4번째 단계는 선택적이다.
.injected 섹션의 함수를 먼저 실행해야 할 경우에는 엔트리 포인트로 전환하여야 하지만,
기존 함수에서 .injected섹션으로 전환하는 코드를 추가하거나, 추가로 해당 섹션의 코드가 실행되는 트리거를 설정하여야 한다.
두가지 모두 하지 않았을 경우 삽입된 섹션은 실행되지 않는다.
7.3.2 elfinject를 사용해 ELF 섹션 삽입하기
binary@binary-VirtualBox:~/code/chapter7$ cp /bin/ls .
binary@binary-VirtualBox:~/code/chapter7$ make elfinject
gcc -o elfinject -O2 elfinject.c -lelf
binary@binary-VirtualBox:~/code/chapter7$ ./elfinject ls hello.bin ".injected" 0x800000 0
binary@binary-VirtualBox:~/code/chapter7$ ./ls
hello world!
elfinject encrypted.original heapoverflow hello-ctor.s ls ls.plt xor_encrypt.original
elfinject.c heapcheck.c heapoverflow.c hello-got.bin ls.ctor Makefile
encrypted heapcheck.o hello.bin hello-got.s ls.entry xor_encrypt.c
encrypted.fixed heapcheck.so hello-ctor.bin hello.s ls.got xor_encrypt.fixed
C
복사
binary@binary-VirtualBox:~/code/chapter7$ readelf --wide --headers ls
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x800e78
Start of program headers: 64 (bytes into file)
Start of section headers: 124728 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 28
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .init PROGBITS 00000000004022b8 0022b8 00001a 00 AX 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 0000c0 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400358 000358 000cd8 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000401030 001030 0005dc 00 A 0 0 1
[ 7] .gnu.version VERSYM 000000000040160c 00160c 000112 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000401720 001720 000070 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000401790 001790 0000a8 18 A 5 0 8
[10] .rela.plt RELA 0000000000401838 001838 000a80 18 AI 5 24 8
[11] .plt PROGBITS 00000000004022e0 0022e0 000710 10 AX 0 0 16
[12] .plt.got PROGBITS 00000000004029f0 0029f0 000008 00 AX 0 0 8
[13] .text PROGBITS 0000000000402a00 002a00 011259 00 AX 0 0 16
[14] .fini PROGBITS 0000000000413c5c 013c5c 000009 00 AX 0 0 4
[15] .rodata PROGBITS 0000000000413c80 013c80 006974 00 A 0 0 32
[16] .eh_frame_hdr PROGBITS 000000000041a5f4 01a5f4 000804 00 A 0 0 4
[17] .eh_frame PROGBITS 000000000041adf8 01adf8 002c6c 00 A 0 0 8
[18] .jcr PROGBITS 000000000061de10 01de10 000008 00 WA 0 0 8
[19] .init_array INIT_ARRAY 000000000061de00 01de00 000008 00 WA 0 0 8
[20] .fini_array FINI_ARRAY 000000000061de08 01de08 000008 00 WA 0 0 8
[21] .got PROGBITS 000000000061dff8 01dff8 000008 08 WA 0 0 8
[22] .dynamic DYNAMIC 000000000061de18 01de18 0001e0 10 WA 6 0 8
[23] .got.plt PROGBITS 000000000061e000 01e000 000398 08 WA 0 0 8
[24] .data PROGBITS 000000000061e3a0 01e3a0 000260 00 WA 0 0 32
[25] .gnu_debuglink PROGBITS 0000000000000000 01e600 000034 00 0 0 1
[26] .bss NOBITS 000000000061e600 01e600 000d68 00 WA 0 0 32
[27] .injected PROGBITS 0000000000800e78 01ee78 00003f 00 AX 0 0 16
[28] .shstrtab STRTAB 0000000000000000 01e634 000102 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x01da64 0x01da64 R E 0x200000
LOAD 0x01de00 0x000000000061de00 0x000000000061de00 0x000800 0x001568 RW 0x200000
DYNAMIC 0x01de18 0x000000000061de18 0x000000000061de18 0x0001e0 0x0001e0 RW 0x8
LOAD 0x01ee78 0x0000000000800e78 0x0000000000800e78 0x00003f 0x00003f R E 0x1000
GNU_EH_FRAME 0x01a5f4 0x000000000041a5f4 0x000000000041a5f4 0x000804 0x000804 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x01de00 0x000000000061de00 0x000000000061de00 0x000200 0x000200 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .init .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .jcr .init_array .fini_array .got .dynamic .got.plt .data .bss
04 .dynamic
05 .injected
06 .eh_frame_hdr
07
08 .jcr .init_array .fini_array .got .dynamic
binary@binary-VirtualBox:~/code/chapter7$
C
복사
7.4 삽입된 코드 호출하기
7.4.1 엔트리 포인트 개조
헥스 편집기를 통해 엔트리 포인트 자체를 직접 수정하는 방식을 사용하겠다.
해당 실습에 사용할 소스는 아래와 같다.
BITS 64 //64비트
SECTION .text
global main
main:
push rax ; save all clobbered registers // 함수 프롤로그
push rcx ; (rcx and r11 destroyed by kernel)
push rdx
push rsi
push rdi
push r11
mov rax,1 ; sys_write //syscall write 호출 변수
mov rdi,1 ; stdout
lea rsi,[rel $+hello-$] ; hello
mov rdx,[rel $+len-$] ; len
syscall
pop r11 //함수 에필로그
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
push 0x4049a0 ; jump to original entry point
ret
hello: db "hello world",33,10 //문자열
len : dd 13 // 길이
C
복사
해당 코드를 컴파일 하고 elfinject를 이용하여 삽입해준다
binary@binary-VirtualBox:~/code/chapter7$ ./elfinject ls.entry hello.bin ".injected" 0x800000 -1
binary@binary-VirtualBox:~/code/chapter7$ readelf -h ./ls.entry
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4049a0
Start of program headers: 64 (bytes into file)
Start of section headers: 124728 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 28
C
복사
확인 해 보면 엔트리포인트가 변경되지 않았다.
hexedit으로 직접 수정해준다.
00000000 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 02 00 3E 00 01 00 00 00 .ELF..............>.....
00000018 78 0E 80 00 00 00 00 00 40 00 00 00 00 00 00 00 38 E7 01 00 00 00 00 00 x.......@.......8.......
00000030 00 00 00 00 40 00 38 00 09 00 40 00 1D 00 1C 00 06 00 00 00 05 00 00 00 ....@.8...@.............
00000048 40 00 00 00 00 00 00 00 40 00 40 00 00 00 00 00 40 00 40 00 00 00 00 00 @.......@.@.....@.@.....
00000060 F8 01 00 00 00 00 00 00 F8 01 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ........................
00000078 03 00 00 00 04 00 00 00 38 02 00 00 00 00 00 00 38 02 40 00 00 00 00 00 ........8.......8.@.....
00000090 38 02 40 00 00 00 00 00 1C 00 00 00 00 00 00 00 1C 00 00 00 00 00 00 00 8.@.....................
000000A8 01 00 00 00 00 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 ........................
000000C0 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 64 DA 01 00 00 00 00 00 ..@.......@.....d.......
000000D8 64 DA 01 00 00 00 00 00 00 00 20 00 00 00 00 00 01 00 00 00 06 00 00 00 d......... .............
000000F0 00 DE 01 00 00 00 00 00 00 DE 61 00 00 00 00 00 00 DE 61 00 00 00 00 00 ..........a.......a.....
00000108 00 08 00 00 00 00 00 00 68 15 00 00 00 00 00 00 00 00 20 00 00 00 00 00 ........h......... .....
00000120 02 00 00 00 06 00 00 00 18 DE 01 00 00 00 00 00 18 DE 61 00 00 00 00 00 ..................a.....
binary@binary-VirtualBox:~/code/chapter7$ readelf -h ./ls.entry
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x800e78
Start of program headers: 64 (bytes into file)
Start of section headers: 124728 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 28
binary@binary-VirtualBox:~/code/chapter7$
C
복사
정상적으로 변조된 것을 확인할 수 있다.
binary@binary-VirtualBox:~/code/chapter7$ ./ls.entry
hello world!
elfinject encrypted.original heapoverflow hello-ctor.s ls ls.plt xor_encrypt.original
elfinject.c heapcheck.c heapoverflow.c hello-got.bin ls.ctor Makefile
encrypted heapcheck.o hello.bin hello-got.s ls.entry xor_encrypt.c
encrypted.fixed heapcheck.so hello-ctor.bin hello.s ls.got xor_encrypt.fixed
binary@binary-VirtualBox:~/code/chapter7$
C
복사
실행해보면 정상적으로 실행되는것을 볼 수 있다.
7.4.2 생성자와 소멸자 탈취하기
생성자와 소멸자의 주소값을 후킹하여 삽입한 바이너리가 호출되도록 변조하여 보자.
BITS 64
SECTION .text
global main
main:
push rax ; save all clobbered registers
push rcx ; (rcx and r11 destroyed by kernel)
push rdx
push rsi
push rdi
push r11
mov rax,1 ; sys_write
mov rdi,1 ; stdout
lea rsi,[rel $+hello-$] ; hello
mov rdx,[rel $+len-$] ; len
syscall
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
push 0x404a70 ; jump to original constructor
ret
hello: db "hello world",33,10
len : dd 13
C
복사
elfInject로 바이너리를 삽입한 후 확인해보자
binary@binary-VirtualBox:~/code/chapter7$ readelf --wide -S ls.ctor
There are 29 section headers, starting at offset 0x1e738:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .init PROGBITS 00000000004022b8 0022b8 00001a 00 AX 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 0000c0 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400358 000358 000cd8 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000401030 001030 0005dc 00 A 0 0 1
[ 7] .gnu.version VERSYM 000000000040160c 00160c 000112 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000401720 001720 000070 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000401790 001790 0000a8 18 A 5 0 8
[10] .rela.plt RELA 0000000000401838 001838 000a80 18 AI 5 24 8
[11] .plt PROGBITS 00000000004022e0 0022e0 000710 10 AX 0 0 16
[12] .plt.got PROGBITS 00000000004029f0 0029f0 000008 00 AX 0 0 8
[13] .text PROGBITS 0000000000402a00 002a00 011259 00 AX 0 0 16
[14] .fini PROGBITS 0000000000413c5c 013c5c 000009 00 AX 0 0 4
[15] .rodata PROGBITS 0000000000413c80 013c80 006974 00 A 0 0 32
[16] .eh_frame_hdr PROGBITS 000000000041a5f4 01a5f4 000804 00 A 0 0 4
[17] .eh_frame PROGBITS 000000000041adf8 01adf8 002c6c 00 A 0 0 8
[18] .jcr PROGBITS 000000000061de10 01de10 000008 00 WA 0 0 8
[19] .init_array INIT_ARRAY 000000000061de00 01de00 000008 00 WA 0 0 8
[20] .fini_array FINI_ARRAY 000000000061de08 01de08 000008 00 WA 0 0 8
[21] .got PROGBITS 000000000061dff8 01dff8 000008 08 WA 0 0 8
[22] .dynamic DYNAMIC 000000000061de18 01de18 0001e0 10 WA 6 0 8
[23] .got.plt PROGBITS 000000000061e000 01e000 000398 08 WA 0 0 8
[24] .data PROGBITS 000000000061e3a0 01e3a0 000260 00 WA 0 0 32
[25] .gnu_debuglink PROGBITS 0000000000000000 01e600 000034 00 0 0 1
[26] .bss NOBITS 000000000061e600 01e600 000d68 00 WA 0 0 32
[27] .injected PROGBITS 0000000000800e78 01ee78 00003f 00 AX 0 0 16
[28] .shstrtab STRTAB 0000000000000000 01e634 000102 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
binary@binary-VirtualBox:~/code/chapter7$ objdump ls.ctor -s --section=.init_array
ls.ctor: file format elf64-x86-64
Contents of section .init_array:
61de00 704a4000 00000000 pJ@.....
C
복사
해당 섹션을 확인해보면 생성자 함수의 포인터를 보여준다. 해당 값을 변조해주고 실행해보면
binary@binary-VirtualBox:~/code/chapter7$ ./ls.ctor
hello world!
elfinject encrypted.original heapoverflow hello-ctor.s ls ls.plt xor_encrypt.original
elfinject.c heapcheck.c heapoverflow.c hello-got.bin ls.ctor Makefile
encrypted heapcheck.o hello.bin hello-got.s ls.entry xor_encrypt.c
encrypted.fixed heapcheck.so hello-ctor.bin hello.s ls.got xor_encrypt.fixed
C
복사
아래와 같이 hello world! 가 호출된다.