1 아드레날린 POC
exploit-db에 올라온 exploit poc이다.
POC 내용을 보면 [BOF유발’A’*2140개 + SEH overwrite + 쉘코드]로 심플하게 구성돼있다.
POC 파이썬 스크립트를 실행하면 .wvx 파일이 생성된다.
아드레날린을 열고 .wvx파일을 드래그하면 계산기가 팝업된다.
왜 그런지, 실제로 어떻게 동작하게 되는 건지 원인으 분석해보자.
2 POC 실행 테스트
아무 BP없이 무작정 poc파일을 올려보았다. 앞서 POC 스크립트를 봐서 SEH 취약점이라는 것을 알고있기도 하지만, 쉘코드가 진행되지 않고 예외처리를 묻는 과정에서 멈춘다는 점에서 다시한번 SEH 취약점이구나를 짐작할 수 있다.
우측 스택을 보면 이미 수 많은 "AAAA"들로 가득차서 BOF가 진행된 상태다.
예외가 발생한 어셈블리 코드를 보니 [ECX-C]의 값과 0을 비교하는 CMP구문인데, 이 값을 참조할 수 없어서 예외가 발생하는 것.
SEH체인을 보면 역시나 오염되어 있는 상태를 확인할 수 있다. 이 주소에 BP를 걸어 놓고 해당 주소의 어셈블리 코드를 보자.
PPR이 위치한 것을 보아 전형적인 SEH 취약점 공략방식임을 알 수 있다.
exception handler 함수의 두번째 인자가 SEH체인의 시작주소를 가지고 있다는 점을 이용한 공략방식이다.
exception handler함수를 호출하여 들어오는 것이므로 들어온 직후 스택 최상단에는 RETRUN주소가 담기며, 다음으로 exception handler의 인자가 차례로 놓인다.
따라서 두번째 인자를 이용하기 위해서 POP POP RET이 있는 주소로 덮어쓴 것이다.
SEH 취약점에서 예외처리 함수로 들어온 직후 PPR이 있는 코드로 가도록 해놓은 이유에 대하여 except_handler 함수 인자를 참조하자.
PPR을 거쳐서 except_handler의 두번째 인자인 SEH구조체의 시작주소로 점프한 상태이다.
어셈블리 명령어를 보면 short jump가 위치해있는데, 이는 BOF에서 SEH구조체를 덮어씌울 때 아래에 있는 쉘코드로 점프하여 쉘코드를 실행시키기 위함이다.
기본적으로 스택상에서 SEH구조체는 [next_SEH, SE Handler] 순으로 구성되기에 이렇게 만든 것이다.
short jump로 점프하는 주소를 보면 BOF로 덮어씌워져있는 수많은 "AAAA" 이후에 오는 쉘코드임을 알 수 있다.
3 원인분석
이제 취약점이 발생한 원인에 대해 분석해보자. 스택 기반의 취약점이므로 크래시가 터진 그 근처에서 원인을 쉽게 찾을 수 있다.
다시 재실행하여 취약점이 트리거 되기 전인, 예외가 발생하여 SEH 구조체를 참조하기를 기다리는 상태로 돌아왔다. 먼저 이 상태에서 주변 정보를 살펴보자.
BOF가 진행되어 AAAA로 가득찬 스택상황을 확인하였음에도 스택 최상단 근처에는 RETRUN주소가 살아있음을 확인하였다. 이는 BOF가 먼저 발생한 이후 Call 명령어가 수행됐기 때문이다.
이런 경우는 정말 편하게 원인분석이 가능하다.
RETURN주소를 타고 한칸 위를 보면 크래시 유발함수를 호출한 Call 명령어가 보인다.
그리고 그 근방을 찾아보면 BOF를 유발하는 버퍼와 관련된 부분을 바로 찾을 수 있다.
ASCII "%s" 문자열이 있다는 점에서 format string 형식으로 버퍼에 값이 옮겨졌음을 추측할 수 있다. (실제로도 그렇다)
ida로 보면 함수명과 함께 조금 더 구체적으로 알 수 있다.
CStdioFile::ReadString 함수를 호출하여 특정 파일로부터 문자열을 읽어온다.
이어서 목적지가 될 버퍼를 0으로 초기화하는 코드가 나타나는데 그 크기를 보면 0x7FF(Dst Buffer 크기)임을 알 수 있다.
이후 sprintf 함수를 호출하며 Src에서 Dst로 %s형식으로 데이터를 옮긴다. 여기서 Src와 Dst의 크기를 검사하는 루틴이 없으므로 Src의 크기가 더 크더라도 무작정 덮어쓰므로 BOF가 발생하는 것이다. 원인분석이 정말 간단하게 끝났다.
앞서 ReadString의 인자는 다루지 않았는데 여기서 살펴보겠다.
ReadString을 호출할 때 전달한 인자는 1개로, 두번째 가상함수 형식과 동일하다.
nMax-1개의 문자까지 읽어올 수 있는데, Dst버퍼의 크기를 0x7FF로 고정을 시켜놨다는 점과 취약한 sprinf 함수를 사용함에도 그 앞에 크기를 비교하여 예외처리를 하는 구문이 없다는 점이 이같은 BOF를 유발시키게 된 것이다.
아드레날린 POC 취약점 분석은 여기서 마무리한다.