CVE-2019-1458

样本分析

  1. 拿到样本后观察到这是个MFC程序,并且在资源段存在加密数据

CVE_2019-1458__1

  1. 在样本中找到解密这段shellcode的地方后将其dump出来,载入IDA分析

CVE_2019-1458__2

CVE_2019-1458__3

  1. 经过分析发现这段shellcode通过内存加载运行了位于其中的dll文件

    CVE_2019-1458__4

  2. 将其dump出来后经过分析发现其实现了傀儡进程了那一套,替换的进程就是CVE-2019-1458的32位提权exp

    CVE_2019-1458__5

漏洞分析

  1. exp中首先根据不同系统版本使用不同方法获取了NtUserMessageCall的地址、并初始化了一些内核结构成员的偏移数组

    CVE_2019-1458__6

    CVE_2019-1458__7

  2. 随后根据不同系统版本采用了不同的池喷射对象,我测试的环境Win7 x86 sp1采用的是Palette对象,因为在Win7中可以通过GdiSharedHandleTable来泄露GDI对象的内核地址,所以在exp中通过不断的申请一对Palette对象,直到两者内核地址之差为0x2000,通过后面分析可知,该样本利用这个有条件的任意写修改了位于内存上方的Palette2的numberOfEntries,然后通过越界写修改位于内存下方的Palette3的ptr_PALETTEENTRY 指针,从而将这个有条件的任意写漏洞转换为了任意地址读写

    signed int sub_401B40()
    {
      unsigned int v0; // edx
      HPALETTE palette1; // ebx
      HPALETTE palette2; // eax
      HPALETTE v3; // edi
      int v4; // edx
      unsigned int v5; // esi
      unsigned int v6; // eax
      bool v7; // cf
      bool v8; // zf
      HPALETTE v9; // eax
      int v10; // edx
      HPALETTE v11; // edi
      unsigned int v12; // eax
    
      v0 = Palettle_array_index;
      while ( v0 < 0xFFD )
      {
        stru_4081C0.palVersion = 0x300;
        stru_4081C0.palNumEntries = palette_number;
        memset(stru_4081C0.palPalEntry, 1, 8192u);
        palette1 = CreatePalette(&stru_4081C0);
        palette2 = CreatePalette(&stru_4081C0);
        v3 = palette2;
        if ( !palette1 || !palette2 )
          break;
        v4 = Palettle_array_index + 2;
        Palettle_array1[v4] = palette1;
        Palettle_array2[v4] = palette2;
        Palettle_array_index = v4;
        v5 = GetGdiKernelAddress(palette1);
        v6 = GetGdiKernelAddress(v3);
        v7 = v5 < v6;
        v8 = v5 == v6;
        if ( v5 >= v6 )
          goto LABEL_9;
        if ( v6 - v5 == 0x2000 )          // 保证两个Palettle对象地址足够接近
        {
          handle_Palettle_1 = palette1;
          kernelAddress_Palettle_1 = v5;
          handle_Palettle_2 = v3;
          kernelAddress_Palettle_2 = v6;
    LABEL_12:
          v9 = CreatePalette(&stru_4081C0);
          v10 = Palettle_array_index;
          v11 = v9;
          dword_4041A0[Palettle_array_index] = v9;
          Palettle_array_index = v10 + 1;
          v12 = GetGdiKernelAddress(v9);
          if ( v12 > kernelAddress_Palettle_2 && v12 - kernelAddress_Palettle_2 == 0x2000 )
          {
            kernelAddress_Palettle_3 = v12;
            handle_Palettle_3 = v11;
            sub_4017F0();
            return 1;
          }
          if ( v12 < kernelAddress_Palettle_1 && kernelAddress_Palettle_1 - v12 == 0x2000 )
          {
            handle_Palettle_3 = handle_Palettle_2;
            kernelAddress_Palettle_3 = kernelAddress_Palettle_2;
            handle_Palettle_2 = handle_Palettle_1;
            handle_Palettle_1 = v11;
            kernelAddress_Palettle_2 = kernelAddress_Palettle_1;
            kernelAddress_Palettle_1 = v12;
            sub_4017F0();
            return 1;
          }
        }
        else
        {
          v7 = v5 < v6;
          v8 = v5 == v6;
    LABEL_9:
          if ( !v7 && !v8 && v5 - v6 == 0x2000 )
          {
            handle_Palettle_2 = palette1;
            kernelAddress_Palettle_2 = v5;
            handle_Palettle_1 = v3;
            kernelAddress_Palettle_1 = v6;
            goto LABEL_12;
          }
        }
      }
    
  3. 随后创建了一个MySwitchWnd窗口并调用NtUserMessageCall,通过在IDA中分析win32k!NtUserMessageCall后发现这只是一个分发函数,其最终的处理函数由其参数Msg和dwType决定,通过windbg中调试发现在Msg = 0x1 dwType = 0xE0时,最终的调用流向了win32k!xxxWrapSwitchWndProc

    CVE_2019-1458__8

    CVE_2019-1458__9

  4. 通过windbg动态调试发现gpsi->mpFnid_serverCBWndProc[6]为0,而exp中创建的窗口的Class中将cbwndExtra设置为4,且新创建的窗口cbwndExtra的内容为0,因此通过此次NtUserMessageCall调用绕过了一下三个检查成功将hwnd->fnid设置为0x2A0(FNDI_SWITCH)

    CVE_2019-1458__10

  5. 再接着回来分析exp,发现其通过SetWindowLongWhwnd->cbwndExtra设置为Palette内核地址-0x40,随后创建了#32771窗口,这个窗口类在msdn中解释为用于任务切换窗口,通过分析发现创建这个窗口可以将gpsi->mpFnid_serverCBWndProc[6]设置为0xB4,因此可以绕过后面的检查,然后通过SetKeyboardState将ALT键的状态设置为press,最后再次调用了NtUserMessageCall

    image-20200425164031399

  6. 在第二次调用NtUserMessageCallMsg = 0x14并且此时hwnd->fnid = 0x2A0,因此调用在xxxSwitchWndProc中流向了xxxPaintSwitchWindow

    CVE_2019-1458__12

  7. xxxPaintSwitchWindow中调用Getpswi检查了a1->cbwndExtra + 0xB0 != gpsi->mpFnid_serverCBWndProc[6],由于在前面样本中已经创建了#32771窗口来将gpsi->mpFnid_serverCBWndProc[6]初始化为0xB4,因此Getpswi在检查通过后返回了hwnd->cbwndExtra的值,在简单的检查了当前ALT键是否被按下后调用了_GetClientRect漏洞函数。现在我们知道v1 = a1->cbwndExtra = kernelAddress_Palettle_2 - 0x40,因此_GetClientRect第二个参数v1 + 0x48之后其实是指向了 kernelAddress_Palettle_2 + 8

    image-20200425165229722

  8. _GetClientRect中我们观察到了有对第二个参数(也就是kernelAddress_Palettle_2 + 8)赋值的操作,并且在windbg中观察到lprc[3]就是kernelAddress_Palettle->numberOfEntries的地址,其被赋值为a1->rcClient.bottom,这个值是由我们创建窗口的大小和位置决定的,exp中这个值为0x7000。因此通过被扩大的kernelAddress_Palettle->numberOfEntries来达到越界修改的效果

    CVE_2019-1458__13

  9. kernelAddress_Palettle->numberOfEntries被增大为0x7000后,就可以通过GetPaletteEntriessetPaletteEntries构造读写原语

    CVE_2019-1458__14

    CVE_2019-1458__15

  10. 构造好读写原语后,exp中采用了读取system token并替换指定进程token的方式进行了提权

    CVE_2019-1458__16

参考链接

https://github.com/piotrflorczyk/cve-2019-1458_POC

样本文件:https://github.com/DreamoneOnly/CVE-2019-1458-malware/tree/master

原文地址:https://www.cnblogs.com/DreamoneOnly/p/12774145.html