利用手法
此文主要是梳理最近windows上cve多数人比较常用的几个手法,肯定有所遗漏但涵盖主流常见手法用以学习。
漏洞的品相也决定了漏洞的利用手法的多样,像比较直接的可以进行任意地址读写的操作,就可以通过修改Token来实现提权,这就涉及到了windows中令牌的这一概念,windows进程所关联的用户访问权限等都是由令牌去进行校验的,也是常说的token概念。而在EPROCESS 里有个token结构,是进程的权限令牌,如果只要把system进程的token 替换到我们需要提权进程的eprocess里,则就获得system权限。但是这个操作需要Ring0的权限。
我们可以通过内核调试 dt nt!_EPROCESS 命令直接查看其结构

可以看到在Token的值存放在eprocess 偏移为 0x360 的地方,不同windows版本其偏移不相同,在其后边发现token是一个_EX_FAST_REF结构,是因为在windows上16字节的边界上需要将内核数据结构对齐到内存中所以指向token或其他任何内核对象的指针最低的4个位永远都是0如下图所示。



其次还有大多数漏洞利用都是为了铺垫一个教好的内存布局从而达到可以修改PreviousMode字段的作用。
PreviousMode :用户态程序调用系统的 Nt 或 Zw 时,系统调用机制会将调用线程捕获到内核模式。 为了标识参数源自用户模式,系统调用的处理程序将调用方线程对象中的 PreviousMode 字段设置为 UserMode。
而内核态程序调用系统例程并将参数值传递给来自内核态的例程,当前线程对象中的 PreviousMode 字段应为 KernelMode。 系统会检查调用线程的 PreviousMode 字段。这样通过参数就可以知道是来自用户态还是内核态了。
所以当PreviousMode 设置为 1 时,来自用户空间的NT 或 Zw 版本函数调用将其中进行地址验证。在这种情况下,对内核内存的任意写将失败,因为此时PreviousMode是用户态的状态。当 PreviousMode 设置为 0 (此时是内核态) 时,将跳过地址验证写入任意内核内存地址,从而完成提权。比如CVE-2023-36802

Scoop the Windows 10 pool!
再来谈一下Scoop the Windows 10 pool!,这篇很多知识是为了铺垫方便理解布局所以内容比较繁琐。
win10最近改变了内核空间中管理堆的方式,所以研究Windows堆的机制从而挖掘出一种新的利用手法。
原文-Scoop_The_Windows_10_pool.pdf,此段落主要辅佐cve漏洞加以分析了解此利用手法所以对一些内容进行了省略,而且利用方式是针对于x64架构存在。
介绍和了解:
首先了解池是保留给Windows系统内核空间的堆,而池的分配不同于用户空间的分配。而在用户空间使用的堆已经被引入内核。 还需要了解一下前置的知识比如常见分配池中的内存,这里分配的主要函数和Windows释放池的主要函数分别是:
void * ExAllocatePoolWithTag ( POOL_TYPE PoolType ,size_t NumberOfBytes ,unsigned int Tag );
void ExFreePoolWithTag ( void * P, unsigned int Tag );
而ExAllocatePoolWithTag中的PoolType池类型是一个位字段具体如下:
这样一些信息是就存储在PoolType中的:使用的内存类型: NonPagedPool,PagePool,SessionPool 或 NonPagedPoolNx 。如果分配关键位(bit 1),必须成功。如果分配失败,它会触发一个BugCheck。
如果分配符合缓存大小(bit 2)对齐,如果分配使用了PoolQuota机制(bit 3),其他未记录的机制。
NonPagedPool = 0
PagedPool = 1
NonPagedPoolMustSucceed = 2
DontUseThisType = 3
NonPagedPoolCacheAligned = 4
PagedPoolCacheAligned = 5
NonPagedPoolCacheAlignedMustSucceed = 6
MaxPoolType = 7
PoolQuota = 8
NonPagedPoolSession = 20h
PagedPoolSession = 21h
NonPagedPoolMustSucceedSession = 22h
DontUseThisTypeSession = 23h
NonPagedPoolCacheAlignedSession = 24h
PagedPoolCacheAlignedSession = 25h
NonPagedPoolCacheAlignedMustSSession = 26h
NonPagedPoolNx = 200 h
NonPagedPoolNxCacheAligned = 204 h
NonPagedPoolSessionNx = 220 h
而在这其中使用的内存类型很重要,因为它隔离了分配在不同的内存范围内。使用的两种主要内存类型是PagedPool NonPagedPool。 MSDN文档关于它的描述:"非分页池是不可分页的系统内存。它可以从任何IRQL访问,但是它是一种稀缺资源,驱动程序应该只在必要时分配它。分页池是可分页的系统内存,只能在IRQL小于 DISPATCH_LEVEL 时候分配和访问。而NonPagedPoolNx 在Windows 8 中必须使用,而不是NonPagedPool。SessionPool 是用于分配会话空间,并且是唯一的到每个用户会话它主要由Win32k使用 。
The POOL_HEADER
在池中包含在单个页面中的所有块都是从一个POOL_HEADER结构开始的。 当我们编写利用Windows内核中的堆溢出漏洞时,首选要覆盖的就是POOL_Header结构 。这时我们可以选择重写POOL_HEADER结构 ,也可以选择修改POOL_HEADER结构中的部分信息,下述是在windows 1809上简述的POOL_HEADER结构体
struct POOL_HEADER
{
char PreviousSize ;
char PoolIndex ;
char BlockSize ;
char PoolType ;
int PoolTag ;
Ptr64 ProcessBilled ;
};
虽然 POOL_HEADER结构略有改进但总是保持相同的主要字段:
PreviousSize :是先前的chunk 的大小除以16
PoolIndex : 是PoolDescriptor数组中的一个索引
BlockSize : 是当前分配大小除以16
PoolTye : 是一个包含分配类型信息的位字段
ProcessBilled :是一个指向进行分配的KPROCESS的指针只有在PoolType中设置了PoolQuota Flag时才会设置该参数。
win7时ExAllocatePoolWithQuotaTag将利用processbilling字段来指向存储_KPROCESS的指针与分配 。此攻击使用堆溢出漏洞去覆盖分配块POOL_HEADER中的processbilling指针。当释放块时,如果块的PoolType包含PoolQuota(0x8),那么ProcessBilled字段存储的指针将被用于解引用一个值。所以控制ProcessBilled指针可以提供一个任意指针解引用原语从而完成提权。
win8开始ExpPoolQuotaCookie被引入,此攻击手法被缓解。因为在系统启动的引到阶段会生成一个Cookie,这个Cookie的值用于保护指针不被攻击者覆盖。而且当块被释放的时候,内核还会检查编码的指针是不是一个有效的KPROCESS指针。
process_ptr = (struct _KPROCESS *)(chunk_addr ^ ExpPoolQuotaCookie ^ chunk_addr ->process_billed );
if ( process_ptr )
{
if (process_ptr < 0xFFFF800000000000 || (process_ptr ->Header.Type & 0x7F) != 3 )
KeBugCheckEx ([...])
[...]
}
而win8中,还引入了NonPagedPoolNx–一种新的池内存类型,其工作原理与NonPagedPool相同只是其内存页是不可执行的这样即使塞入shellcode到其内存中也是无法利用。
但随着windows不断更新和引入新的机制,Segment Heap的出现重新改变了POOL_HEADER利用方式让我们可以通过堆溢出漏洞实现提权操作。
Segment Heap–段堆自Windows 10 19H1开始用于内核空间,与用户空间中使用的段堆非常相似。就像在用户态使用的一样,段堆是为根据分配大小的不同提供不同的功能,为此来定义了多种不同类型的后端处理。
win10之后堆分为两种:Segment heap和NT heap。当一个进程分配堆的时候,大部分场合默认使用的堆都是后面那种,前面的segment heap通常会在winapp或者某些特殊的进程(核心进程)中会使用到。
这两种堆称为前端堆(Frontend Heap)和后端堆(Backend Heap)
Low Fragmentation Heap(abbr LFH):RtlHpLfhContextAl


443

被折叠的 条评论
为什么被折叠?



