EXP编写-windows内核利用原语技巧总结
总结下自己之前分析过的漏洞,相关利用原语的编写方法与可使用的场景.
1.通过wnf构造的任意读写原语
wnf是微软操作系统的一种通知框架,用于操作系统内部和应用程序之间进行通信和事件传递.
用户可以订阅一些类型的事件(StateName),在发生变化时可以接收到通知.
1.wnf的api
NtCreateWnfStateName
1 2 3 4 5 6 7 8 9
| typedef NTSTATUS (NTAPI *NtCreateWnfStateName)( _Out_ PWNF_STATE_NAME StateName, _In_ WNF_STATE_NAME_LIFETIME NameLifetime, _In_ WNF_DATA_SCOPE DataScope, _In_ BOOLEAN PersistData, _In_opt_ PCWNF_TYPE_ID TypeId, _In_ ULONG MaximumStateSize, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor );
|
NtUpdateWnfStateData
1 2 3 4 5 6 7 8
| typedef NTSTATUS (NTAPI *NtUpdateWnfStateData)( _In_ PWNF_STATE_NAME StateName, _In_reads_bytes_opt_(Length) const VOID * Buffer, _In_opt_ ULONG Length, _In_opt_ PCWNF_TYPE_ID TypeId, _In_opt_ const PVOID ExplicitScope, _In_ WNF_CHANGE_STAMP MatchingChangeStamp, _In_ ULONG CheckStamp);
|
NtQueryWnfStateData
1 2 3 4 5 6 7
| typedef NTSTATUS (NTAPI * NtQueryWnfStateData)( _In_ PWNF_STATE_NAME StateName, _In_opt_ PWNF_TYPE_ID TypeId, _In_opt_ const VOID * ExplicitScope, _Out_ PWNF_CHANGE_STAMP ChangeStamp, _Out_writes_bytes_to_opt_(*BufferSize, *BufferSize) PVOID Buffer, _Inout_ PULONG BufferSize);
|
NtDeleteWnfStateData
1 2 3 4 5 6
| typedef NTSTATUS (NTAPI * __NtDeleteWnfStateData) ( _In_ PWNF_STATE_NAME StateName, _In_opt_ const VOID *ExplicitScope );
|
2.wnf对象的内核结构
_WNF_NAME_INSTANCE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| +0x000 Header : _WNF_NODE_HEADER +0x008 RunRef : _EX_RUNDOWN_REF +0x010 TreeLinks : _RTL_BALANCED_NODE +0x028 StateName : _WNF_STATE_NAME_STRUCT +0x030 ScopeInstance : Ptr64 _WNF_SCOPE_INSTANCE +0x038 StateNameInfo : _WNF_STATE_NAME_REGISTRATION +0x050 StateDataLock : _WNF_LOCK +0x058 StateData : Ptr64 _WNF_STATE_DATA +0x060 CurrentChangeStamp : Uint4B +0x068 PermanentDataStore : Ptr64 Void +0x070 StateSubscriptionListLock : _WNF_LOCK +0x078 StateSubscriptionListHead : _LIST_ENTRY +0x088 TemporaryNameListEntry : _LIST_ENTRY +0x098 CreatorProcess : Ptr64 Void +0x0a0 DataSubscribersCount : Int4B +0x0a4 CurrentDeliveryCount : Int4B
|
_WNF_SCOPE_INSTANCE
1 2 3 4 5 6 7 8 9 10
| +0x000 Header : _WNF_NODE_HEADER +0x008 RunRef : _EX_RUNDOWN_REF +0x010 DataScope : _WNF_DATA_SCOPE +0x014 InstanceIdSize : Uint4B +0x018 InstanceIdData : Ptr64 Void +0x020 ResolverListEntry : _LIST_ENTRY +0x030 NameSetLock : _WNF_LOCK +0x038 NameSet : _RTL_AVL_TREE +0x040 PermanentDataStore : Ptr64 Void +0x048 VolatilePermanentDataStore : Ptr64 Void
|
_WNF_STATE_DATA
1 2 3 4
| +0x000 Header : _WNF_NODE_HEADER +0x004 AllocatedSize : Uint4B +0x008 DataSize : Uint4B +0x00c ChangeStamp : Uint4B
|
3.利用原理
首先看看一些api的作用
NtCreateWnfStateName
追踪到ntoskrnl!NtCreateWnfStateName函数,大概看下是如何创建wnf对象的.
在ring0创建对象时,会将StateName与0x41C64E6DA3BC0074进行xor编码

然后在ExpWnfCreateNameInstance中为wnf对象申请池空间,默认大小为0xb8,结构体大小为0xa8,加上pool_header的长度,后面的代码填充了一些字段.

NtUpdateWnfStateData
在NtUpdateWnfStateData-> ExpWnfWriteStateData中判断新的StateData数据长度,大于之前的就重新申请StateData池空间,然后写入数据.可以通过这个函数进行任意地址写.


NtQueryWnfStateData
在NtQueryWnfStateData-> ExpWnfReadStateData中,就是读取StateData数据到内存,可以通过这个函数进行任意地址读.

NtDeleteWnfStateData
用于释放wnf对象,释放内存池.可以通过这个函数构造内存空洞.
任意地址读写
通过wnf构造任意地址读写其实就是通过堆喷构造堆的布局,然后通过漏洞覆盖掉wnf内核对象的一些字段.
通过覆盖_WNF_STATE_DATA中的DataSize字段,可以将数据的范围扩大,通过调用函数NtQueryWnfStateData/NtUpDateWnfStateData 可以实现相对的任意地址读写,构造了堆的布局后,可以通过这个相对的任意地址读写去覆盖掉_WNF_NAME_INSTANCE中StateData的指针,通过伪造一个_WNF_STATE_DATA
对象就可以实现任意地址读写了.
不过在这之前需要注意堆喷对堆的布局,让_WNF_STATE_DATA分配在_WNF_NAME_INSTANCE的地址之前.
2.Io-ring任意地址读写原语