第 22 章,内核漏洞利用技术

首先编写具有漏洞的驱动 exploitme.sys,再展开内核漏洞利用思路和方法:

 /********************************************************************
created: 2010/12/06
filename: exploitme.c
author: shineast
purpose: Exploit me driver demo
*********************************************************************/
#include <ntddk.h> #define DEVICE_NAME L"\\Device\\ExploitMe"
#define DEVICE_LINK L"\\DosDevices\\ExploitMe"
#define FILE_DEVICE_EXPLOIT_ME 0x00008888
#define IOCTL_EXPLOIT_ME (ULONG)CTL_CODE(FILE_DEVICE_EXPLOIT_ME,0x800,METHOD_NEITHER,FILE_WRITE_ACCESS) //创建的设备对象指针
PDEVICE_OBJECT g_DeviceObject; /**********************************************************************
驱动派遣例程函数
输入:驱动对象的指针,Irp指针
输出:NTSTATUS类型的结果
**********************************************************************/
NTSTATUS DrvDispatch(IN PDEVICE_OBJECT driverObject,IN PIRP pIrp)
{
PIO_STACK_LOCATION pIrpStack; //当前的pIrp栈
PVOID Type3InputBuffer; //用户态输入地址
PVOID UserBuffer; //用户态输出地址
ULONG inputBufferLength; //输入缓冲区的大小
ULONG outputBufferLength; //输出缓冲区的大小
ULONG ioControlCode; //DeviceIoControl的控制号
PIO_STATUS_BLOCK IoStatus; //pIrp的IO状态指针
NTSTATUS ntStatus=STATUS_SUCCESS; //函数返回值 //获取数据
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
Type3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
UserBuffer = pIrp->UserBuffer;
inputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
IoStatus=&pIrp->IoStatus;
IoStatus->Status = STATUS_SUCCESS;// Assume success
IoStatus->Information = ;// Assume nothing returned //根据 ioControlCode 完成对应的任务
switch(ioControlCode)
{
case IOCTL_EXPLOIT_ME:
if ( inputBufferLength >= && outputBufferLength >= )
{
*(ULONG *)UserBuffer = *(ULONG *)Type3InputBuffer;
IoStatus->Information = sizeof(ULONG);
}
break;
} //返回
IoStatus->Status = ntStatus;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return ntStatus;
}
/**********************************************************************
驱动卸载函数
输入:驱动对象的指针
输出:无
**********************************************************************/
VOID DriverUnload( IN PDRIVER_OBJECT driverObject )
{
UNICODE_STRING symLinkName;
KdPrint(("DriverUnload: 88!\n"));
RtlInitUnicodeString(&symLinkName,DEVICE_LINK);
IoDeleteSymbolicLink(&symLinkName);
IoDeleteDevice( g_DeviceObject );
}
/*********************************************************************
驱动入口函数(相当于main函数)
输入:驱动对象的指针,服务程序对应的注册表路径
输出:NTSTATUS类型的结果
**********************************************************************/
NTSTATUS DriverEntry( IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath )
{
NTSTATUS ntStatus;
UNICODE_STRING devName;
UNICODE_STRING symLinkName;
int i=;
//打印一句调试信息
KdPrint(("DriverEntry: Exploit me driver demo!\n"));
//创建设备
RtlInitUnicodeString(&devName,DEVICE_NAME);
ntStatus = IoCreateDevice( driverObject,
,
&devName,
FILE_DEVICE_UNKNOWN,
, TRUE,
&g_DeviceObject );
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
//创建符号链接
RtlInitUnicodeString(&symLinkName,DEVICE_LINK);
ntStatus = IoCreateSymbolicLink( &symLinkName,&devName );
if (!NT_SUCCESS(ntStatus))
{
IoDeleteDevice( g_DeviceObject );
return ntStatus;
}
//设置该驱动对象的卸载函数
driverObject->DriverUnload = DriverUnload;
//设置该驱动对象的派遣例程函数
for (i = ; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
driverObject->MajorFunction[i] = DrvDispatch;
}
//返回成功结果
return STATUS_SUCCESS;
}

exploitme.sys 创建的设备名称为 \Device\ExploitMe,符号链接为 \DosDevices\ExploitMe。在 Ring3 可以通过设备名称 \\.\ExploitMe 打开设备得到设备句柄,进而使用 DeviceIoControl() 来调用驱动派遣例程,与驱动交互。

exploitme.sys 的派遣例程只处理了一个 IoControlCode,即 IOCTL_EXPLOIT_ME(0x8888A003),处理方式十分简单,没有使用 ProbeForRead() 和 ProbeForWrite() 来探测 IO 地址是否可读写:

        if ( inputBufferLength >=  && outputBufferLength >=  )
{
*(ULONG *)UserBuffer = *(ULONG *)Type3InputBuffer;
IoStatus->Information = sizeof(ULONG);
}

IOCTL_EXPLOIT_ME 这个 IoControlCode 指定的 Ring3/Ring0 内存访问为 METHOD_NEITHER 方式(最后两位为 0x03)。因此 Type3InputBuffer 表示 Ring3 的输入缓冲区指针,UserBuffer 表示 Ring3 的输出缓冲区指针,inputBufferLength 表示 Ring3 的输入缓冲区大小(字节数),ouputBufferLength 表示 Ring3 的输出缓冲区大小(字节数)。

对 IOCTL_EXPLOIT_ME 的处理,实际上就是将 Ring3 的输入输出缓冲区的第一个 ULONG 数据写入 Ring3 输出缓冲区的第一个 ULONG 数据中。输入、输出都是由 Ring3 程序来指定的,读写却是在 Ring0 完成的。因此 Ring3 可以将输出缓冲区地址指定为内核高端地址,相当于篡改内核中任意地址的数据为任意值。

很多驱动程序漏洞最终都可以归纳为此类模型,属于向任意地址写任意数据的内核漏洞。

利用思路

从公布的内核漏洞数量来看,远程任意代码执行漏洞已经很少见了,更多的是本地权限提升和 DOS 类漏洞。驱动程序编译器默认都开启了 GS 选项,直接利用缓冲区溢出比较困难,阻碍重重。因此更希望看到能篡改系统内核内存数据或执行 Ring0 Shellcode 的漏洞。能达到这个目的的漏洞主要有三种:任意地址写任意内容、固定地址写任意内容和任意地址写固定内容。其中任意地址写任意内容的内核漏洞必定能实现本地权限提升。

目前常见的内核漏洞利用方法主要有两种:篡改内核内存数据、执行 Ring0 Shellcode。

实际利用中,不推荐篡改内存数据,因为很多重要的内存内核数据都不可直接被改写,如果内存属性被标记为只读,并且 CR0 寄存器的 WP 位设置为 1,是不能直接写入该内存的。如果一定要篡改,需要在 Ring0 Shellcode 中,首先将 CR0 的 WP 位置 0,从而禁用内存保护以篡改数据,改完后再恢复 WP 位

第二各利用方法,是在 Ring0 中执行 Shellcode。Ring0 中有很多内核 API 函数,这些函数大多保存于一些表中,并且这个表也是内核导出的。例如 SSDT 表(System Service Dispatch Table)、HalDispatchTable 等。如果能修改这些表中的内核 API 函数地址为事先准备好的 Shellcode 存放的地址(本进程空间内存地址),然后在本进程中调用这个内核 API 函数,就能在 Ring0 权限下执行 Shellcode。需要注意的是,选用内核 API 时要选择不常被调用的函数。因为 Shellcode 保存在本进程空间的 Ring3 内存地址中,别的进程无法访问到。如果别的进程再调用篡改过的 API 函数,就会导致内存访问错误或内核崩溃,这是相当危险的。

接下来对 exploitme.sys 进行利用。利用思路为:首先在当前进程(exploit.exe)的 0x0 地址处申请内存,并存放好 Ring0 Shellcode,然后利用漏洞将 HalDispatchTable 中的 HalQuerySystemInformation() 的地址改写为 0x0,最后再调用该函数的上层封装函数 NtQueryIntervalProfile(),于是 Shellcode 会在 Ring0 执行。

首先看 HalDispatchTable(内核模块 hal.dll 导出的一个函数表):

 // extracted from haltypes.h

 typedef struct {
ULONG Version;
pHalQuerySystemInformation HalQuerySystemInformation;
pHalSetSystemInformation HalSetSystemInformation;
pHalQueryBusSlots HalQueryBusSlots;
ULONG Spare1;
pHalExamineMBR HalExamineMBR;
#if 1 /* Not present in WDK 7600 */
pHalIoAssignDriveLetters HalIoAssignDriveLetters;
#endif
pHalIoReadPartitionTable HalIoReadPartitionTable;
pHalIoSetPartitionInformation HalIoSetPartitionInformation;
pHalIoWritePartitionTable HalIoWritePartitionTable;
pHalHandlerForBus HalReferenceHandlerForBus;
pHalReferenceBusHandler HalReferenceBusHandler;
pHalReferenceBusHandler HalDereferenceBusHandler;
pHalInitPnpDriver HalInitPnpDriver;
pHalInitPowerManagement HalInitPowerManagement;
pHalGetDmaAdapter HalGetDmaAdapter;
pHalGetInterruptTranslator HalGetInterruptTranslator;
pHalStartMirroring HalStartMirroring;
pHalEndMirroring HalEndMirroring;
pHalMirrorPhysicalMemory HalMirrorPhysicalMemory;
pHalEndOfBoot HalEndOfBoot;
pHalMirrorVerify HalMirrorVerify;
pHalGetAcpiTable HalGetCachedAcpiTable;
pHalSetPciErrorHandlerCallback HalSetPciErrorHandlerCallback;
#if defined(_IA64_)
pHalGetErrorCapList HalGetErrorCapList;
pHalInjectError HalInjectError;
#endif
} HAL_DISPATCH, *PHAL_DISPATCH;

这个结构中第一个 ULONG 是版本号,第二个 ULONG 是需要利用的 HalQuerySystemInformation() 的地址。

如果要将 HalQuerySystemInformation() 篡改为 0,则需要构造如下的 DeviceIoControl 函数参数:

接下来看看 NtQueryIntervalProfile() 和 HalQuerySystemInformation() 的关系,这里参考 ReactOS 0.3.11 源码文件 \ntoskrnl\ex\profile.c 中的 NtQueryIntervalProfile():

 NTSTATUS
NTAPI
NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
OUT PULONG Interval)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
ULONG ReturnInterval;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE(); /* Check if we were called from user-mode */
if (PreviousMode != KernelMode)
{
/* Enter SEH Block */
_SEH2_TRY
{
/* Validate interval */
ProbeForWriteUlong(Interval);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
} /* Query the Interval */
ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource); /* Enter SEH block for return */
_SEH2_TRY
{
/* Return the data */
*Interval = ReturnInterval;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END; /* Return Success */
return Status;
}
 typedef enum _KPROFILE_SOURCE
{
ProfileTime,
ProfileAlignmentFixup,
ProfileTotalIssues,
ProfilePipelineDry,
ProfileLoadInstructions,
ProfilePipelineFrozen,
ProfileBranchInstructions,
ProfileTotalNonissues,
ProfileDcacheMisses,
ProfileIcacheMisses,
ProfileCacheMisses,
ProfileBranchMispredictions,
ProfileStoreInstructions,
ProfileFpInstructions,
ProfileIntegerInstructions,
Profile2Issue,
Profile3Issue,
Profile4Issue,
ProfileSpecialInstructions,
ProfileTotalCycles,
ProfileIcacheIssues,
ProfileDcacheAccesses,
ProfileMemoryBarrierCycles,
ProfileLoadLinkedIssues,
ProfileMaximum
} KPROFILE_SOURCE;
 ULONG NTAPI KeQueryIntervalProfile    (    IN KPROFILE_SOURCE     ProfileSource    )
Definition at line of file profobj.c. {
HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
ULONG ReturnLength, Interval;
NTSTATUS Status; /* Check what profile this is */
if (ProfileSource == ProfileTime)
{
/* Return the time interval */
Interval = KiProfileTimeInterval;
}
else if (ProfileSource == ProfileAlignmentFixup)
{
/* Return the alignment interval */
Interval = KiProfileAlignmentFixupInterval;
}
else
{
/* Request it from HAL */
ProfileSourceInformation.Source = ProfileSource;
Status = HalQuerySystemInformation(HalProfileSourceInformation,
sizeof(HAL_PROFILE_SOURCE_INFORMATION),
&ProfileSourceInformation,
&ReturnLength); /* Check if HAL handled it and supports this profile */
if (NT_SUCCESS(Status) && (ProfileSourceInformation.Supported))
{
/* Get the interval */
Interval = ProfileSourceInformation.Interval;
}
else
{
/* Unsupported or invalid source, fail */
Interval = ;
}
} /* Return the interval we got */
return Interval;
}

(以上代码摘自 ReactOS 官网)

可见,NtQueryIntervalProfeile() 中并没有做实际的操作,而是将第一个输入参数作为 KeQueryIntervalProfile() 的参数并获取结果。只要输入 KeQueryIntervalProfile() 中的第一个参数 ProfileSource 不等于 ProfileTime,也不等于 ProfileAlignmentFixup,就会调用到 HalQuerySystemInformation()。

Ring0 Shellcode:运行在 Ring0 环境下的 shellcode 可以为所欲为,因为已经具有了最高权限,可以完全控制带个系统。其常见的用法有:

* 提权到 SYSTEM:修改当前进程的 token 为 SYSTEM 进程的 token,这样当前进程便具备了 SYSTEM 权限。
* 恢复内核 Hook/Inline Hook:大部分安全软件通过 Hook 系统内核函数实现防御。可以通过恢复这些 Hook 来突破安全软件,甚至瓦解整个防御体系。
* 添加调用门/中断门/任务门/陷阱门:四门机制是出入 Ring0/Ring3 的重要手段。在系统中成功添加一个门,就能在后续代码中自由出入 Ring0、Ring3。

-----------------------------------------------------------------------------------

* 注-1 *
HAL,Hardware Abstraction Layer,硬件抽象层。HAL 高度依赖于机器,它必须与其所装入的系统完全匹配,Windows 的安装光盘上提供了许多种版本的 HAL。系统安装时,选择一种合适的 HAL 并以 hal.dll 为名复制到硬盘上的 %systemroot%\system32 下。之后的启动都使用该版本 HAL,删除该文件将导致系统无法启动。

尽管 HAL 已经相当高效,但对于多媒体应用而言,它的速度可能还不够快。为此,微软另外提供了 DirectX,用附加的过程增强了 HAL,并允许用户对硬件进行更直接的访问。

* 注-2 *

ReactOS 是开源免费的 Windows NT 系列(含 NT4.0/2000/XP/2003)克隆操作系统,保持了与 Windows 的系统级兼容性,旨在实现和 NT 与 XP 操作系统二进制下的完全应用程序和驱动设备的兼容性,通过使用类似构架和提供完全公共接口。

OD: Kernel Exploit - 1的更多相关文章

  1. OD: Kernel Exploit - 2 Programming

    本节接前方,对 exploitme.sys 进行利用. exploitme.sys 存在任意地址写任意内容的内核漏洞,现在采用执行 Ring0 Shellcode 的方式进行利用. 获取 HalDis ...

  2. OD: Heap Exploit : DWORD Shooting & Opcode Injecting

    堆块分配时的任意地址写入攻击原理 堆管理系统的三类操作:分配.释放.合并,归根到底都是对堆块链表的修改.如果能伪造链表结点的指针,那么在链表装卸的过程中就有可能获得读写内存的机会.堆溢出利用的精髓就是 ...

  3. OD: Shellcode / Exploit & DLL Trampolining

    看到第五章了. 标题中 Dll Tramplining(跳板)名字是从如下地址找到的,写的很好: http://en.wikipedia.org/wiki/Buffer_overflow#The_ju ...

  4. OD: Kernel Vulnerabilities Analyze

    内核漏洞大多出没于 ring3 到 ring0 的交互中.从 ring3 进入 ring0 的通道,以及操作系统提供的 API 都有可能存在漏洞.例如:驱动程序中 IoControl 的处理函数,SS ...

  5. OD: Kernel Vulnerabilities

    内核漏洞概述 内核漏洞的分类 运行在 Ring0 上的操作系统内核.设备驱动.第三方驱动能共享同一个虚拟地址空间,可以完全访问系统空间的所有内存,而不像用户态进程那样拥有独立私有的内存空间.由于内核程 ...

  6. How to exploit the x32 recvmmsg() kernel vulnerability CVE 2014-0038

    http://blog.includesecurity.com/2014/03/exploit-CVE-2014-0038-x32-recvmmsg-kernel-vulnerablity.html ...

  7. [轉]Exploit Linux Kernel Slub Overflow

    Exploit Linux Kernel Slub Overflow By wzt 一.前言 最近几年关于kernel exploit的研究比较热门,常见的内核提权漏洞大致可以分为几类: 空指针引用, ...

  8. An iOS zero-click radio proximity exploit odyssey

    NOTE: This specific issue was fixed before the launch of Privacy-Preserving Contact Tracing in iOS 1 ...

  9. 利用Android的UXSS漏洞完成一次XSS攻击

    黑客攻击的方式思路是先搜集信息,定位漏洞,然后针对不同的漏洞采用不同的方式来黑掉你.下面用metasploit模拟一次跨站脚本攻击(黑掉自己的手机). 1.搜集信息 msf > search a ...

随机推荐

  1. Leetcode: Reverse Integer 正确的思路下-要考虑代码简化

    题目: Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 Have ...

  2. div+css知识点

    前端书写规范: 1.所有书写均在英文半角状态下的小写: 2.id,class必须以字母开头: 3.所有标签必须闭合: 4.html标签用tab键缩进: 5.属性值必须带引号: 6.<!-- ht ...

  3. web.py simpletodo 例子

    一个很好的例子: 许多新手,特别是从 ASP/PHP/JSP 转过来的同学,经常问下面这几个问题: 所有东西都放在一个 code.py 中呀?我有好多东西该如何部署我的代码? 是不是 /index 对 ...

  4. [LeetCode#82]Remove Duplicates from Sorted Array II

    Problem: Follow up for "Remove Duplicates":What if duplicates are allowed at most twice? F ...

  5. find the most comfortable road

    XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Spee ...

  6. [转]是String,StringBuffer还是StringBuilder?

    原文链接. 相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,今天我在这里重新把这个概念给大家复习一下,顺便牵 ...

  7. 怒刷BZOJ记录(一)1001~1037

    我实在是太弱了...不滚粗只能刷BZOJ了...这里来记录每天刷了什么题吧. 2015-7-9 : 1001[BeiJing2006]狼抓兔子                 | 最大流练习(呃..其 ...

  8. 动态规划:NOI2013 快餐店

    Description 小 T打算在城市C开设一家外送快餐店.送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近 的地方. 快餐店的顾客分布 ...

  9. Unique Binary Search Trees——LeetCode

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  10. iOS 用CocoaPods做iOS程序的依赖管理

    文档更新说明 2012-12-02 v1.0 初稿 2014-01-08 v1.1 增加设置 ruby 淘宝源相关内容 2014-05-25 v2.0 增加国内 spec 镜像.使用私有 pod.po ...