windows-0x05
0x05 GS
What is GS?
GS는 STACK Cookie, Canary로도 불리는 메모리 보호 기법입니다.
이 GS는 BOF를 방어하기 위해 나온 기법 중 하나로, 함수가 시작될 때 STACK에 4 byte를 넣어놓고 함수가 종료될 때 이 4 byte가 변조되었는지 확인합니다.
만약 이 STACK Cookie가 처음과 다르다면, 프로그램은 바로 종료되어 버립니다.
아래 그림은 STACK Cookie가 적용되고 검사하는 과정을 나타냅니다.
STACK BOF는 반드시 Buffer의 시작 주소부터 덮기 시작하여 RET를 변조하는 것이기 때문에 GS를 정확하게 똑같이 입력하지 않으면 BOF 공격을 성공할 수가 없죠.
STACK Cookie는 고정된 값으로 넣어줄 수도 있지만, 값을 랜덤하게 넣거나, 첫 바이트를 Null로 넣어주는 방식도 있습니다.
따라서, BOF 공격을 방어하기 위한 아주 효과적인 방법이라고 볼 수 있습니다.
그렇다면 이 메모리 기법은 어떻게 우회를 할 수 있을까요??
이 기법을 우회하기 위해서는 Window의 Error Handlering에 대한 지식이 필요합니다.
Structured Error Handling
구조적 예외처리(이하 SEH)는 윈도우 운영 체제 특성에 기반한 예외 처리 매커니즘입니다.
이 SEH는 Thread별로 독립적으로 설치되고 운영되는 특징이 있습니다.
쉽게 이야기를 해보자면..프로그램이 좀 더 안정적으로 돌아갈 수 있도록 에러가 발생했을 때, 처리를 담당하는 역할을 맡고 있다고 생각하시면 되겠습니다.
위 그림은 SEH의 구조와 동작을 표현한 내용입니다. 뭔가 좀 복잡해보이죠??
하나씩 설명해보도록 할게요.
프로그램이 실행되다가 에러가 발생하면 FS(=Segment Register)의 0번지의 값을 참조해서 TIB의 Exception_Register라는 값을 찾습니다.
여기에는 컴파일러가 미리 등록해놓은 에러 처리를 위한 Handler의 주소가 담겨있는데 이를 참조해서 Handler를 호출합니다.
만약 에러가 해결되지 않는다면 다음 Handler를 호출하는 방식으로 구현되어 있습니다.
만약 이 Handler가 발생한 에러를 위한 Handler가 맞다면 실행되고 아니면 *next SEH Record를 참조하여 다음 Handler를 실행하러 갑니다.
그런데 이 과정이 무한 반복이 되면 안되겠죠? 이 과정은 *next SEH Record의 값이 0xffffffff가 될 때까지만 진행이 되고
만약 0xffffffff가 된다면 Default Exception Handler를 호출하게 됩니다.
간단하쥬?
다음 그림을 통해 NSEH와 SEH의 구조를 살펴보겠습니다.
위 그림처럼 EXCEPTION_RECORD는 _next와 _handler로 구성되어 있습니다.
위에서 이미 설명했듯이 _next는 다음 EXCEPTION_RECORD의 위치를 담고 있습니다.
_handler는 실제로 에러 처리 과정을 실행할 함수이고, 구조는 아래와 같습니다.
위 함수 구조와 같이 에러 처리 함수는 4개의 인자를 필요로 하며, 함수 실행 시 실행에 필요한 인자들을 STACK에 구성합니다.
따라서 에러 처리 함수가 동작되면 STACK 구조는 아래처럼 구성됩니다.
이 중 우리가 공격에서 유용하게 사용할 값이 EstablierFrame입니다. EstablierFrame은 이전 Frame의 주소를 가지고 있습니다.
이게 왜 중요할까요?
다음에서 우회 기법을 설명하며 EstablierFrame 값이 왜 중요한지 같이 알아보겠습니다.
SEH Overwrite
우리는 지금까지 GS와 SEH의 개념을 살펴보았습니다.
STACK Cookie를 우호하는데 왜 SEH의 개념이 필요할까요??
GS가 적용되면 함수 시작 시, 특정 4Byte를 넣고 함수가 종료될 때 4Byte를 검사한다고 했습니다.
이를 우회하려면? 반드시 4Byte를 알아내서 동일하게 적어줘야겠죠?
그런데 이 STACK Cookie를 검사하기 전에 프로그램에 치명적인 에러가 발생한다면 어떻게 될까요?
윈도우는 이 에러를 먼저 처리하려고 하겠죠???
이 점을 이용하는 겁니다. 지금부터 이 점을 이용해서 STACK Cookie 검사 과정을 우회할 것 입니다.
어떻게 할 것이냐? 저는 STACK의 끝까지 더미 값을 넣어서 할당된 STACK 공간 밖에 값을 쓰도록 Payload를 구성할 겁니다.
그러면? 반드시 에러가 발생하겠죠??
에러가 발생하면 윈도우의 SEH 과정이 실행됩니다.
지금까지 이야기한 것을 STACK으로 표현하면 다음과 같습니다.
위 그림을 살펴보면 _next는 Jump to Shellcode, _handler는 & pop pop ret으로 되어 있죠?
그리고 아래 STACK Frame에 EstablierFrame도 빨간 색으로 표현되어 있네요.
먼저, EstablierFrame은 이전 Frame의 _next의 주소를 담고 있습니다.
_handler가 실행되면서 자체적으로 새로운 Frame을 생성하는데 이 함수의 인자 중 하나가 이전 Frame의 주소를 가지고 있으니까
다시 원래 Frame으로 돌아가서 Shellcode를 실행하도록 만들자는 것이죠.
그래서 _handler에는 & pop pop ret를 넣어줬습니다.
함수의 프롤로그 과정을 잘 떠올려보세요. 그럼 _handler가 실행될 때의 ESP는 그림에서 가장 아래에 있을 것이고
+8 위치에 EstablierFrame가 있죠? 그래서 & pop pop ret을 넣어주는 것입니다.
& pop pop ret이 실행되면?? _next로 오겠죠?
그럼 이제 남은 것은 _next에서 Shellcode가 얼마나 떨어져있는지 계산해서 jump code를 구성해주면 됩니다.
해당 그림에서는 바로 뒤에 Shellcode를 위치시켰습니다.
그래서 뛰어 넘을 거리는 6Byte!
4Byte가 아니냐구요? jump 0x06을 기계어로 바꾸면 06 eb가 됩니다. 2 byte짜리 명령어에요.
그래서 90 90 06 eb로 구성합니다. 그럼 90 90과 & pop pop ret을 뛰어넘어야 하니까 6byte 맞죠?
아래 그림은 Immunity Debugger를 이용해서 우회 과정을 살펴본 것입니다.
우회가 잘 되죠??
지금까지 GS에 대한 설명을 했습니다. 다음 연재에서는 Safe SEH 기법에 대해 설명하겠습니다.