RET Oerwrite

함수는 종료될 때 Return address(RET)를 pop하여 다음 명령어로 분기됩니다. 하지만 RET Overwrite를 이용해 되돌아갈 RET를 다른 주소로 변경한다면 해당 주소로 이동해 명령을 수행합니다. 

[그림 1] RET Overwrite

'BBBB'가 입력될 자리에 쉘코드의 주소가 들어간다면 함수가 리턴된 뒤 쉘코드로 인해 쉘이 실행될 수 있습니다.

 

BOF는 버퍼의 크기를 고려하지 않거나 입력 값의 길이를 검증하지 않는 경우에 발생할 수 있습니다.

 

BOF(Buffer OverFlow)

메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점입니다.(위키피디아)

 

(BOF를 이해하려면 함수가 호출되고 리턴될 때 발생하는 함수 프롤로그 및 에필로그에 대해 알아야합니다.)

 

입력된 데이터를 저장하는 버퍼에 지정된 버퍼크기 이상의 데이터를 입력할 경우 버퍼를 넘어선 다른 영역에 데이터를 덮어 씌우게 됩니다(RET Overwrite). 이때 다른 영역이라함은 함수가 호출될 때와 프롤로그를 수행할 때 저장된 Return address와 Base Pointer입니다.

 

실습

[그림 2] bugfile1.c

BOF를 실습할 수 있는 코드를 작성해줍니다. 해당 코드에서 strcpy는 argv[1]의 길이가 얼마인지 검사하지 않고 크기가 256인 buf에 복사합니다. 그리고 buf를 출력합니다.

[그림 3] bugfile1 실행-1

 

256이하의 길이만큼 입력하였을 때는 정상적으로 동작하는 것을 확인할 수 있습니다.

 

[그림 4] bugfile1 실행-2

 

그림 4와 같이 256이 넘는 264 크기만큼을 입력 후 출력했을 때 Segmentation fault가 발생합니다.

 

[그림 5] gdb -q bugfile1
[그림 6] strcpy 후 bp 설정

 

gdb 통해 bugfile을 디스어셈블한 것을 그림 5를 통해 확인할 수 있습니다. strcpy 후 buf에 어떤 값이 들어가는지 확인하기 위해 bp를 걸어줍니다(그림6).

 

[그림 7] buf의 주소

그림7을 통해 buf의 주소가 0xbffff0df인 것을 확인할 수 있습니다.

 

 

 

[그림 8] 스크립트 작성 - 1

그림 7을 구한 buf의 주소에 쉘 코드를 집어 넣으나 쉘이 실행되지 않습니다.

그 이유는 gdb를 통해 바이너리를 실행한 경우와 그냥 실행했을 때와의 차이 때문입니다.

1. 파일명 길이

2. $_ 환경변수 존재 여부

3. 절대경로/상대경로 여부

4. $OLDPWD 환경변수 존재 여부

이러한 차이로 인해 환경변수의 길이가 달라지게 되는데 이때 지역 변수의 주소도 변경되게 됩니다.

그 차이는 16(0x10)배수 만큼 차이나게 됩니다.

 

[그림 9] buf 주소 출력문 추가

실제 buf의 주소를 알기 위해 buf 주소 출력문을 추가해줍니다.

[그림 10] buf 주소 재출력

 

[그림 11] 스크립트 작성 - 2

buf의 주소를 0xbffff218로 바꾸어 주었음에도 쉘이 실행되지 않습니다.

스크립트 작성 후 buf의 주소가 0xbffff118로 변경되었습니다.

 

[그림 12] 쉘 동작

다시 주소를 buf의 주소를 0xbffff118로 변경하여 스크립트를 실행시키니 쉘이 동작하는 것을 확인할 수 있었습니다.

 

 

BOF에 취약한 함수

char *gets(char *buffer);

표준 입력(stdin)에서 문자열을 입력받아 사용자가 전달한 buffer에 저장하는 함수

 

int scanf(const char *format[,argument]...);

표준 입력 스트림에서 형식이 지정된 데이터를 읽습니다.

 

char *strcpy(char *strDestination, const char *strSource);

strSource의 문자열을 strDestination으로 복사합니다.

 

char *strcat(char *strDestination, const char *strSource);
strSource의 문자열을 strDestination의 뒤로 추가합니다. 

 

위 함수들의 문제점은 표준 입력을 통해 입력받는 문자열이나 문자열을 복사하고 이어붙일 때 길이는 제한이 없기 때문에 buffer의 크기보다 큰 입력값을 buffer에 전달할 수 있습니다.

 

그렇기에 입력값의 길이 검사 또는 지정한 길이만큼의 데이터를 이어붙이거나 복사하는 아래와 같은 함수를 사용합니다.

 

int scanf_s(const char *format [, argument]...);

 

char *gets_s(char *buffer, size_t sizeInCharacters);

 

char *strncpy(char *strDest, const char *strSource, size_t count);

 

char *strncat(char *strDest, const char *strSource, size_t count);

 
 

FSB(Format StringBug)

포맷스트링과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 쉘 코드의 주소를 읽어 쉘을 획득하는 해킹 공격입니다.

 

포맷 스트링이란?

일반적으로 사용자로부터 입력을 받아들이거나 결과를 출력하기 위해 사용하는 형식(%d, %f, %s etc...)

 

printf(variable) <- 이것이 문제가 되는 이유는 printf 함수에서 사용할 각종 형식 지시자(%d, %s, %c... 등)를 포함한 Format String으로 인식하게 됩니다.

 

 

%x: 스택에서 4바이트를 읽어와 16진수로 출력한다.
%x로 메모리 구조를 추측할 수도 있습니다.

%n: 앞에서 출력된 바이트수를 ESP 레지스터가 가리키는 주소에 덮어 씀

 

실습

[그림 13] easyfsb.c

그림 13에서 간단한 FSB를 알아보겠습니다.

 

1. i는 1로 초기화 되어 있고 i의 주소 및 값을 출력합니다.

2. 후에 printf를 이용하여 "test%n"를 출력하고 %n 자리에 i의 주소를 배치합니다.

 

 

[그림 14] i값 변경

그림 14를 통해 i값이 4로 변경된 것을 확인할 수 있습니다. 그 이유는 test를 출력하고 난 뒤 %n이 붙었기 때문에 test의 길이가 4이기 때문입니다.

 

위처럼 발생하는 문제 때문에 정확하고 안전하게 포맷스트링 함수를 사용해야 합니다.

 

 

 

 

 

 

 

 

 

 

참고

 

https://ko.wikipedia.org/wiki/%EB%B2%84%ED%8D%BC_%EC%98%A4%EB%B2%84%ED%94%8C%EB%A1%9C

 

버퍼 오버플로 - 위키백과, 우리 모두의 백과사전

다른 뜻에 대해서는 오버플로 문서를 참고하십시오. 버퍼 오버플로(영어: buffer overflow) 또는 버퍼 오버런(buffer overrun)은 메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취

ko.wikipedia.org

 

https://d4m0n.tistory.com/14?category=796362 

 

Buffer Overflow 기초

Buffer Overflow란? Buffer Overflow는 C 언어나 C++에서 버퍼에 데이터를 입력받을 때 입력 값의 크기를 검증하지 않아 버퍼가 흘러넘쳐 다른 변수나 메모리를 덮어 씌우게 되는 버그이다. 이 취약점을

d4m0n.tistory.com

https://eip343.tistory.com/25

 

(해커스쿨 LOB) gdb에서 확인한 쉘코드 시작 주소와 실제 바이너리에서의 주소가 다른 이유

해커스쿨에서 제공하는 Lord of BOF (redhat 6.2) 환경에서 테스트를 진행하였다. gremlin.c에서 환경변수를 출력하도록 변형한 gremlin2.c의 소스코드는 아래와 같다. gremlin2.c /* The Lord of the BOF : The..

eip343.tistory.com

 

 

https://kali-km.tistory.com/entry/BOF%EC%97%90-%EC%B7%A8%EC%95%BD%ED%95%9C-%ED%95%A8%EC%88%98

 

BOF에 취약한 함수

취약한 함수란 취약한 함수란 컴파일되기 이전에 프로그래머로부터 작성된 코드 중 버퍼 오버 플로우나 포맷 스트링 공격 등에 노출될 수 있는 함수를 뜻한다. 이러한 함수의 사용은 오류를

kali-km.tistory.com

 

http://wiki.hash.kr/index.php/%ED%8F%AC%EB%A7%B7%EC%8A%A4%ED%8A%B8%EB%A7%81_%EA%B3%B5%EA%B2%A9

 

포맷스트링 공격 - 해시넷

포맷스트링 공격(Format String Attack)이란 포맷스트링과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 셸 코드의 주소를 읽어 셸을 획득하는 해킹 공격이다. 기존에 널리 사용되던

wiki.hash.kr

 

https://velog.io/@2rlo/pwntools-%EC%82%AC%EC%9A%A9%EB%B2%95

 

pwntools 사용법

Pwntools 설치, 실행, 기능 정리 / 2020 / SISS / System / Tool

velog.io

+ Recent posts