利用了 Empire 代理从受感染主机窃取文件的方式。通过冒充代理,他可以“欺骗” C2 服务器,使其认为有效代理响应了“下载”任务。由于服务器没有正确处理文件名,zeroSteiner 可以遍历文件系统并覆盖关键系统文件,从而导致远程代码执行 (RCE)。
从 stager 中提取两个关键信息:staging 密钥和 C2 服务器 URL。C2 服务器上的侦听器有一个与之关联的 staging 密钥。任何想要向侦听器注册的代理都必须使用此密钥进行“身份验证”。如果我们有这个密钥,我们实际上就是 C2 服务器的代理。
第一阶段的目的是减少 stager 的大小。它用于加载第二阶段所需的数据包处理、加密和引导类。对于我们的目的来说,这一阶段是不必要的。
我们使用有效的暂存密钥请求第二阶段。第二阶段用于通过 Diffie-Hellman 密钥交换过程建立新的会话密钥。这也是我们告诉 C2 服务器我们想要的会话 ID的地方。
请求STAGE2
涉及向 C2 服务器发送系统信息。当一切按预期工作时,代理会收集用户和操作系统信息,并使用新的会话密钥对其进行加密。C2 服务器使用主代理类进行响应。
C2 任务
Empire 与大多数 C2 框架一样,具有任务流程。当操作员与代理交互时,C2 服务器会将其命令排队到数据库中。代理会定期呼叫 C2 服务器并请求新的任务。如果有任务等待,它们会执行指定的操作并将结果响应给 C2 服务器。代理如何响应任务以及服务器如何处理响应取决于任务类型。以下是一些任务类型的示例:
-
TASK_SYSINFO - 更新代理的主机信息
-
TASK_SHELL-执行 shell 命令
-
TASK_UPLOAD-将文件上传到受害者的系统
-
TASK_DOWNLOAD-从受害者的系统下载文件
无需服务器执行任务即可处理响应。换句话说,我们可以通过发送未经提示的“响应”来强制服务器处理任务数据。
目录遍历
在调查服务器如何处理不同任务类型之后,我们注意到 TASK_DOWNLOAD 路径中有一个不祥的注释:
此时,我们找到了第一个漏洞利用候选者。如果您还记得STAGE1
,代理会选择自己的会话 ID。会话 ID 为 8 个字节且未经过滤。这意味着我们可以告诉服务器我们的会话 ID 应该是什么./../../
,它会将其附加到 download_dir: /<install path>/empire/server/downloads/
。
仅凭这些信息,我们就可以覆盖下载目录上一个或两个目录的任何文件。主要候选者是中的server.py和config.ymlempire/server/
。我们可以用后门覆盖server.py ,或者我们可以将config.yml中的下载目录更改为 root ,并发送另一个 TASK_DOWNLOAD 以写入/etc/cron.d/
。但是,这两个文件都是在启动时加载的。因此,我们要么让 C2 服务器崩溃,要么等待操作员重新启动它。我们可以通过社会工程学来实现这一点,也许我们可以通过另一种任务类型向操作员发送虚假错误,但这并不是一个令人满意的利用途径。
绕过 Skywalker 检测
我们可以在下载目录之外创建文件的全部原因归结为这种情况:
safe_path = download_dir.absolute()
if not str(save_file.absolute()).startswith(str(safe_path)):
该条件检查save_file路径是否以safe_path ( )开头
/<install dir>/empire/server/downloads/
。如果不是,则服务器假定响应是目录遍历攻击,发出“试图利用 skywalker!”检测并返回。问题是,
absolute()
不会规范化路径。它只解析相对路径。这意味着它将类似 的路径转换dir/file.txt
为/home/kali/dir/file.txt
。但它也会将类似 的路径转换../../dir/file.txt
为/home/kali/../../dir/file.txt
。令人困惑的是,Python 的 pathlib 中的路径规范化是使用abspath()
方法而不是 来完成的absolute()
。我们第一次尝试绕过时需要猜测 Empire 的安装位置。我们的路径遍历覆盖了整个save_path。因此,猜测正确的安装路径将满足
startswith()
条件。
但还有一种更简单的方法可以做到这一点。我们的路径遍历覆盖了 save_path,因为pathlib 附加子目录的方式存在细微差别。如果您以开头附加目录,
/
它会用子目录替换整个路径。这个怪癖对 Empire 有利。然而,它说明了为什么..
从输入中剥离不能替代路径规范化。
我们只需要确保我们的遍历不会从这里开始,
\
并且我们有一个可靠的漏洞。
另一条路径遍历
还有另一个函数用于保存代理响应中的文件:
save_module_file()
。其逻辑与大致相同save_file()
,Skywalker 检测等。该save_module_file()
函数由两种任务类型调用:TASK_CMD_WAIT_SAVE 和 TASK_CMD_JOB_SAVE。由于没人喜欢等待,我们首先研究了 TASK_CMD_JOB_SAVE。但它没有正确编码输入并插入
b’’
到文件扩展名中。幸运的是,TASK_CMD_WAIT_SAVE 完全相同,但在创建文件名之前对我们的输入进行编码。
此任务的save_path很有趣。它创建一个名称为 15 个字节的目录和一个文件,文件内容如下:
-
受感染系统的主机名
-
当前日期和时间
-
5 字节扩展
因此,“合法”文件的名称应为:prefix/alice-pc_2024-02-09_16-25-32.txt
。我们恰好用响应数据控制 15 字节前缀和 5 字节扩展名。但是,这两个输入仅允许 5 次目录遍历。即使我们找到了一个不错的写入位置,尽管存在此限制,我们仍只能使用文件名中的日期字符串。
如果您还记得准备阶段,我们会在请求第三阶段时提供操作系统信息。STAGE2
我们可以将没有长度限制或输入验证的主机名设置为我们所需的遍历。我们需要做的就是在 crontab 代码片段中放置一个反向 shell,并使用非常丑陋的名称将我们的文件上传_2024-02-09_16-25-32.aaaaa
到/etc/cron.d/
。
它不起作用。事实证明,Cron 对文件名非常挑剔,它不喜欢我们丑陋的文件中的特殊字符。但是,文件扩展名中仍然有 5 个方便的字节。我们可以将它们设置为/../a
并执行一次遍历。这将在中创建一个空目录和一个字符文件/etc/cron.d/
。我们的save_path最终看起来像这样:
有一些逻辑使这种方法变得复杂。条件save_module_file()
检查保存路径是否存在,如果不存在则创建必要的目录。
由于我们在扩展中有一个额外的路径遍历,因此os.makedirs()
会抛出一个错误。它会尝试创建_2024-02-09_16-25-32.
目录两次:一次用于名称,一次用于最终遍历../
。没什么大不了的。我们只需要在同一秒内发送另一个请求。这将导致条件if not save_path.exists()
返回 true,我们可以将文件a
写入/etc/cron.d/
。
总结
此类漏洞的风险可大大降低,而且,只需付出最小的努力,便可检测到漏洞利用。
进攻队伍
通过实施可防御的 C2 架构,您可以大大降低 C2 服务器被接管的风险。可防御的架构可防止攻击者利用您的基础设施对您进行攻击,并限制成功入侵的影响。
以下内容基于通过 C2 通道利用 C2 服务器的威胁。对于其他载体,还应进行额外考虑。
-
阻止超出范围的 IP 对侦听器的访问。
-
为每个客户建立专用基础设施。如果两个目标具有不同的安全边界,则它们应该具有独特的、隔离的基础设施。
-
实施对 C2 基础设施的监控和检测。
-
每次攻击后彻底销毁 C2 服务器和窃取的数据。
-
在交战期间轮换代理/植入密钥。对于 Empire,这些是暂存密钥和会话密钥。
-
制定计划以快速隔离受到损害的 C2 服务器。
日志应与 C2 服务器分开存储,并在整个交战过程中进行监控。基本日志源包括:
-
C2 服务器 (teamserver) 日志
-
已审核
-
身份验证日志(auth.log)
-
Web 服务器或文件服务器日志(如果您正在提供工具/阶段)
更广泛的威胁是攻击者可能会恢复暂存密钥。限制对侦听器的访问可防止拥有有效密钥的攻击者注册代理/植入程序。如果所有其他方法都失败了,我们可以使用基本检测来检测此类目录遍历攻击。
原文始发于微信公众号(TtTeam):利用 Empire C2 框架
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论