首页
社区
课程
招聘
[原创] 基于BYOVD方法无限使用SandboxiePlus的高级功能
发表于: 2025-6-10 15:49 596

[原创] 基于BYOVD方法无限使用SandboxiePlus的高级功能

2025-6-10 15:49
596

随着SandboxiePlus版本的迭代,从1.15.10版本开始,原有无限试用其高级功能的方法已经不再适用。
verify_cert_active
上图中,proces.c中的Process_Create函数在目标进程启动的时候校验了证书的active字段.如果active是0,则在目标进程启动五分钟后,kill掉目标进程。否则,进程正常执行,不进行kill。
原来的破解方法是基于证书有效期的字段全在用户态进行校验,修改用户态校验的结果,进而达到无限试用高级功能的目的。但从1.15.10版本开始出现了驱动侧(r0)验证证书的有效性,自然而然原来的破解方法就失效了。本文试用BYOVD的方法修改驱动侧的全局变量Verify_CertInfo,实现在没有证书的情况下也可以免费试用其高级功能。

BYOVD(Bring Your Own Vulnerable Driver),即攻击者向目标环境植入一个带有漏洞的合法驱动程序,再通过漏洞利用获得内核权限以杀死/致盲终端安全软件等。这项技术最初主要被如Turla和方程式这样的顶级APT组织所使用,而随着攻击成本的降低,其它攻击组织也逐渐开始使用这项技术。

SandboxiePlus 高级功能无限试用文章中,我介绍了SandboxiePlus的验证过程。它通过验证证书的内容和签名,来判断当前证书的类型和有效期,最后将这些信息写入到Verify_CertInfo这个全局变量中。每次使用带有高级功能的沙盒时,会获取该变量的信息,来判断是否可以使用高级功能,不能使用高级功能则出现如下提示:
redbox_5_mins
如果能够直接修改驱动侧Verify_CertInfo变量的值为一个有效期证书验证之后的结果,那么即便没有证书,我们也可以实现无限使用高级功能的效果。而是实现驱动侧修改Verify_CertInfo变量的方法就是使用一个带有写任意地址漏洞的签名驱动。

为了保证使用者系统的安全性,在需要破解的时候创建漏洞驱动服务,马上启动该服务。在修改变量结束之后,马上停止该漏洞服务,并删除。这里使用的漏洞驱动是echo_driver,漏洞利用的Github地址是097K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6C8K9i4c8W2x3o6y4Q4x3V1k6W2j5$3S2G2j5h3y4Q4x3X3c8H3L8$3y4Q4x3V1k6@1M7X3g2W2i4K6u0r3L8h3q4A6L8W2)9J5c8W2m8G2b7#2!0q4x3#2)9^5x3q4)9^5x3R3`.`.

SandboxiePlus的安装目录中存在SbieDrv.sys和SbieDrv.pdb文件,可以解析这个PDB文件获取Verify_CertInfo变量的偏移offset。
然后获取SbieDrv驱动的基地址,然后两者之和就是Verify_CertInfo地址。在Windows系统中,所有进程共享内核地址空间,因此当前进程修改Verify_CertInfo地址就可以实现预期的效果。

read_memory_raw函数参数的意思是将targetProcess进程的address地址的len字节内容写入到当前进程buf地址中。这里processHandle我们设置成当前进程,表示从当前进程中读取newValue对应的值写入到Verify_CertInfo变量中,而newValue变量的值是一个合法试用期证书的值,但将时间调至了无限。

前面两个小结介绍了修改变量Verify_CertInfo的方法,即怎么修改。还有一个关键问题:什么时候修改?
通过阅读SandboxiePlus的代码,发现了什么时候会触发Verify_CertInfo变量的修改:

解决方法:

本文利用BYOVD方法直接修改驱动SbieDrv的变量Verify_CertInfo为一个有效的值,实现了无证书的情况下使用其高级功能的效果。在"安全狗的开发日常"公众号回复SandboxiePlus,可以获取补丁文件的下载链接,其中1.15.12_patch_files.zip是使用本方法的补丁。获取该zip文件并解压缩,把解压之后的文件全部拷贝SandboxiePlus的安装目录中。
patch_usage

echo_driver:d38K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2D9L8$3I4V1M7X3W2$3k6i4u0K6i4K6u0W2K9h3!0Q4x3V1k6V1M7X3W2$3k6i4u0K6i4K6u0r3j5h3k6T1z5r3u0T1y4o6k6Q4x3X3b7I4k6o6p5K6i4K6u0V1y4o6l9%4k6q4)9J5k6o6V1^5y4U0k6Q4x3X3b7I4k6r3q4S2y4$3x3^5x3X3y4S2y4U0y4Q4x3V1j5`.
echo_driver poc:325K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6C8K9i4c8W2x3o6y4Q4x3V1k6W2j5$3S2G2j5h3y4Q4x3X3c8H3L8$3x3`. /tree/main/PoC
SandboxiePlus 高级功能无限试用破解 :633K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7J5x3o6p5%4y4U0M7K6i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9

// 参数是sbiedrv驱动路径和sbiedrv pdb路径
uint64_t getVerifyCertInfoOffset(const wchar_t* sysPath, const wchar_t* pdbPath)
{
    uint64_t res = 0;
 
    SymInitialize(GetCurrentProcess(), NULL, FALSE);
    // load sys file
    uint64_t baseAddress = SymLoadModuleExW(GetCurrentProcess(), NULL, sysPath, NULL, 0x10000000, 0, NULL, 0);
    if (baseAddress == 0)
    {
        printf("[!] Error load sys file\n");
        return 0;
    }
 
    // load pdb file
    if (!SymSetSearchPathW(GetCurrentProcess(), pdbPath))
    {
        printf("[!] Error load pdb file\n");
        SymUnloadModule(GetCurrentProcess(), baseAddress);
        SymCleanup(GetCurrentProcess());
        return 0;
    }
 
    char symbolBuffer[sizeof(SYMBOL_INFOW) + 0x100 * sizeof(wchar_t)] = { 0 };
    PSYMBOL_INFOW pSymbol = (PSYMBOL_INFOW)symbolBuffer;
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFOW);
    pSymbol->MaxNameLen = 0xff;
 
    if (SymFromNameW(GetCurrentProcess(), L"Verify_CertInfo", pSymbol))
    {
        res = pSymbol->Address - baseAddress;
    }
 
    SymUnloadModule(GetCurrentProcess(), baseAddress);
    SymCleanup(GetCurrentProcess());
    return res;
}
// 参数是sbiedrv驱动路径和sbiedrv pdb路径
uint64_t getVerifyCertInfoOffset(const wchar_t* sysPath, const wchar_t* pdbPath)
{
    uint64_t res = 0;
 
    SymInitialize(GetCurrentProcess(), NULL, FALSE);
    // load sys file
    uint64_t baseAddress = SymLoadModuleExW(GetCurrentProcess(), NULL, sysPath, NULL, 0x10000000, 0, NULL, 0);
    if (baseAddress == 0)
    {
        printf("[!] Error load sys file\n");
        return 0;
    }
 
    // load pdb file
    if (!SymSetSearchPathW(GetCurrentProcess(), pdbPath))
    {
        printf("[!] Error load pdb file\n");
        SymUnloadModule(GetCurrentProcess(), baseAddress);
        SymCleanup(GetCurrentProcess());
        return 0;
    }
 
    char symbolBuffer[sizeof(SYMBOL_INFOW) + 0x100 * sizeof(wchar_t)] = { 0 };
    PSYMBOL_INFOW pSymbol = (PSYMBOL_INFOW)symbolBuffer;
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFOW);
    pSymbol->MaxNameLen = 0xff;
 
    if (SymFromNameW(GetCurrentProcess(), L"Verify_CertInfo", pSymbol))
    {
        res = pSymbol->Address - baseAddress;
    }
 
    SymUnloadModule(GetCurrentProcess(), baseAddress);
    SymCleanup(GetCurrentProcess());
    return res;
}
// 这里需要使用未公开的系统调用参数SystemModuleInformation,对应的系统调用是NtQuerySystemInformation
uint64_t getSbieDrvBaseAddress()
{
    NTSTATUS status = STATUS_SUCCESS;
    uint64_t sbiedrvBaseAddress = 0;
    PRTL_PROCESS_MODULES moduleInfo = (PRTL_PROCESS_MODULES)VirtualAlloc(NULL, 0x400 * 0x400, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (moduleInfo == NULL)
    {
        printf("[!] Error allocating module memory! Error Code: %lu\n", GetLastError());
        return 0;
    }
 
    if(!NT_SUCCESS(status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)11,
        moduleInfo, 0x400 * 0x400, NULL)))
    {
        printf("[!] Error: Unable to query module list (%#x)\n", status);
        VirtualFree(moduleInfo, 0, MEM_RELEASE);
        return 0;
    }
 
    for (ULONG i = 0; i < moduleInfo->NumberOfModules; i++)
    {
        if (!_stricmp((const char*)moduleInfo->Modules[i].FullPathName +
            moduleInfo->Modules[i].OffsetToFileName, "sbiedrv.sys"))
        {
            sbiedrvBaseAddress = (uint64_t)moduleInfo->Modules[i].ImageBase;
            break;
        }
    }
    return sbiedrvBaseAddress;
}
// 这里需要使用未公开的系统调用参数SystemModuleInformation,对应的系统调用是NtQuerySystemInformation
uint64_t getSbieDrvBaseAddress()
{
    NTSTATUS status = STATUS_SUCCESS;
    uint64_t sbiedrvBaseAddress = 0;
    PRTL_PROCESS_MODULES moduleInfo = (PRTL_PROCESS_MODULES)VirtualAlloc(NULL, 0x400 * 0x400, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (moduleInfo == NULL)
    {
        printf("[!] Error allocating module memory! Error Code: %lu\n", GetLastError());
        return 0;
    }
 
    if(!NT_SUCCESS(status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)11,
        moduleInfo, 0x400 * 0x400, NULL)))
    {
        printf("[!] Error: Unable to query module list (%#x)\n", status);
        VirtualFree(moduleInfo, 0, MEM_RELEASE);
        return 0;
    }
 
    for (ULONG i = 0; i < moduleInfo->NumberOfModules; i++)
    {
        if (!_stricmp((const char*)moduleInfo->Modules[i].FullPathName +
            moduleInfo->Modules[i].OffsetToFileName, "sbiedrv.sys"))
        {
            sbiedrvBaseAddress = (uint64_t)moduleInfo->Modules[i].ImageBase;
            break;
        }
    }
    return sbiedrvBaseAddress;
}
DriverInterface::DriverInterface()
{
    // 获取对应的设备句柄
    hDevice = CreateFileA(
        "\\\\.\\EchoDrv",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL
    );
 
    //If driver handle failed to open print message and return
    if (hDevice == INVALID_HANDLE_VALUE) {
        std::cout << "Invalid handle on CreateFileA!" << std::endl;
        //Get the last error from windows for CreateFile
        std::cout << "Error code: " << GetLastError() << std::endl;
    }
 
    // Yes, this buffer seems useless - but without it the driver BSOD's the PC.
    //Create a buffer to have data returned to.
    void* buf = (void*)malloc(4096);
 
    //Call IOCTL that sets the PID variable and gets past the DWORD check
    //0x9e6a0594 - IOCTL Code,设置当前的进程。如果不设置当前进程,无法调用驱动对应的功能,具体原因请逆向分析echo_driver.sys文件
    BOOL success = DeviceIoControl(hDevice, 0x9e6a0594, NULL, NULL, buf, 4096, NULL, NULL);
    if (!success) {
        std::cout << "DeviceIOControl 0x9e6a0594 failed!" << std::endl;
        std::cout << "Error code: " << GetLastError() << std::endl;
 
        CloseHandle(hDevice);
        return;
    }
 
    //We don't need that buffer anymore
    free(buf);
}
DriverInterface::DriverInterface()
{
    // 获取对应的设备句柄
    hDevice = CreateFileA(
        "\\\\.\\EchoDrv",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL
    );
 
    //If driver handle failed to open print message and return
    if (hDevice == INVALID_HANDLE_VALUE) {
        std::cout << "Invalid handle on CreateFileA!" << std::endl;
        //Get the last error from windows for CreateFile
        std::cout << "Error code: " << GetLastError() << std::endl;
    }
 
    // Yes, this buffer seems useless - but without it the driver BSOD's the PC.
    //Create a buffer to have data returned to.
    void* buf = (void*)malloc(4096);
 
    //Call IOCTL that sets the PID variable and gets past the DWORD check
    //0x9e6a0594 - IOCTL Code,设置当前的进程。如果不设置当前进程,无法调用驱动对应的功能,具体原因请逆向分析echo_driver.sys文件
    BOOL success = DeviceIoControl(hDevice, 0x9e6a0594, NULL, NULL, buf, 4096, NULL, NULL);
    if (!success) {
        std::cout << "DeviceIOControl 0x9e6a0594 failed!" << std::endl;
        std::cout << "Error code: " << GetLastError() << std::endl;
 
        CloseHandle(hDevice);
        return;
    }
 
    //We don't need that buffer anymore

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 2257
活跃值: (4520)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
6天前
0
游客
登录 | 注册 方可回帖
返回