(2)熟悉HackSys驱动

上一章,我们搭建好了内核双机调试环境,实体机作为调试机,虚拟机win7Sp1x86作为被调试机。现在我们先熟悉一下HackSysExtremeVulnerableDriver这个用来练习内核漏洞利用的项目,后面简称(HEVD)下一章我将演示一些漏洞利用的技术。

本次将用到的工具如下:

  • 双机内核调试环境
  • HackSys Extreme Vulnerable Driver (HEVD) - 编译版本,和源码都要
  • OSR Driver Loader(驱动加载器)
  • DebugView (from SysInternals Suite)
  • Visual Studio 2013(或任何你喜欢的版本)

安装测试 HEVD

首先,我会介绍如何安装HEVD,然后配置调试器和被调试机器显示输出调试字符串信息以及HEVD项目的符号信息,最后使用HEVD项目自带的漏洞利用程序,测试一下漏洞是否可以成功触发!

查看调试字符串

HEVD内置了很多漏洞利用时的调试字符串,我们可以使用Windbg在调试机查看,也可以使用DbgView在被调试机器中查看这些调试信息。

在安装HEVD之前,首先需要开启调试输出配置,然后在被调试机器打开DbgView,才能看到安装驱动时的调试信息。

配置调试器

建立好双机调试以后,让Windbg中断下来,输入开启打印调试字符串命令:

ed nt!Kd_Default_Mask F

然后,让被调试机再次运行起来,输入运行命令:

g

配置被调试机器

我们需要以管理员权限运行DbgView。然后我们选择菜单:

Capture -> Capture Kernel

在这里插入图片描述

安装内核漏洞驱动

首先我们需要在被调试机器中下载好,预编译好的二进制文件,以及源码包。安装并测试!
我们可以在HackSysTeam的github上release页面找到最新版本的:
https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases

在这里插入图片描述

预编译压缩包中有两个版本,我们选择32位的i386,打开刚才下载的OSR Driver Loader工具,加载32位的HEVD驱动。

在OSR工具的Service Start项,选择Automatic,然后点击下面的Register Service按钮,提示成功后,再点击
Start Service按钮!

在这里插入图片描述

如果驱动服务安装并启动成功,我们可以看到在windbg或者被调试机器的DbgView中看到打印出的banner信息:

在这里插入图片描述

添加符号

预编译的HEVD包中含有调试符号PDB文件,我们可以将这个PDB文件添加到windbg中,方便我们调试时定位函数以及在源码中的位置等。

首先我们中断调试器,然后查看一下所有加载的模块:

lm

可以使用过滤命令,来找到HEVD模块:

lm m H*

我们可以看到windbg并没有使用任何符号,这个很好解决,首先开启:

!sym noisy

然后使用reload命令查看未加载的符号路径:

.reload /f

47e42f969a8e6a109a4606206365eb0f.png

可以看到这里有两个符号路径,我们选择其中一个:

d:\mss\HEVD.pdb\38ACF1BD8B354E07B7A8C3554683ABD71\HEVD.pdb

比如我就选择第一个,然后按照这个路径创建一模一样的目录,然后把HEVD.pdb拷贝进去,

然后重新执行.reload命令,随后可以使用x命令查看关于HEVD的符号信息了:

x HEVD!*

ad60fb92500ea88a4daf73eb9e085512.png

测试漏洞利用

在下载的HEVD压缩包内有一个漏洞利用测试程序,我们可以传入不同的参数测试每种类型的漏洞,效果是打开一个system权限的cmd:

如果打开该文件时提示缺少msvcr100.dll类库,自己拷一个32位的就行过来,放在同目录下就行!
519e6c95a366af3fd50aa8f5463ca08f.png

测试池溢出利用:

79f2a90595d283459523e2f1c046a3e1.png

我们看到利用成功后弹出了一个system权限的cmd程序,并且windbg中也打印出了相关的漏洞利用调试信息:
在这里插入图片描述

Hi Driver,Let’s Talk!

像r3的漏洞利用一样,我们需要找到一个可以破坏程序执行的输入点,如果在r3程序就会崩溃,在内核r0,系统就会蓝屏。

为了能和驱动通讯,我们需要使用IOCTL码,也叫输入输出控制码。可以让我们从r3发送一些字符串给驱动,这也是我们漏洞利用很重要的一点:

HEVD中包含了各种类型的漏洞,每一个漏洞都可以使用IOCTL加精心构造的输入Buffer来触发。其中有一些触发后,可能会让你的系统蓝屏!

找到Device name & IOCTLs

在开始与驱动通讯之前,我们需要知道两件事:

  1. 驱动创建的设备对象名(如果驱动没有创建任何设备对象,那我们是不可能与驱动通讯的)
  2. 驱动接收的IOCTLs列表

HEVD是一个开源项目,所以我们可以直接从源码中阅读有用的信息。现实生活中的漏洞挖掘大部分情况下我们是没有源码的,有时候只能去逆向来获取相关信息。

来看一下HEVD项目源码中创建设备的相关代码

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L79
e2fe9ffa6d744a0f2515230936a82f69.png

上面显示了设备对象的名称,现在我们来找一下IOCTLs, 我们将从IRPs数组中找:
https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L106

db1cd4cc70b9d69749f2be345d11af3b.png

连接IRP_MJ_DEVICE_CONTOL的函数用来分发各种IOCTL给驱动,我们需要看一下这个函数:

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L193

f8dd85f45191bdc31a0dda11fd13dd68.png

函数中有一个switch来分发各种IOCTL给相应的处理函数,我们可以在头文件中找到这些值得定义:
https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.h#L57
c9eea5b123d4c21af23beb2af56b1ad5.png

编写客户端程序

现在拿到了所有IOCTLs,我们可以使用我们自己编写的程序来和驱动通信,把拿到的IOCTL,写到我们自己程序的头文件中:

#pragma once
#include <windows.h>

const char kDevName[] = "\\\\.\\HackSysExtremeVulnerableDriver";

// IOCTLs
#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_NON_PAGED_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)
#define HACKSYS_EVD_IOCTL_INSECURE_KERNEL_FILE_ACCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80E, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_MEMORY_DISCLOSURE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80F, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_PAGED_POOL_SESSION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_WRITE_NULL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x811, METHOD_NEITHER, FILE_ANY_ACCESS)

每一个IOCTL控制码是被一个标准宏创建的,定义在windows头文件winioctl.h中:
1ca2d8b8334801ffa7bee14193aab498.png

如果你添加了windows.h头文件,它会自动包含上图中这个宏的。

现在我们准备写一个简单的程序来跟驱动通信。首先,我们使用CreateFile打开设备对象,然后我们可以用DeviceIoControl发送IOCTl控制码。

下面是一个简单的例子,发送STACK_OVERFLOW ioctl 给驱动程序:


#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[] = "\\\\.\\HackSysExtremeVulnerableDriver";

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!\n");
        system("pause");
        return -1;
    }

    send_ioctl(dev, HACKSYS_EVD_IOCTL_STACK_OVERFLOW);

    close_device(dev);
    system("pause");
    return 0;
}

尝试编译上面c程序源码,然后放到虚拟机运行,打开DbgView然后查看驱动输出的调试字符信息:

66c42aef6bea3c0a1ddc84e94920bc15.png

正如上图所示,驱动收到了我们发送的控制码,然后打印出了调试字符串信息。

实验:走个崩溃吧~~

尝试输入0x1000的Buffer size,直到驱动程序崩溃,因为虚拟机在调试模式下,崩溃时机器不会蓝屏,而是被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

在我们上面写的简单程序中,用户层输入的buffer用字母 “A” -> asciii 0x41 填充.

RtlFillMemory(inBuffer, bufSize, 'A');

所以只要我们分析崩溃详情时看到了这个字母,就说明我们可以从用户层输入buffer填充内核层的内存。

附录

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值