随着SandboxiePlus版本的迭代,从1.15.10版本开始,原有无限试用其高级功能的方法已经不再适用。 上图中,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这个全局变量中。每次使用带有高级功能的沙盒时,会获取该变量的信息,来判断是否可以使用高级功能,不能使用高级功能则出现如下提示: 如果能够直接修改驱动侧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的安装目录中。
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
uint64_t getVerifyCertInfoOffset(
const
wchar_t
* sysPath,
const
wchar_t
* pdbPath)
{
uint64_t res = 0;
SymInitialize(GetCurrentProcess(), NULL, FALSE);
uint64_t baseAddress = SymLoadModuleExW(GetCurrentProcess(), NULL, sysPath, NULL, 0x10000000, 0, NULL, 0);
if
(baseAddress == 0)
{
printf
(
"[!] Error load sys file\n"
);
return
0;
}
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;
}
uint64_t getVerifyCertInfoOffset(
const
wchar_t
* sysPath,
const
wchar_t
* pdbPath)
{
uint64_t res = 0;
SymInitialize(GetCurrentProcess(), NULL, FALSE);
uint64_t baseAddress = SymLoadModuleExW(GetCurrentProcess(), NULL, sysPath, NULL, 0x10000000, 0, NULL, 0);
if
(baseAddress == 0)
{
printf
(
"[!] Error load sys file\n"
);
return
0;
}
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;
}
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;
}
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
(hDevice == INVALID_HANDLE_VALUE) {
std::cout <<
"Invalid handle on CreateFileA!"
<< std::endl;
std::cout <<
"Error code: "
<< GetLastError() << std::endl;
}
void
* buf = (
void
*)
malloc
(4096);
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
;
}
free
(buf);
}
DriverInterface::DriverInterface()
{
hDevice = CreateFileA(
"\\\\.\\EchoDrv"
,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
NULL,
NULL
);
if
(hDevice == INVALID_HANDLE_VALUE) {
std::cout <<
"Invalid handle on CreateFileA!"
<< std::endl;
std::cout <<
"Error code: "
<< GetLastError() << std::endl;
}
void
* buf = (
void
*)
malloc
(4096);
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
;
}
[培训]科锐逆向工程师培训第53期2025年7月8日开班!