首页
社区
课程
招聘
[原创]0day书中内核漏洞exploitme.sys的学习记录
发表于: 2021-10-31 13:45 32050

[原创]0day书中内核漏洞exploitme.sys的学习记录

2021-10-31 13:45
32050

本文是对0day安全这本书中,关于内核漏洞的入门的例子的学习分享。

下面这段是对作者所说的具有的漏洞的驱动代码,不过我对它进行了一些修改,可以更容易看懂

这段驱动代码有以下两个特点

没有指定设备的数据交互方式,此时用户层和这个驱动的交互就会是METHOD_NEITHER。而这种方式会对用户层传入的输入输出的地址不会进行处理,直接进行更改。

在对应的控制码的操作中,程序只是判断输入输出的长度是否大于等于4。并没有判断输入输出的地址是否是合法的,地址中的内容是否是可以随意更改的,直接就将输入地址中的内容赋值到了输出地址中。

接下来通过一个正常的示例来看看这段驱动的作用

在这段代码中,将合法的输入输出地址也就是dwInput和dwOutput传给了驱动。那么在驱动中,就会将dwInput中的内容赋值到dwOutput中。

可以看到,驱动成功的将输出地址中保存的内容赋值到输入地址中。但是由于没有对地址的合法性进行检查,所以如果可以知道保存了系统函数的地址,就可以直接修改这个地址中保存的数据。这样,当程序再次调用这个函数的时候,就会调用我们写入的地址。这个手法和IAT Hook的的原理是一样的,感兴趣的话可以看看这篇Win PE系列之导入表解析与IAT Hook技术

作者给出的例子是,修改HAL_DISPATCH结构体中的HalQuerySuystemInformation的入口地址,将它改为0地址。这样程序在调用这个函数的时候,就会调用0地址中保存的指令。而我们可以在0地址申请一段内存,并写入想要执行的指令,这样就达到了执行想要的指令的目的。

那么要完成上面的内容,就要以下三个步骤:

在0地址申请内存并写入要执行的指令

找到保存HalQuerySystemInformation函数地址的地址,并通过驱动将函数地址改为0地址

调用HalQuerySystemInformation函数

接下来将对这三个步骤进行一一讲解。

在这里,申请内存使用的内核API是ZwAllocateVirtualMemory,该函数的定义如下

由参数的含义可以知道,要申请0地址的内存是不可以通过直接将BaseAddress指定为0的方式来实现。因为你将它指定为0,那么就会有系统来决定分配内存的区域。想要在0地址分配内存,需要用到MEM_TOP_DOWN这个页类型,也就是第五个参数要包含MEM_TOP_DOWN这个页类型。该类型的含义是将从尽可能高得地址分配内存。当第二个参数指定为一个比较小得数,比如1或者4这种,而第五个参数又带有MEM_TOP_DOWN标志。那么根据页对齐,此时会向上对齐,返回得BaseAddress就会是0且RegionSize将会是两个页的大小。

要找到这个函数的保存地址,需要找到HalDispatchTable。该值是一个HAL_DISPATCH结构体遍历,首先看看HAL_DISPATCH结构体的定义

根据改结构体的定义,可以看到HalQuerySystemInformation函数就保存在HAL_DISPATCH结构体偏移为0x04的地址。而HalDispatchTable是从内核模块中导出的,所以要就需要找到内核模块在内存中的基地址,再根据偏移得到HalDispatchTable的VA。而要找到内核模块的基地址就需要用到ZwQuerySystemInformation这个API。该函数的定义如下

SystemInformation

SYSTEM_INFORMATION_CLASS,在文档中的部分内容如下

当该值指定的是SystemModuleInformation(11)的时候,SystemInformation返回的就是SYSTEM_MODULE_INFORMATION的指针。改结构体的定义如下

可以看到SYSTEM_MODULE_INFORMATION中保存了SYSTEM_MODULE_INFORMATION_ENTRY的数组。数组中的每一个元数都保存了一个模块的信息,其中的Base保存模块的加载地址,ImageName保存了模块的名称。而本次查询一共有多少个SYSTEM_MODULE_INFORMATION的数组则保存在Count中。

由于,一开始并不知道要申请多大的内存才可以保存这些数据。所以,需要调用两次函数,第一次调用的目的就是通过将第三个参数传入0来获得需要使用的内存大小。

查询到的内核模块中的第一个就是要使用的内核模块。这样就可以获得这个内核模块的名称,接下来就需要使用LdrLoadDll来将内核模块导入,该函数的定义如下

通过这个函数就可以将模块加载到内存中算出HalDispatchTable的偏移,在使用上面得到的ImageBase就可以计算机HalDispatchTable在内核中的具体位置,接下来就通过IO控制码发送消息给驱动完成修改。

要调用这个函数,只需要调用NtQueryIntervalProfile函数的时候,它的第一个参数传入的不等于ProfileTime和ProfileAlignmentFixup就好

首先要知道0xFFDFF000这个地址保存的是_KPCR,该结构体的定义如下

该结构体偏移0x120处保存的是_KPCB结构体,该结构体的部分定义如下

据此可以知道0xFFDFF124保存的是当前线程的_KTHREAD,而_KTHREAD又是_ETHREAD结构体中的第一个成员,所以0xFFDFF124保存的其实是当前_ETHREAD结构体。_ETHREAD结构体中保存的内容如下


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-5-19 11:44 被1900编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (5)
雪    币: 14484
活跃值: (17483)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
优秀,感谢分享
2021-10-31 17:19
0
雪    币: 22411
活跃值: (25361)
能力值: ( LV15,RANK:910 )
在线值:
发帖
回帖
粉丝
3
pureGavin 优秀,感谢分享
兄弟过奖了
2021-10-31 18:25
0
雪    币: 1095
活跃值: (655)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
兄弟,刚好我也在学这部分,提权那里好像并不是因为Token与SYSTEM进程一样,其所属用户就是SYSTEM,具体是什么原因我也不太清楚,不过我尝试将其他进程的Token复制成了SYSTEM进程的Token,结果显示进程所属用户还是Admin,其实这部分测试是否提权成功通过能否执行cli等特权指令就行了。这部分的资料确实比较不全,需要自己去探索。
2021-10-31 19:42
2
雪    币: 22411
活跃值: (25361)
能力值: ( LV15,RANK:910 )
在线值:
发帖
回帖
粉丝
5
zzzzzssssmmm 兄弟,刚好我也在学这部分,提权那里好像并不是因为Token与SYSTEM进程一样,其所属用户就是SYSTEM,具体是什么原因我也不太清楚,不过我尝试将其他进程的Token复制成了SYSTEM进程的To ...
雀实,作者没写的很详细
2021-10-31 20:03
0
雪    币: 221
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
win10下就不行了
2023-4-25 10:58
0
游客
登录 | 注册 方可回帖
返回
//