翻译:https://aceresponder.com/blog/exploiting-empire-c2-framework
0X00 前言
这篇文章介绍了利用Empire C2 Framework <5.9.3
的过程,并最终给攻防团队提出了建议。漏洞的 PoC (概念验证) 可以在这里找到。作为一家专门从事现实防御培训的公司,我们对指挥控制(C2)框架的内部机制非常感兴趣。Empire是一个现代化的、开源的框架,配备了广泛的能力。检查其内部工作机制提供了两个机会:首先,改进敌手模拟;其次,开发新颖的防御技术。在审查分发和任务处理过程中,我们发现了攻击者可能利用的几个漏洞,以获得对C2服务器的root访问权限。
2024年2月5日星期一,我们向BC Security报告了这些漏洞。开发团队迅速采取行动解决问题,在24小时内推出了第一版修补程序。到2月9日星期五,BC Security发布了Empire v5.9.3,其中包含了一个经过充分测试的补丁,完全缓解了识别出的漏洞。
0X01 Empire漏洞简介
这些问题的根本原因最早出现在前几年,当时@zeroSteiner向原开发人员披露了一个目录遍历漏洞。他的利用程序被称为Skywalker,利用了Empire代理从受损主机中窃取文件的方式。通过冒充一个代理,他可以”欺骗”C2服务器,使其认为一个有效的代理响应了”下载”任务。由于服务器没有正确处理文件名,zeroSteiner能够遍历文件系统并覆盖关键的系统文件,导致远程代码执行(RCE)。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
Empire的开发者们在随后的版本中充分解决了这些问题。然而,由于基本的更改和升级,路径遍历的漏洞会重新出现。
0X02 Stagers载荷
Empire提供了多种预生成的启动器,涵盖多种语言和格式,如下图所示:
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
我们可以从启动器中提取两个关键信息:分发密钥和C2服务器的URL。C2服务器上的监听器与单个分发密钥相关联。任何想要注册到监听器的代理都必须使用这个密钥进行”认证”。如果我们有了这个密钥,我们实际上就是C2服务器的一个代理。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
0X03 Stagers载荷分发
分发过程Empire的启动器通过三个交易引导一个代理,如下图所示:
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
STAGE0
第一阶段的目的是减少启动器的大小。它存在于加载对第二阶段必需的数据包处理、加密和引导类。对我们来说,这个阶段是不必要的。
STAGE1
我们用有效的分发密钥请求第二阶段。第二阶段用于通过Diffie-Hellman密钥交换过程建立一个新的会话密钥。这也是我们告诉C2服务器我们希望会话ID是什么的地方。
STAGE2
STAGE2请求涉及向C2服务器发送系统信息。当一切按预期工作时,代理会收集用户和操作系统信息,并用新的会话密钥加密。C2服务器响应主代理类。
0X04 Empire任务分配
像大多数C2框架一样,Empire拥有一个任务分配过程。当操作人员与代理互动时,C2服务器在数据库中排队他们的命令。代理定期呼叫C2服务器并请求新的分配任务。如果他们有一个任务等待着,他们将执行指定的动作,并将结果响应给C2服务器。代理对任务的反应以及服务器如何处理响应,是由任务类型决定的。一些任务类型的示例包括:
- TASK_SYSINFO – 更新代理的宿主信息
- TASK_SHELL – 执行一个shell命令
- TASK_UPLOAD – 将文件上传到受害者的系统
- TASK_DOWNLOAD – 从受害者的系统下载文件
服务器处理响应并不要求存在任务分配。换句话说,我们可以通过发送未经请求的“响应”来强制服务器处理任务数据。
0X05 Empire目录穿越(方式一)
在调查服务器如何处理不同任务类型后,我们注意到了TASK_DOWNLOAD路径中一个不祥的评论:
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
此时我们发现了第一个可利用的候选项。如果你还记得STAGE1,代理选择它自己的会话ID。会话ID是8字节长并且未经清洗。这意味着我们可以告诉服务器我们的会话ID应该是./../../
,它会将它追加到download_dir: /<安装路径>/empire/server/downloads/
。
仅凭这些信息,我们就能覆盖任何位于downloads目录上方一级或两级的文件。主要候选文件是empire/server
目录中的server.py和config.yml。我们可以用后门覆盖server.py,或者我们可以在config.yml中更改下载目录为root,然后发送另一个TASK_DOWNLOAD写入到/etc/cron.d/
。然而,这两个文件都是在启动时载入的。所以我们要么需要崩溃C2服务器,要么等待操作人员重启它。我们可以通过社会工程学完成这一点,也许我们可以通过另一种任务类型向操作人员发送假错误,但这并不是一个令人满意的利用路径。
0X06 绕过Skywalker检测
我们能在downloads目录之外创建文件的全部原因归结于这个条件:
safe_path = download_dir.absolute()
if not str(save_file.absolute()).startswith(str(safe_path)):
这个条件检查save_file路径是否以safe_path(/<安装目录>/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()条件。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
但有一种更简单的方法。我们的路径遍历覆盖了save_path,这是因为pathlib如何附加子目录的一个细微差别。如果你附加一个以/
开头的目录,它会用子目录替换整个路径。这个习惯对Empire是有利的。然而,它说明了为什么从输入中去除..不是路径规范化的替代方法。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
我们只需要确保我们的遍历不是以\\
开头,我们就有了一个可靠的利用方法。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
0X07 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与此完全相同,但会在创建文件名之前对我们的输入进行编码。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
这个任务的save_path很有意思。它创建了一个名字有15个字节的目录和一个包含以下内容的文件:
- 受损系统的主机名
- 当前日期和时间
- 一个5个字节的扩展名
因此,一个”合法”的文件会被命名为类似:prefix/alice-pc_2024-02-09_16-25-32.txt。我们碰巧可以通过我们的响应数据控制这15个字节的前缀和5个字节的扩展名。但是,这两个输入只允许5次目录穿越。即使我们找到了一个好地方去写入,尽管有这个限制,我们还是会被文件名中的日期字符串卡住。
如果你还记得分发过程,我们在请求第三阶段STAGE2时提供了操作系统信息。我们可以设置没有长度限制或输入验证的主机名为我们想要的穿越路径。我们所要做的就是把一个反向Shell放在一个crontab片断中,并上传名字非常丑陋的文件:_2024-02-09_16-25-32.aaaaa
到/etc/cron.d/
。但是我们发现这并没有什么作用,原因是Cron对文件名要求很严格,特殊字符并没有起到效果。但是,我们在文件扩展名中仍有合适的5个字节。我们可以将这些设置为/../a
并进行一次更多的穿越。这会在/etc/cron.d/
中创建一个空目录和一个一字符文件。我们的save_path最终会看起来,如下图所示:
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
存在一些逻辑使得这种方法变得复杂。save_module_file()中的一个条件会检查保存路径是否存在,如果不存在则创建必要的目录。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
由于我们在扩展名中有一个额外的路径穿越,os.makedirs()会抛出一个错误。它试图创建_2024-02-09_16-25-32.
目录两次:一次是为了名称,另一次是为了最后的穿越../
。我们只需要在同一秒钟内发送另一个请求。这会使得if not save_path.exists()条件返回真,我们可以将文件a写入到/etc/cron.d/
。
![【翻译】如何反制Empire C2攻击](https://www.ctfiot.com/wp-content/themes/onenav/images/t.png)
0X08 漏洞启示
这种性质的漏洞风险可以大大降低,而且,在失败的情况下,也可通过最小的努力检测到利用行为。
攻击方
攻击方可以通过开发一个可防御的C2架构,可以大大降低C2服务器被接管的风险。一个可防御的C2架构可以防止攻击者利用你的基础设施对抗你,并限制成功入侵的影响。以下基于通过C2渠道利用C2服务器的威胁。应对其他向量做出额外考虑。
- 阻止超出范围IP对监听器的访问
- 单独搭建基础设施
- 针对不同的安全边界做好隔离措施
- 实施C2基础设施的监控和检测
- 每次参与后彻底销毁C2服务器和被窃取的数据
- 在参与之间更换代理/植入物密钥
对Empire而言,这些是分发和会话密钥。制定快速隔离受损C2服务器的计划。日志应该与C2服务器分开存储,并在整个参与过程中进行监控。基本的日志来源包括:
- C2服务器(teamserver)日志
- Auditd认证日志(auth.log)
- Web服务器或文件服务器日志
更广泛的威胁是攻击者可能恢复分发密钥。限制对监听器的访问可以防止拥有有效密钥的攻击者注册一个代理/植入物。如果所有其他方法都失败了,我们可以利用基本的检测措施来检测像这样的目录穿越攻击。
应该根据你的C2设置构建额外的检测也非常值得。我们在最初探索的路径中只能穿越两个目录。因此,Cron检测不适用于所有场景。要覆盖这个向量,我们可以监控对我们C2框架文件的更改,因为它们不应该经常更改。
防御方
红队及其衍生物(假定的入侵、对手模拟等)是一些,如果不是最高,那么是最高投资回报率的安全服务之一。除了发现的漏洞和建议之外,他们还提供了观察现实攻击路径的机会。无论结果如何,你都应该完全了解他们的存在。将其视为真正的事件,并在他们离开后保留检测/指标。
与我们防御的系统相同,路径穿越考虑也适用。就像Empire一样,被大小或字符限制限制的攻击者可能会覆盖服务工作目录中的文件。识别这些服务定期运行的脚本和可执行文件,并监控它们是否被修改是个好主意。保留Web访问和服务日志也很重要,就像Empire一样,这些可能在不太可能的地方揭示路径穿越字符串。