• 内核漏洞学习—熟悉HEVD


    一直以来内核漏洞安全给很多人的印象就是:难,枯燥。但是内核安全是否掌握是衡量一个系统安全工程师水平的标准之一,也是安全从业人员都应该掌握的基本功。本文通过详细的实例带领读者走进内核安全的大门。难度系数:四颗星

    原文地址:https://hshrzd.wordpress.com/2017/06/05/starting-with-windows-kernel-exploitation-part-2/

    由prison翻译整理,首发i春秋。

    本文默认读者已经配置好了基本实验环境,因为环境配置网络上有大量详细资源,在此编者不再单独成文介绍环境。此文章供参考:https://hshrzd.wordpress.com/201 … setting-up-the-lab/

    1.本文所使用的环境:

    §  环境配置 the previous part

    §  HackSys Extreme Vulnerable Driver(HEVD) – prebuild version + the source code

    §  OSR Driver Loader

    §  DebugView (from SysInternals Suite)

    §  Visual Studio 2012 (版本随意)

    安装并测试HEVD首先,我将展示如何安装HEVD。我们将会配置Debugee和调试器,以查看调试字符串和HEVD的符号。我们也会用一些专用的漏洞来玩。

    油管视频(我也很无奈啊~):

    查看调试字符串HEVD和一些专门的行为利用了大量的信息作为调试字符串。我们可以从调试器(使用WinDbg)和被调试程序(使用调试器)来观察它们。

    安装HEVD之前,我们将设着一些内容来查看驱动程序初始化期间打印的字符串。


    在调试器上:为了获得kd标识,我们需要中断调试器的执行(WinDbg->Debug->Break)。然后,我们可以通过命令打印调试字符串:

    ed nt!Kd_Default_Mask 8

    之后,我们可以通过执行命令进一步运行调试器:

    g
    Warning: Enabling this slows down the Debugee. So, whenever possible, try to watch DebugStrings locally (on the Debugee only).

    这个警告大概是说这个命令会减慢调试器的速度,所以优先选择在本地仅在被调试程序上观察调试字符。

    在被调试程序上:

    我们需要以管理员身份运行DebugView。然后我们从菜单中依次选择:

    Capture->Capture Kernel

    1.png

    安装驱动程序首先,我们将在被调试方(靶机)上下载预构建包(驱动程序+exploit)安装并且进行测试。我们可以在GitHub上找到HackSysTem,https://github.com/hacksysteam/HackSy**tremeVulnerableDriver/releases

    这个包包含两个易受攻击的版本的驱动程序,我们将选一个比较脆皮的进行构建(

    32位i386)

    2.png

    我们选择自启动。然后我们点击[RegisterService]成功后点击[StartService]

    我们应该可以在调试方的WinDbg和靶机的WbgView上看到打印出的HEVD

    添加标识符HEVD的预编译包带有符号(sdb文件),我们还可以将其添加到调试器中。首先,我们通过发送一个中断信号来停止调试器,并查看所有已加载的模块。

    我们可以设置一个过滤器来找到HEVD模块

    lm m H*

    我们可以看到它没有任何标识符,我们可以很容易的将其固定。首先,打开:

    !sym_noisy

    –用来打印所有和WinDbg引用路径的信息以找到标识符。然后试着重载这些标识符:

    .reload

    再试着再次提引用。你将看到路径,我们可以复制pdb文件。在将pdb文件移动到调试器机器上的适当位置之后,再次重载标识符。你可以试着打印HEVD的所有功能来进行测试。

    x HEVD!*利用测试同一个包中还包含了一组专用的漏洞。我们可以通过执行一个适当的命令来运行它们。我们来尝试进行一些部署,并执行cmd.exe

    3.png

    池溢出利用部署:

    4.png

    如果利用成功,请求的应用程序将会被部署到更高的权限。

    通过命令:

    whoami

    我们可以确定,它的权限升高。

    5.png

    与此同时,我们可以调试机上看到由expolit打印的调试字符串:

    6.png

    所有的攻击,除了双重取回,都应该在一个核心上运行良好。如果我们想要利用这个,则需要在调试器机器上启用两个核心。

    警告:有些exp并不是100%可靠的,我们在部署它们之后可能会遇到系统崩溃。别担心,一切不以格盘为目的的崩溃信息都是纸老虎。

    与驱动进行一场扎心的交流就像在用户层的情况下一样,在内核方面的开发利用中,从寻找点开始,我们可以为程序提供一个输入。然后,我们需要找到能够破坏执行的输入(与用户层相反——在内核层面上,崩溃将直接导致蓝屏!)最后,我们将尝试以一种控制脆弱程序执行的方式来构造输入。

    为了与来自用户模式的驱动程序进行通信,我们将发送一些IOCTL-输入-输出控制。IOCTL允许我们从用户向驱动程序发送一些内容到缓冲区。这是我们可以尝试利用的点。

    HEVD包含各种各样的漏洞的演示。每一个都可以使用不同的IOCTL触发,并被所提供的缓冲区所利用。有些(但不是全部)会导致系统在触发时崩溃。

    查找设备名称和IOCTL

    在我们尝试与设备连接之前,我们需要知道两件事:

    驱动程序创建的设备(如果它不创建任何东西,我们将无法进行通信)

    驱动程序接受的IOCTL(输入-输出控制)列表

    HEVD是开源的,因此我们可以直接从源代码中读取所有必需的数据。在黑盒测试过程中,我们可能要通过逆向驱动程序来读取所需数据。

    让我们来看看HEVD创建一个设备的代码片段。

    https://github.com/hacksysteam/HackSy**tremeVulnerableDriver/blob/master/Driver/HackSy**tremeVulnerableDriver.c#L79

    7.png

    上面提到了这个设备的名称。

    现在,我们通过从IRP数组来找IOCTL列表

    连接到IRP_MJ_DEVICE_CONTOL的函数将会分配IOCTL发送给驱动程序。所以,我们需要看一下这个函数。

    https://github.com/hacksysteam/HackSy**tremeVulnerableDriver/blob/master/Driver/HackSy**tremeVulnerableDriver.c#L193

    9.png

    这里有一个switch循环,调用一个特定的处理IOCTL的函数,我们可以对这些switch的case进行转换以获取我们所需要的IOCTL列表。常量的值在header中被忽视。

    https://github.com/hacksysteam/HackSy**tremeVulnerableDriver/blob/master/Driver/HackSy**tremeVulnerableDriver.h#L57

    10.png

    编写一个客户端应用程序

    好的,我们现在已经搞到了所有需要用到的数据,我们可以用我们自己的程序与驱动程序进行通信。我们可以把它们放在头文件中,也就是:hevd_constants.h

    #pragma once
    #include <windows.h>
     
    const char kDevName[] = "\\.\HackSy**tremeVulnerableDriver";
     
    #define HACKSYS_EVD_IOCTL_STACK_OVERFLOW                  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS               CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE             CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_POOL_OVERFLOW                   CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT             CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_USE_UAF_OBJECT                  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT                 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT            CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_TYPE_CONFUSION                  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW                CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE        CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE     CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80C, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define HACKSYS_EVD_IOCTL_DOUBLE_FETCH                    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80D, METHOD_NEITHER, FILE_ANY_ACCESS)

    IOCTL的数目是由一个标准的Windows头文件winioctl.h中定义的宏所创建的。

    13.png

    如果你引用了windows.h文件,上面的宏将被自动添加。现在,我们不需要操心特定常量的含义——我们只需要使用已定义的元素就可以了。

    因此,我们准备编写一个简单的用户层应用程序,该应用程序将与驱动程序进行对话。首先,我们使用函数CreateFile打开设备。然后,我们可以使用DeviceIoControl.来发送IOCTL。

    下面你可以看到一个小例子。这个应用程序将STACK_OVERFLOWIOCTL 发送给驱动程序: send_ioctl.cpp

    #include <stdio.h>
    #include <windows.h>
     
    #define HACKSYS_EVD_IOCTL_STACK_OVERFLOW    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
     
    const char kDevName[] = "\\.\HackSy**tremeVulnerableDriver";
     
    HANDLE open_device(const char* device_name)
    {
        HANDLE device = CreateFileA(device_name,
            GENERIC_READ | GENERIC_WRITE,
            NULL,
            NULL,
            OPEN_EXISTING,
            NULL,
            NULL
        );
        return device;
    }
     
    void close_device(HANDLE device)
    {
        CloseHandle(device);
    }
     
    BOOL send_ioctl(HANDLE device, DWORD ioctl_code)
    {
        //prepare input buffer:
        DWORD bufSize = 0x4;
        BYTE* inBuffer = (BYTE*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufSize);
     
        //fill the buffer with some content:
        RtlFillMemory(inBuffer, bufSize, 'A');
     
        DWORD size_returned = 0;
        BOOL is_ok = DeviceIoControl(device,
            ioctl_code,
            inBuffer,
            bufSize,
            NULL, //outBuffer -> None
            0, //outBuffer size -> 0
            &size_returned,
            NULL
        );
        //release the input bufffer:
        HeapFree(GetProcessHeap(), 0, (LPVOID)inBuffer);
        return is_ok;
    }
     
    int main()
    {
        HANDLE dev = open_device(kDevName);
        if (dev == INVALID_HANDLE_VALUE) {
            printf("Failed!
    ");
            system("pause");
            return -1;
        }
     
        send_ioctl(dev, HACKSYS_EVD_IOCTL_STACK_OVERFLOW);
     
        close_device(dev);
        system("pause");
        return 0;
    }

    尝试编译这个程序并将其部署到靶机上。启动DebugView观察由驱动程序打印的调试字符串。

    你可能看到类似输出:

    12.png

    你懂得~

    以下为作者福利:

    练习,让我们一起搞波事情~

    我在HEVD创建了一个小客户端,你可以在这看到源代码:

    https://github.com/hasherezade/wke_exercises/tree/master/task1

    编译后的32位二进制文件: here.

    试着玩各种不同的IOCTL,直到你成功。因为Debugee在调试器的控制下运行,你不会出现蓝幕——相反,WinDbg会被触发。试着对每一个案例做一个简短的崩溃分析。从打印信息开始:

    !analyze -v其他一些有用的命令:

    k - stack trace
    kb - stack trace with parameters
    r - registers
    dd [address]- display data as DWORD starting from the address

    要了解更多,请参阅“WinDbg”帮助文件:

    .hh

    在我们的示例应用程序中,用户缓冲区填满了“A”-ASCII 0×41

    https://github.com/hasherezade/wke_exercises/blob/master/task1/src/main.cpp#L34

    RtlFillMemory(inBuffer, bufSize, 'A');

    油管示例:

    Example #1

    https://www.youtube.com/watch?v=lw7vMrkeTpY

    Example #2

    https://www.youtube.com/watch?v=YV0IqXEUf5s

    请注意,触发相同的漏洞可能会给您带来不同的输出,这取决于崩溃的直接原因,这与溢出的大小、内存的当前布局等有关。

  • 相关阅读:
    linux反汇编
    Java中UML图
    Java设计模式_创建型模式_单例模式
    Javadoc注释的用法
    VIM使用技巧1
    手动破解的 Linux下的Maltab 2014b
    让vim的在输入模式下现实光标不同
    Vim 自动补全成对的括号和引号
    MAMP:在 OSX 中搭建 Apache, MySQL, PHP 环境并本地安装、调试 WordPress
    MAC+iTerm定制目录显示颜色和提示符
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/7374977.html
Copyright © 2020-2023  润新知