0%

EXP编写-windows内核利用原语技巧总结

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编码

image-20230821170229307

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

image-20230821171538475

NtUpdateWnfStateData

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

image-20230821173147939

image-20230821173621808

NtQueryWnfStateData

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

image-20230821174741796

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任意地址读写原语