• 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

  • 相关阅读:
    教程-Supports判断接口(Instance)是否支持
    Android实例-如何使用系统剪切板(XE8+小米2)
    Android实例-从照相机或图库获取照片(XE8+小米2)
    Android实例-TTabControl的使用(XE8+小米2)
    Android实例-闪光灯的控制(XE8+小米2)
    WCF小问题总汇
    wpf程序线程与UI内容交互
    xml解析原理一些想法
    XML序列化
    C#用正则表达式一键Unicode转UTF8(解决LitJson中文问题)
  • 原文地址:https://www.cnblogs.com/DreamoneOnly/p/12774145.html
Copyright © 2020-2023  润新知