深入解析Merlin:基于Go与HTTP/2的现代C2框架设计与实战

张开发
2026/4/26 2:50:45 15 分钟阅读

分享文章

深入解析Merlin:基于Go与HTTP/2的现代C2框架设计与实战
1. 项目概述一个用Go写的跨平台C2框架如果你在红队或者渗透测试领域摸爬滚打过一阵子肯定对C2Command Control命令与控制框架不陌生。从老牌的Metasploit Meterpreter到后来火热的Cobalt Strike再到如今百花齐开的Sliver、Havoc每个工具都有其独特的魅力和适用场景。今天我想深入聊聊的是一个我个人在内部测试和某些特定场景下非常偏爱的一个选择Merlin。简单来说Merlin是一个用Go语言编写的、跨平台的、基于HTTP/2协议的后渗透阶段C2服务器和代理Agent。它的核心卖点非常明确轻量、快速、模块化并且原生拥抱现代协议。不像一些庞然大物需要复杂的Java环境或者沉重的图形界面Merlin的服务器和客户端都是独立的二进制文件开箱即用部署极其简单。更关键的是它选择HTTP/2以及更新的HTTP/3作为主要通信协议这不仅仅是追赶潮流而是在实战对抗中有着实实在在的优势——更好的性能、更低的延迟以及更容易混迹于正常的Web流量中。这个项目适合谁呢首先当然是从事渗透测试、红队演练的安全研究人员和工程师。如果你厌倦了某些商业工具的黑盒感或者需要一款高度可定制、能集成到自己自动化流程中的C2Merlin值得你花时间研究。其次对于蓝队和防御方而言理解Merlin这样的现代C2框架的工作原理、通信特征和检测方法对于构建更有效的威胁狩猎能力至关重要。最后对于Go语言开发者或对安全工具开发感兴趣的朋友Merlin的代码结构清晰文档相对完善是一个非常好的学习样本。2. 核心架构与设计哲学解析2.1 为什么是Go语言与HTTP/2Merlin选择Go语言作为实现语言这背后有非常务实的考量。Go编译生成的是静态链接的单一二进制文件依赖项极少跨平台编译Windows、Linux、macOS异常简单。这意味着你可以在一台Linux开发机上轻松编译出能在所有主流操作系统上运行的Agent和Server极大简化了武器化的流程。同时Go语言在并发处理Goroutine和网络编程方面的原生优势使得构建一个高并发、稳定的C2服务器变得相对轻松。而将HTTP/2作为默认的C2协议则是Merlin在对抗层面做出的一个关键决策。相比传统的HTTP/1.1HTTP/2有几个对C2通信极为有利的特性多路复用Multiplexing在一个TCP连接上可以同时进行多个请求和响应且互不干扰。这意味着一个Agent可以同时上传文件、下载任务、执行命令而无需建立多个连接大大减少了连接建立的握手开销和暴露的风险。头部压缩HPACKHTTP/2使用HPACK算法压缩请求和响应的头部信息。对于C2这种需要频繁发送心跳、状态更新的小数据包通信能有效减少网络流量使得通信特征更不明显。二进制分帧层HTTP/2将通信数据分解为更小的“帧”并以二进制格式传输。这比HTTP/1.1的文本格式更高效也使得流量分析工具如Wireshark无法直接以明文方式解析出HTTP头部增加了简单检测的难度。服务器推送Server Push虽然Merlin的Agent- Server模式不一定用到推送但这个特性为未来可能的“Server主动向Sleep中的Agent发送唤醒指令”等高级功能提供了协议基础。Merlin还支持HTTP/1.1明文和TLS、HTTP/2明文h2c以及前沿的HTTP/3基于QUIC。这种灵活性允许操作者根据目标网络环境例如是否严格审查TLS握手、是否支持UDP的QUIC选择最隐蔽的通信渠道。2.2 模块化与可扩展性设计Merlin的架构体现了清晰的模块化思想这从它的代码仓库组织就能看出来。核心的merlin仓库是服务器和主逻辑而merlin-agent、merlin-agent-dll、merlin-cli、merlin-message等都是独立的仓库。这种分离带来了几个好处关注点分离Agent的代码独立便于单独进行免杀处理、功能裁剪或定制开发。DLL形式的Agent则为进程注入、持久化提供了另一种载体。接口标准化merlin-message作为一个独立的Go库定义了Server和Agent之间交换消息的格式。任何理解此格式的客户端理论上都可以与Merlin Server通信这为第三方工具集成或自定义Agent开发提供了可能。CLI与Server分离merlin-cli通过gRPC与Server通信。这意味着Server可以作为一个无头服务Headless Service运行在某个跳板机上而多个操作员可以在各自的工作站上使用CLI同时连接、协作操作。这种多用户支持对于团队作战至关重要。这种设计哲学使得Merlin不像一个 monolithic单体的庞然大物而更像一个乐高积木套装。你可以按需取用也可以基于它的接口搭建属于自己的独特组件。3. 核心功能深度剖析与实战应用3.1 灵活的通信与隐蔽机制Merlin在通信的隐蔽性上下了不少功夫提供了多种可配置的“变换”Transform和“填充”Padding选项。数据变换TransformsAgent在发送数据前可以对载荷Payload进行多层编码和加密。支持的算法包括AES对称加密强度高是默认的推荐选择。RC4/XOR流加密速度极快在资源受限的环境或需要快速加解密的场景下有用。Base64/Hex/Gob编码方式常用于将二进制数据转换为可打印字符以绕过某些基于内容检测的简单规则。JWEJSON Web Encryption这是一个非常有意思的特性。Merlin使用PBES2_HS512_A256KW方案简单说就是用一个基于口令的密钥派生函数生成密钥再用AES密钥封装Key Wrap来加密内容。这提供了一种非对称的密码学感觉但实际上核心还是对称加密其优势在于密钥管理更灵活。在实际配置监听器Listener时你可以像搭积木一样组合这些变换例如AES - Base64 - XOR。顺序很重要因为Agent会按相反顺序解码。这种链式变换能有效对抗网络层简单的特征匹配。消息填充Padding固定的心跳包大小是许多C2 Beacon被检测的致命弱点。Merlin允许为消息配置随机填充。你可以设置一个范围比如-padding 500-1500这样每个Agent发送的消息体长度都会在这个范围内随机变化使得基于固定包大小的流量检测模型失效。JA3指纹动态修改JA3是一种对TLS握手过程进行指纹识别的方法被广泛用于威胁情报和流量监控。Merlin Agent能够动态修改自己的JA3指纹模拟成常见浏览器如Chrome、Firefox或其它软件的握手特征从而更好地融入正常的HTTPS流量中。这个功能需要Server在生成Agent时预先配置好目标指纹。3.2 多样的载荷执行与内存操作能力后渗透的核心是执行代码。Merlin提供了多种在目标内存中执行代码的方式尽可能避免文件落地。Shellcode执行支持多种注入技术CreateThread在当前进程内创建线程执行。最简单但也最容易被针对当前进程的检测抓到。CreateRemoteThread注入到另一个远程进程中执行经典的进程注入方法。RtlCreateUserThread另一种远程线程创建API可能用于绕过某些简单的CreateRemoteThread钩子Hook。QueueUserAPC使用异步过程调用APC将代码注入到目标线程中执行常用于“傀儡进程”注入等高级技巧。 选择哪种技术需要根据目标进程的安全产品EDR/AV监控强度、目标进程的权限和稳定性来综合决定。.NET程序集与PE文件执行invoke-assembly使用类似ExecuteAssembly的技术将.NET程序集.dll或.exe直接加载到Agent进程的AppDomain中执行完全无文件。适合执行Sharphound、Seatbelt这类.NET工具。execute-assembly在一个新创建的、牺牲性的sacrificial子进程中执行.NET程序集。子进程崩溃不影响主Agent进程更稳定。execute-pe直接执行原生的Windows PE文件.exe。Merlin会先在内存中映射PE文件修复导入地址表IAT然后创建一个挂起的牺牲进程将PE映像注入其中并恢复执行。这是运行像Mimikatz这样的原生工具的关键。集成工具支持Merlin内置了对几个流行内存操作工具的集成Donut可以将.NET程序集或PE文件转换成位置无关的Shellcode。Merlin集成后可以方便地生成Donut Shellcode再通过上述Shellcode执行方法运行。sRDI将DLL转换成反射加载Reflective DLL的Shellcode。用于运行那些原本需要LoadLibrary的DLL工具。SharpGen用于生成混淆后的.NET执行程序可以一定程度上绕过静态查杀。实操心得在实战中execute-assembly和execute-pe是我最常用的功能。尤其是execute-pe它解决了在内存中直接运行复杂原生工具的难题。但需要注意不是所有PE都能完美地在内存中运行特别是那些严重依赖外部文件或特定环境变量的程序。最好先在测试环境中充分验证。3.3 点对点P2P通信与横向移动Merlin支持Agent之间的P2P通信这是进行网络横向移动和构建跳板网络的关键。它支持绑定Bind和反向Reverse两种模式以及SMB、TCP、UDP三种协议。反向连接Reverse最常用。比如你已经控制了一台Web服务器Agent A想通过它去访问内网中另一台无法直接出网的数据库服务器。你可以在数据库服务器上运行一个Merlin AgentAgent B配置它反向连接到Agent A的某个端口。这样所有发给Agent B的指令都通过Agent A中转。绑定连接BindAgent B在本地监听一个端口等待Agent A主动连接。这在某些严格的出站限制环境下可能有用但风险较高因为会在内网机器上开放端口。协议选择SMB在Windows域环境中极其隐蔽因为SMB流量445端口是内网中最常见的流量之一。Merlin可以伪装成正常的SMB通信。TCP/UDP更通用的协议配置简单。P2P功能的强大之处在于你可以构建一个多级的、冗余的C2通道。即使边缘的Agent失联只要内网的P2P网络还在你仍然可能通过其他路径保持控制。4. 从零开始部署与实战操作指南4.1 环境准备与快速启动假设我们在一个Linux攻击机Kali或自建VPS上进行部署。下载与解压# 创建工作目录 mkdir -p /opt/merlin cd /opt/merlin # 下载最新版Merlin Server请始终从官方Release页面获取最新链接 wget https://github.com/Ne0nd0g/merlin/releases/latest/download/merlinServer-Linux-x64.7z # 使用7z解压密码是 merlin 7z x merlinServer-Linux-x64.7z -pmerlin解压后你会看到merlinServer-Linux-x64可执行文件和data/目录。data/bin/下包含了所有平台预编译好的CLI和Agent。启动Server# 直接运行默认监听0.0.0.0:443https和0.0.0.0:80http sudo ./merlinServer-Linux-x64 # 或者指定接口和端口 sudo ./merlinServer-Linux-x64 -i 10.0.0.10 -lp 8443 -lps 8080-i指定监听IP-lp指定HTTPS端口-lps指定HTTP端口。首次运行会生成自签名的TLS证书。启动CLI并连接 新开一个终端。cd /opt/merlin ./data/bin/merlinCLI-Linux-x64进入CLI后使用connect命令连接到本地的Server如果Server运行在默认端口Merlin» connect 127.0.0.1:443如果Server使用了自签名证书这里会提示证书验证错误需要根据提示选择是否继续。4.2 配置监听器与生成Agent成功连接后我们进入主菜单。首先需要创建一个监听器让Agent能够回连。创建HTTP/2监听器Merlin» listeners Merlin[listeners]» use http2 Merlin[listeners][http2]» set Name MyH2Listener Merlin[listeners][http2]» set Host 10.0.0.10 # 你的公网VPS IP或域名 Merlin[listeners][http2]» set Port 443 Merlin[listeners][http2]» set PSK changeme # 预共享密钥用于初步认证务必修改 Merlin[listeners][http2]» set Transform aes-256-gcm-base64-jwe # 设置加密编码链 Merlin[listeners][http2]» set JA3 Firefox_65 # 模拟Firefox 65的JA3指纹 Merlin[listeners][http2]» set Padding 500-2000 # 设置随机填充 Merlin[listeners][http2]» start监听器就创建好了在443端口等待HTTP/2的Agent连接。生成Agent 回到主菜单使用agent命令生成Agent。Merlin» agent Merlin[agent]» generate你会进入一个交互式配置界面Listener: 选择刚才创建的MyH2Listener。AgentOS: 选择目标系统如windows。AgentArch: 选择架构如amd64。AgentFormat: 选择输出格式。exe是独立可执行文件service-exe是Windows服务格式dll是动态库。Sleep: 设置心跳间隔如30s30秒。Skew: 设置心跳间隔的随机偏移量如5±5秒让心跳时间不固定。MaxRetry: 连接失败重试次数。Transform: 这里可以覆盖监听器的默认设置。Username/Password: 如果监听器配置了OPAQUE认证这里需要设置。配置完成后Merlin会生成Agent二进制文件并显示保存路径通常是在data/agent/目录下。这个文件就是你要投放到目标主机上的“木马”。4.3 Agent上线与基础操控将生成的Agent例如merlin-agent-windows-amd64.exe通过某种方式鱼叉邮件、漏洞利用、社工等投放到目标Windows主机并执行。回到Merlin CLI稍等片刻取决于你设置的Sleep时间在agents菜单下就能看到上线的Agent了。Merlin» agents Merlin[agents]» list你会看到一个表格包含Agent的ID、主机名、用户名、IP、进程ID、操作系统和最后上线时间。与Agent交互Merlin[agents]» interact Agent_ID Merlin[Agent_ID]»现在你进入了该Agent的交互式会话。可以执行各种命令shell whoami /all执行系统命令。pwd/cd查看和切换Agent的工作目录。ls/dir列出文件。download 远程文件路径从目标下载文件。upload 本地文件路径上传文件到目标。ps列出进程。kill pid结束进程。sleep 60修改该Agent的心跳间隔为60秒。exit退出Agent交互模式不会杀死Agent进程。5. 高级技巧、问题排查与防御思考5.1 实战中的高级配置技巧域名前置Domain Fronting与CDN隐匿Merlin的HTTP/2监听器可以很好地与域名前置技术结合。你可以在云服务商如AWS CloudFront、Azure CDN配置一个CDN将流量转发到你真实的Merlin Server IP。Agent在通信时设置HTTP Host头为CDN的域名但实际TCP连接指向另一个允许的域名。许多网络过滤设备只检查TLS SNIServer Name Indication或HTTP Host头这使得流量看起来是发往合法云服务的。在Merlin中你需要相应设置监听器的Host头CDN域名和实际的服务器证书。多监听器与负载均衡可以创建多个不同协议、不同端口的监听器例如一个HTTP/2 on 443一个HTTP/3 on 443 UDP。让Agent随机或按顺序尝试连接增加稳定性和抗封锁能力。这需要在生成Agent时配置多个-url参数。利用P2P构建冗余通道在拿下第一台边界机器后不要只依赖它直接出网。立即在内网部署1-2个Agent并配置它们通过SMB或TCP反向连接到边界Agent。这样即使边界Agent被发现清除你仍然可以通过内网的其他节点尝试寻找新的出网点。内存规避在执行invoke-assembly或execute-pe时结合sleep和jitter参数让操作在时间上分散开避免短时间内产生大量的内存分配和CPU活动从而触发EDR的行为检测。5.2 常见问题与排查实录Agent编译后无法运行提示“不是有效的Win32应用程序”原因很可能是在Linux上交叉编译Windows Agent时没有正确设置CGO_ENABLED0。Go的某些特性如使用C库需要CGO但在交叉编译静态二进制文件时通常需要禁用它。解决在编译Agent时明确设置环境变量CGO_ENABLED0 GOOSwindows GOARCHamd64 go build -o agent.exe ...。或者直接使用Merlin官方Release中预编译好的二进制文件。Agent上线后很快失联或命令执行无回显排查网络首先在Server端用tcpdump或Wireshark抓包看Agent是否真的在发送心跳请求。检查防火墙、云安全组是否放行了相应端口。排查Agent进程如果可能在目标机上查看Agent进程是否存活是否被安全软件终止。可以尝试为Agent进程起一个具有迷惑性的名字或者将其注入到一个合法进程如explorer.exe中。检查Transform配置确保Server监听器和Agent使用的加密、编码链Transform完全一致且顺序正确。这是最常见的配置错误之一。一个字符都不能差。execute-assembly执行.NET工具时崩溃原因目标机器可能缺少对应的.NET Framework运行时或者工具本身依赖特定版本的.NET。解决首先使用shell命令检查目标机的.NET版本dir %WINDIR%\Microsoft.NET\Framework\。对于.NET Framework工具尝试使用execute-assembly的-runtime参数指定版本。对于.NET Core/5的工具确保目标机已安装相应运行时或者考虑使用donut将其转换为Shellcode再执行兼容性更好。CLI连接Server时报证书错误原因Server默认使用自签名证书CLI不信任。解决在connect时根据提示输入y接受风险。对于生产环境强烈建议使用受信任的CA签发的证书或者使用-cert和-key参数启动Server时指定你自己的证书。这不仅能避免警告也是隐蔽性要求自签名证书特征明显。5.3 从防御视角看Merlin的检测点了解攻击工具才能更好地防御。对于防御者蓝队来说可以从以下几个层面关注Merlin的潜在检测点网络流量特征JA3指纹虽然Merlin可以修改但默认或配置不当的JA3指纹可能与其声称的浏览器不符。持续监控和建立JA3白名单模型很重要。HTTP/2滥用检查HTTP/2流量是否来自非浏览器进程如svchost.exe,rundll32.exe。企业环境中由非浏览器进程发起的、持续的HTTP/2长连接是高度可疑的。固定心跳与填充模式即使有随机填充长时间观察下特定源IP到特定目的IP/端口的、周期性的、数据包大小在一定范围内随机波动的HTTP/2 POST请求仍然是一个强信号。证书异常自签名证书或证书主题与域名不匹配。主机行为特征进程注入CreateRemoteThread,QueueUserAPC等API的调用是EDR的重点监控对象。特别是从msiexec.exe、regsvr32.exe等 LOLBAS 进程发起的远程线程注入。反射加载与内存模块检测进程内存中是否存在未在磁盘文件映射中出现的PE映像或.NET程序集。这是execute-pe和invoke-assembly的典型痕迹。可疑的网络连接进程与外部IP的443端口建立长连接但该进程并非浏览器或常见的云服务客户端。初始访问与执行Agent投递方式鱼叉邮件附件、漏洞利用的Payload。加强终端防护和邮件网关检测。Agent持久化Merlin Agent本身支持多种持久化方式如注册表Run键、服务、计划任务。监控这些持久化位置的异常修改。Merlin作为一个活跃的开源项目其对抗手段和防御方的检测能力都在不断演进。对于红队深入理解其原理灵活组合各种功能并针对目标环境进行定制化改造是发挥其最大威力的关键。对于蓝队则需构建多层防御体系从网络流量、主机行为、进程血缘等多个维度进行关联分析才能有效发现和遏制此类威胁。

更多文章