(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填充內核層的內存。

附錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章