简介
当恶意程序入侵目标为个人计算机时,相较于服务器,个人计算机对关机或重启操作更加频繁,关机时计算机所有的进程都会被结束,因此恶意程序需要配置自启动来使自己在每次开机时能够被运行。
本篇介绍一个恶意样本(b8090d7d12d6f0abacd16ecfab252f48ee466a254c1751d928567485208634f9)中使用的自启技术,该样本是一个可执行程序,运行后会释放一些文件,其中两个文件比较重要,一个文件为saxbn.exe,该文件是联想应用商店的一个组件(LeASLane),是一个正常的程序。
另一个文件名为libcrypto-3.dll,在正常情况下,该文件为OpenSSL的加密库,被LeASLane所依赖,攻击者通过劫持该文件,并对其使用了文件膨胀、seh异常、加壳等技术来对抗安全检测,在saxbn.exe运行时,会加载被劫持的libcrypto-3.dll,执行其中的恶意载荷。
上述介绍中,可以知道该攻击利用了白加黑,那么想在每次开机时都执行恶意程序,saxbn.exe需要设置自启,该样本对saxbn.exe自启方式比较隐蔽,如果在没有原始样本的情况下,找到自启配置还是比较困难的,在隔离环境中执行样本,并将原始样本清理,然后试着用常规方式,看能否找到可疑的启动项。
自启动排查
上文中已经说明,想要执行恶意载荷,需要在每次开机时执行saxbn.exe,因此需要找到该程序的信息。
最常见的方式是去注册表中查找RUN启动项,配置项如下:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Runonce
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\explorer\run
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
HKCU\Software\Microsoft\Windows\CurrentVersion\Runonce
HKCU\Software\Microsoft\Windows\CurrentVersion\RunServices
HKCU\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKCU\Software\Microsoft\Windows\CurrentVersion\policies\explorer\run
对这些项逐个进行排查,没有发现可疑配置。
然后排查启动项目录,需要自启动的程序会将自己的快捷方式保存在该目录下,按下Win + R键打开"运行"对话框,输入"shell:startup"打开启动项目录,勾选显示隐藏的项目,防止文件隐藏,发现该目录下为空,也没有发现可疑信息。
接下来排查计划任务,恶意程序可以利用com接口,通过类似下方的代码,在计划任务中添加自启动
#include <windows.h>
#include <comdef.h>
#include <taskschd.h>
#include <iostream>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
int main()
{
HRESULT hr;
// 创建与计划任务相关的 COM 对象
CoInitializeEx(NULL, COINIT_MULTITHREADED);
ITaskService* pService = NULL;
hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService);
if (FAILED(hr))
{
std::cout << "Failed to create an instance of ITaskService!" << std::endl;
return -1;
}
// 连接到任务计划程序
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if (FAILED(hr))
{
std::cout << "Failed to connect to the Task Scheduler service!" << std::endl;
pService->Release();
return -1;
}
ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
if (FAILED(hr))
{
std::cout << "Failed to get the root folder!" << std::endl;
pService->Release();
return -1;
}
ITaskDefinition* pTaskDefinition = NULL;
hr = pService->NewTask(0, &pTaskDefinition);
if (FAILED(hr))
{
std::cout << "Failed to create a new task definition!" << std::endl;
pRootFolder->Release();
pService->Release();
return -1;
}
ITriggerCollection* pTriggerCollection = NULL;
hr = pTaskDefinition->get_Triggers(&pTriggerCollection);
if (FAILED(hr))
{
std::cout << "Failed to get the trigger collection!" << std::endl;
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
ITrigger* pTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_BOOT, &pTrigger);
if (FAILED(hr))
{
std::cout << "Failed to create a new trigger!" << std::endl;
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
IActionCollection* pActionCollection = NULL;
hr = pTaskDefinition->get_Actions(&pActionCollection);
if (FAILED(hr))
{
std::cout << "Failed to get the action collection!" << std::endl;
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
IAction* pAction = NULL;
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
if (FAILED(hr))
{
std::cout << "Failed to create a new action!" << std::endl;
pActionCollection->Release();
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
IExecAction* pExecAction = NULL;
hr = pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction);
if (FAILED(hr))
{
std::cout << "Failed to query IExecAction interface!" << std::endl;
pAction->Release();
pActionCollection->Release();
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
// 设置要执行的程序
hr = pExecAction->put_Path(_bstr_t("C:\\Path\\To\\Your\\Program.exe"));
if (FAILED(hr))
{
std::cout << "Failed to set the path of the program to execute!" << std::endl;
pExecAction->Release();
pAction->Release();
pActionCollection->Release();
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
IRegisteredTask* pRegisteredTask = NULL;
hr = pRootFolder->RegisterTaskDefinition(_bstr_t(L"YourTaskName"),
pTaskDefinition,
TASK_CREATE_OR_UPDATE,
_variant_t(),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(L""),
&pRegisteredTask);
if (FAILED(hr))
{
std::cout << "Failed to register the task definition!" << std::endl;
pExecAction->Release();
pAction->Release();
pActionCollection->Release();
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
return -1;
}
std::cout << "The program has been added to startup successfully!" << std::endl;
// 释放 COM 对象及其接口
pRegisteredTask->Release();
pExecAction->Release();
pAction->Release();
pActionCollection->Release();
pTrigger->Release();
pTriggerCollection->Release();
pTaskDefinition->Release();
pRootFolder->Release();
pService->Release();
CoUninitialize();
return 0;
}
打开计划任务,发现也没有相关配置信息。
由于自启的方式还有很多,接下来我们使用Autoruns 工具来查看是否有saxbn.exe启动项的一些信息。
Autoruns 是一款由 Sysinternals 开发的免费工具,用于管理Windows操作系统中自动启动的程序和服务。它提供了一个全面的系统启动项和登录项清单,包括注册表中的所有自动启动位置,从而帮助用户找到并禁用或删除不需要的自动启动项。Autoruns 还提供了强大的搜索功能,以帮助用户准确定位到特定的启动项。此外,该工具还可以显示每个启动项的详细信息,包括路径、描述、发布者和数字签名等,从而帮助用户判断是否需要保留或禁用该启动项。Autoruns 可以帮助用户提高计算机的启动速度,减少系统资源的占用,并且增加系统的安全性,因为某些恶意软件和病毒可能会利用自动启动功能在计算机启动时自动运行。使用Autoruns,用户可以轻松地管理系统的启动项,确保只有必要的程序和服务在系统启动时自动运行。
Autoruns 对自启动的检测是比较全面的,然而遗憾的是,我们在Autoruns中依然没有找到saxbn.exe自启动的信息,但是Autoruns帮我们排除了很多saxbn.exe可能配置的方法。
样本中使用的自启动
那么恶意样本是如何配置自启动的呢?
恶意程序在释放saxbn.exe时,修改了注册表\HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders下Startup项,将其改为C:\Users\Public\Documents\miso。
Windows开机时会根据Startup配置的值,去该目录下获取自启项,查看miso目录,发现有一个快捷方式,指向了加载恶意dll的白程序saxbn.exe。
Windows默认情况下,Startup的初始值为C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup,这与我们之前通过命令执行shell:startup打开的目录相同。但是恶意程序已经修改了Startup值,也就是说,即使修改了Startup项的值,执行shell:startup依旧会打开默认配置,但实际情况该目录已被替换,这会误导我们启动项目录好像没有问题,甚至Autoruns工具也没有检测出来。
总结
使用样本中自启方式,是比较隐蔽,但是该方式仍有缺点,当受害者通过shell:startup添加一些自启项时,会因默认目录被修改,如果没有将默认目录下的文件持续同步到新配置的目录,自启动项不会生效,这时受害者会警惕自己的计算机可能已经被入侵。