버퍼 오버플로우가 일어날 수 있는 취약한 함수들 

Strcpy() =  정해진 곳에 지정된 내용을 복사해서 넣고 그 길이를 체크하지 않는다.

ex) strcpy( ( str ,argv[1]);
100이라는 배열을 선언햇지만 200을 집어 넣어도 무작정 집어넣게됨 

Strcat , sprintf , vsprintf ,gets , fsacnf , scanf, sprintf , sscanf, vfscanf, vsprintf,
vscanf , vsscanf , streadd, strecpy , strtrns 도 마찬가지로 이러한 취약점들이 존재함

Stack Overflow = 위에 게시해놓은 함수들에 의해서 문자열이 복사될 때 버퍼를 넘치게 하여 임의의 코드 (setuid가 담긴 쉘 코드라던가 시스템 함수라던가) 가 담긴 곳으로 ret에 주소를 넣어서 임의의 코드를 실행할 수 있도록 만들어서 공격한다 .



Return Into Libe  (RTL) 

함수의 ret를 덮어서 공유 라이브러리에 존재 하고있는 system() 함수를 가리키게 한뒤에 실행에 필요한 매개 변수를 전달하여 공격한다 
(메모리에 적재된 공유 라이브러리는 스택에 존재하는 것이 아니므로
Non-executable Stack을 우회하는 것이 가능하다)

RET를 메모리에 적재된 System()함수를 가리키게 하고 환경변수에 저장한 "\bin\sh"의 주소를 ebp+x(레드헷 6.2 기준 4 레드헷 8기준 8) 에 위치시킨뒤 공격한다 
|\x90*4|&System() | dummy4 or &exit() | &\bin\sh | \x90 | SFP | RET (레드헷6.2기준)
으로 페이로드를 짜서 공격하면 된다 .

 

Defeating Non-executable Stack 패치

리눅스에서 공유 라이브러리는PLT와 GOT를 이용한다 .
PLT - 프로그램이 호출하는 모든 함수를 나열하고 있는 테이블
GOT - 프로그램 실행 후 libc.so 내의 실제 함수 주소가 저장되는 곳
libc.so(표준 C라이브러리의 공유 객체 파일)
PTL는 실제 호출될 함수를 나타내는 값을 _dl_runime_resolve 함수의 인자로 넘기고 
_dl_runtime_resolve 함수는 전달된 인자 값을 사용하여 호출된 함수의 실제 주소를 구한 후 GOT에 저장한 뒤 호출된 함수로 점프한다.
이 후에 동일한 함수가 다시 호출되면 동적 링커는 GOT에 저장되어 있는 호출된 함수의 실제 주소로 바로 점프하게 된다.
공격하는 사람은 취약한 함수를 사용해서 RET를 strcpy()[취약한 함수] 함수의 PLT 주소로 변경한다.
PLT 주소에는 Null String이 포함되어 있지 않으므로 취약한 함수가 실행되는데 
이 함수가 환경변수에 저장된 shellcode를 데이터 세그먼트 영역에 복사한다 .
strcpy() 함수가 완료된 후 RET가 복사된 데이터 세그먼트 영역의 shellcode를 가리키면서 shellcode가 실행되게 한다 .


나 - strcpy()를 두 번 호출하여 GOT를 덮어쓰기
두 번째부터의 동일한 함수 호출 시에는 GOT에 저장된 주소로 점프하게 되므로 첫 번째 strcpy()실행 시 GOT에 system()의 주소가 쓰여지게 한다 .
그리고 두 번째로 strcpy()가 호출될 때는 GOT에 저장된 system()의 주소로 점프하게 되고 환경변수에 저장된 인자 값을 실행하게 된다 .

Stack Guard

버퍼 오버 플로우 방어 기술  
RET 앞에 Canary 값을 주입하고 에필로그 (leave , ret)시에 Canary 값이 변조되었는지의 여부를 확인하여 버퍼 오버플로우 공격을 탐지한다.
버퍼부터 RET까지 덮어쓰는 공격은 Canary 값을 덮어쓸 수 밖에 없으므로 버퍼 오버플로우가 발생했다는것을 알 수 있다.

Canary는 프로그램 시작 시간에 crt0 라이브러리를 이용하여 무작위로 생성된다
Null Canary , Terminator Canary , XOR Random Canary 등으로 변경되었다.
Null Canary , Terminator Canary는 Canary로 사용될 값에 Null 문자나 Terminator 문자가 들어가게 하여 strcpy() 같은 문자열 복사 함수가 실행될 때 해당 문자에 의해 실행이 종료되어 버리도록 한다. (터미네이터 = 문자열의 맨 마지막을 가리킨다
C 언어에서는 터미네이터로서 널(null) 문자가 사용됩니다)

XOR Random Canary
함수가 호출될 때 RET와 Random Canary를 XOR한 값을 메모리에 저장한 후 RET 앞에 위치시킨다(Canary) 이 후에 함수가 return될 때 메모리에 저장되어있던 Canary와 스택 상의 RET 값을 XOR 연산한 후 이 값을 스택상의 XOR이 된 Canary 값과 비교하여서 일치해야지 실행이된다.

Window 버퍼 오버플로우

소스가 공개되어 있지 않아서 리눅스와는 달리 비교적 안전하다고 생각되었던 Windows를 공격하는 방법은 리눅스에서의 버퍼 오버플로우 공격과 원리는 동일 하였고 단지 shell을 실행하기 위해서는 필요한API(응용 프로그램에서 사용할 수 있도록 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻함)를 가지고 있는 몇몇 dll 파일을 적재하는 코드가 포함되어야 한다는 차이가 있다.

Random Stack 

프로그램이 실행될 때마다 스택이 서로 다른 주소에 위치하도록 하여 버퍼의 주소를 알아내기 힘들게 한 것 스택을 비고정적으로 위치시키기 위해 /dev/urandom , alloca()를 써야 한다고 제안을 하였고 RTL같은 공격으로부터 방어를 하기 위해서 공유 라이브러리의 주소 또한 무작위로 할당할 것을 권하였다 .

Heap Overflow

Heap 메모리에서 오버플로우를 해서 공격한다 

HEAP 영역은 malloc()에 의해 할당되고 Heap 영역 외에도 static 지시자에 의해 할당되는 bss 영역에서도 동일하게 버퍼 오버플로우가 일어날 수 있다 
setjmp() 함수를 이용해 현재의 명령어 , 스택 포인터 , 다른 레지스터를 jmp_buf에 저장한 후 longjmp() 함수로 setjmp()에 의해 저장된 jmpbuf를 복구한 다음 jmpbuf를 덮어씀으로써 longjmp() 함수가 호출될 때 shellcode를 실행할 수 있다.

strlcpy() and strlcat()

strncpy() , strncat() 은 자동으로 Destination의 문자열을 Null String으로 마치게 해주지만 [ char *strncpy(char *dest, char *src , size_t maxlen) =  문자열의 정해진 길이만큼 src에서 dest로 복사한다 ] src 문자열의 길이가 dest 문자열의 길이보다 클 때 오버폴르오가 일어날 수 있다 


Frame Pointer Overwrite (SFP)

SFP의 1바이트 조작만으로 공격이 가능한 기법  SFP의 마지막 1바이트를 덮어써서 eip가 버퍼에 주입된 shellcode를 가리키는 주소가 되도록 조작한다.
leave의 명령어는 mov ebp,esp | pop ebp 이렇게 두 개로 구성이 되있다.
leave 명령어에 의해 ret이 실행되기 전 1바이트가 조작된 ebp(SFP)의 값이 esp에 들어가게 된다.  pop ebp를 수행하면서 esp가 4바이트가 증가하게 되고 eip(현재 실행되고 있는 프로그램의 실행코드가 저장된 메모리의 주소)
즉 eip는 esp+4에 저장된 값을RET으로 인식하여 버퍼에 주입된 shellcode를 실행하게 된다 


+ Recent posts