ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Sliver 的 DNS C2 信道协议解析:dnspb 消息绑定与传输机制深入

Sliver 的 DNS C2 信道协议解析:dnspb 消息绑定与传输机制深入 网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载导读本文聚焦 Sliver 对抗仿真框架Adversary Emulation Framework中基于 DNS 的 C2 通信实现以 protobuf/dnspb/README.md 所描述的 DNS 传输消息绑定为骨架结合 dns.proto 协议定义、implant 端 DNS 客户端与 teamserver 端 DNS 监听器的源码实现完整讲解 dnspb 包的消息结构、消息类型语义、会话建立与数据分片传输的完整链路。读完本文你将掌握 Sliver DNS C2 的信道设计原理、消息字段的位掩码复用技巧以及如何通过jobs dns命令启动 DNS 监听器进行实战部署。一、dnspb 包概览DNS 传输的 protobuf 绑定层在 Sliver 的项目结构中protobuf/dnspb 是专门服务于 DNS 传输通道的 protobuf 包。其 README 明确指出Generated DNS transport message bindings. Covers protobuf structures for DNS-based communications. Key modules cover DNS.即该包是由 protoc 自动生成的 DNS 传输消息绑定覆盖所有基于 DNS 的通信所需的 protobuf 结构。该目录下包含两个核心文件dns.proto协议定义的源文件是 dnspb 包的“唯一事实来源”dns.pb.go由protoc-gen-go v1.36.11/protoc v7.36.1生成的 Go 绑定代码见该文件头部的版本注释提供DNSMessage消息类型与DNSMessageType枚举的 Go 结构体、访问器及序列化支持。整个 dnspb 包的设计哲学非常明确DNS 是带宽极受限制的通道因此协议定义极度精简——整个包只有一个枚举、一个消息结构通过字段复用re-purpose来承载不同消息类型的语义。二、DNSMessageType八种消息类型的完整语义dns.proto 定义了DNSMessageType枚举其完整取值如下enum DNSMessageType { NOP 0; // aka FINGERPRINT reserved 1; // formerly TOTP INIT 2; POLL 3; CLOSE 4; MANIFEST 6; DATA_TO_IMPLANT 7; DATA_FROM_IMPLANT 8; CLEAR 9; }各取值的语义与用途可归纳为下表取值名称方向用途0NOP双向空操作同时用作解析器指纹探测FINGERPRINT携带 8 字节随机数据用于 RTT 基准测试与校验1已保留—曾用于 TOTP现已被reserved标记保留防止误用2INITImplant → Server会话初始化承载 Age 密钥交换材料建立加密会话3POLLImplant → Server轮询消息询问服务端是否有待下发的数据4CLOSEImplant → Server会话关闭该值在枚举中定义用于显式终止会话6MANIFESTServer → Implant下发数据的清单通告待读取消息的 ID 与总大小7DATA_TO_IMPLANTServer → Implant服务端向植入体传输数据8DATA_FROM_IMPLANTImplant → Server植入体向服务端传输数据9CLEARImplant → Server通知服务端清理已读取的暂存消息注意取值编号刻意跳过了 5且 1 被reserved保留——这说明该枚举经历过协议演进TOTP 时代遗留的占位符当前实际活跃的消息类型为 7 种。在生成的 dns.pb.go 中这些取值被编译为DNSMessageType_NOP、DNSMessageType_INIT等常量并生成名称/值双向映射表。三、DNSMessage空间敏感场景下的字段复用设计DNS 协议对消息长度极其敏感因此 dns.proto 中的DNSMessage采用了同一组字段、按消息类型复用意涵的设计源文件注释对此做了明确说明/* NOTE: DNS is very space sensitive so certain fields are re-purposed depending on the DNSMessageType. */ message DNSMessage { DNSMessageType Type 1; uint32 ID 2; // 8 bit message id 24 bit dns session ID uint32 Start 3; // Bytes start at uint32 Stop 4; // Bytes stop at uint32 Size 5; // Total size bytes Data 6; // Actual data }各字段的标准含义如下字段类型说明TypeDNSMessageType消息类型决定其他字段如何被解读IDuint32复合标识符低 24 位为 DNS 会话 IDsession ID高 8 位为消息 IDmessage IDStartuint32数据分片起始字节偏移用于分片重组Stopuint32数据分片结束字节偏移Sizeuint32完整消息的总字节数Databytes实际数据载荷3.1 ID 字段的位掩码语义ID字段将两个标识符合并到一个 32 位整数中这在两端实现中均有明确的位掩码常量佐证implant 端implant/sliver/transports/dnsclient/dnsclient.go#L69-L74// Little endian sessionIDBitMask 0x00ffffff // Bitwise mask to get the dns session IDserver 端server/c2/dns.go#L62-L66// Little endian sessionIDBitMask 0x00ffffff // Bitwise mask to get the dns session ID messageIDBitMask 0xff000000 // Bitwise mask to get the message ID合并在客户端与服务端各自通过msgID函数完成uint32(id24) | uint32(s.dnsSessionID)见 dnsclient.go#L1048-L1051以及服务端对应的 dns.go#L147-L149。由于会话 ID 占 24 位理论上存在 16,777,216 个会话 ID 空间server/c2/dns.go#L22-L25 的注释明确指出服务端必须接收到“包含 24 位 DNS 会话 ID 8 位消息 ID 的有效 protobuf”该空间“或许可以被暴力枚举但至少会非常缓慢”——这是服务端防指纹/防滥用设计的一部分。3.2 字段复用实例在真实传输中字段复用随处可见POLL消息仅填充ID会话 ID与 8 字节随机Data作为 nonce见 pollMsg()MANIFEST消息服务端仅填充Type、ID消息 ID、Size待读数据总长见 handlePoll()DATA_TO_IMPLANT分片请求客户端填充ID消息 ID、Start、Stop不含Data见 parallelRecv()NOP指纹消息填充Type与 8 字节随机Data用于生成 CRC32 校验和见 fingerprintMsg()。四、为什么需要分片DNS 信道的物理容量约束DNS 信道之所以采用“小消息 分片 重组”的设计根源在于 DNS 协议本身的长度限制。implant 端源码头部注释dnsclient.go#L22-L40给出了完整的容量推导*** BASE32 *** DNS domains are limited to 254 characters including . so that means Base 32 encoding, so (n*8 4) / log2(32) 63 means we can encode 39 bytes per subdomain. Format: (subdata...).ns domain.parent domain [63].[63]...[ns].[parent]. 254 - len(parent) subdata space, 128 is our worst case where the parent domain is 126 chars, where [63 NS . 63 TLD], so 128 / 63 2 * 39 bytes 78 bytes, worst case per query We need to include some metadata in each request: Type 2 bytes max ID 4 bytes max Start 4 bytes max Stop 4 bytes max Size 4 bytes max Data 78 - (24444) ~ 60 bytes per query worst case核心要点域名总长上限 254 字符含点号每个子域标签最长 63 字符采用Base32 编码每 63 字符可容纳约 39 字节原始数据因为 Base32 字符集是域名合法字符的子集在父域名长达 126 字符的最坏情况下剩余可用于数据编码的空间仅约 128 字符即 2 个子域 × 39 字节 ≈78 字节/次查询扣除 protobuf 序列化的元数据开销Type/ID/Start/Stop/Size 约 18 字节每个查询实际可承载约 60 字节有效载荷。客户端在初始化时即计算subdataSpace见 NewDNSClient()subdataSpace: 254 - len(parent) - (1 (254-len(parent))/64),接收方向同样有容量约定bytesPerTxt在 TXT 模式下为 182 字节“189 with base64, -6 metadata, -1 margin”在notxtAAAA模式下为 192 字节见 parallelRecv()。五、会话生命周期从 INIT 密钥交换到 CLEAR 清理5.1 整体流程服务端视角server/c2/dns.go#L27-L32 用注释完整勾勒了 DNS C2 的流程DNS command and control outline: 1. Implant generates a random DNS Session ID and sends an INIT (Age key exchange) 2. DNS server validates INIT and allocates session state 3. Requests with valid DNS session IDs enable the server to respond with CRC32 responses 4. Implant establishes encrypted session即植入体生成随机会话 ID → 发送 INIT 完成 Age 密钥交换 → 服务端校验并分配会话状态 → 建立加密会话。5.2 客户端会话初始化客户端 SessionInit() 的步骤为加载系统resolv.conf或使用 C2 URI 指定的强制解析器配置见 loadResolvConf()生成本地随机 24 位非零会话 IDrandomDNSSessionIDdnsclient.go#L827-L840。注释特别说明服务端只在成功 INIT 后才分配状态从而避免未认证的会话引导攻击生成对称密钥通过cryptography.AgeKeyExToServer封装密钥交换材料构造INIT消息Type: DNSMessageType_INIT、ID: nextMsgID()、Size: len(initData)见 dnsclient.go#L301-L315将 INIT 数据分片编码为多个子域通过 TXT或 AAAA查询逐片发送服务端返回加密的会话 ID客户端解密后校验与本地会话 ID 一致对所有可用解析器执行指纹基准测试fingerprintResolvers统计 RTT 与错误数剔除出错解析器见 dnsclient.go#L940-L982按WorkersPerResolver默认 2为每个解析器启动并发 worker建立收发队列容量 1024见 dnsclient.go#L365-L379。5.3 服务端会话初始化服务端在 handleDNSSessionInit() 中完成校验与状态分配从msg.ID sessionIDBitMask提取会话 ID拒绝零值与会话已存在的重复 INIT防御性检查由于 INIT 可能被拆分成多条 DNS 查询服务端通过accumulateInitData暂存分片、按Size重组上限defaultMaxDNSInitSize 16 * 1024字节并受defaultMaxPendingDNSInits 1024个未完成 INIT 的硬上限约束见 server/c2/dns.go#L701-L759使用消息数据前 32 字节查询数据库中的植入体公钥摘要db.ImplantBuildByPublicKeyDigest再以 Age 算法解密密钥交换材料派生会话密钥分配DNSSession包含出入站消息队列、FIFO 消息 ID 列表、加密上下文注册到sessions表启动会话发送循环startDNSSessionSendLoop并将加密后的会话 ID 回传植入体。5.4 数据读取POLL → MANIFEST → DATA_TO_IMPLANT → CLEAR客户端 ReadEnvelope() 展示了完整的“拉取”式下行链路构造POLL消息携带 8 字节随机 nonce并发送服务端 handlePoll() 从 FIFO 队列弹出一条待发消息返回MANIFESTTypeMANIFEST, IDmsgID, SizemsgLen若无待发消息则返回Size0的空 MANIFEST客户端解析 MANIFEST 后按bytesPerTxt粒度并行发起多个DATA_TO_IMPLANT分片请求Start/Stop指定字节区间服务端 handleDataToImplant() 通过OutgoingRead按区间切分暂存密文返回客户端在 parallelRecv() 中按Start偏移量将各分片拼装回完整密文解密后proto.Unmarshal为sliverpb.Envelope最后发送CLEAR消息服务端 handleClear() 调用ClearOutgoingEnvelope释放对应暂存缓冲区。5.5 数据上行DATA_FROM_IMPLANT 分片上传客户端 WriteEnvelope() 将sliverpb.Envelope序列化并加密后通过 parallelSend() 构造DATA_FROM_IMPLANT消息经SplitBuffer拆分为多个子域并行发送。服务端 handleDataFromImplant() 将分片插入PendingEnvelope收集完整后异步交给ForwardCompletedEnvelope解密、反序列化并分发给对应的 RPC handler。5.6 NOP 与解析器指纹NOP消息aka FINGERPRINT在会话建立后用于解析器基准测试客户端发送 8 字节随机数据服务端 handleNOP() 回传数据的 CRC32 校验和以 A 记录的 4 字节 IP 形式返回。客户端通过比对校验和与 RTT 统计剔除失效解析器并维持最优解析器集合benchmark()。六、编码与响应封装Base32、Base64 与记录类型6.1 请求编码subdata所有上行请求均遵循同一编码管线encodeDNSMessage() 与 SplitBuffer()proto.Marshal序列化DNSMessageBase32 编码为域名合法字符按每 63 字符切分为子域标签拼接父域名形成完整查询域名joinSubdataToParentdnsclient.go#L895-L913。服务端反向解析在 decodeSubdata() 中完成Base32 解码 →proto.Unmarshal→ 计算 CRC32 校验和注释特别强调校验和基于编码后的数据而非明文避免泄露明文特征。6.2 响应封装按记录类型区分服务端根据请求的 DNS 记录类型Qtype选择响应载体以handleDNSSessionInit与handlePoll为例见 server/c2/dns.go#L849-L889TXT 记录使用自定义字母表的 Base64implantBase64 encoders.Base64{}编码响应数据按MaxTXTLength默认 254切分AAAA 记录将响应数据按 16 字节切块逐块放入 AAAA 记录并把数据长度与块索引编码进 TTL 字段ttl msg_len ^ (chunkIdx 8)A 记录用于回传 4 字节 CRC32 校验和DATA_FROM_IMPLANT、CLEAR、NOP场景。同时服务端在处理响应时设置resp.Compress trueserver/c2/dns.go#L588-L592注释说明这是因为请求 QNAME 常接近最大长度需启用压缩保证 UDP 响应不超出最小 DNS 报文尺寸。七、实战部署启动 DNS 监听器在 Sliver 客户端控制台中通过jobs命令组的dns子命令启动 DNS 监听器。命令定义见 client/command/jobs/commands.go#L89-L103dnsCmd : cobra.Command{ Short: Start a DNS listener, Run: func(cmd *cobra.Command, con *console.SliverClient, args []string) { DNSListenerCmd(cmd, con, args) }, } flags.Bind(DNS listener, false, dnsCmd, func(f *pflag.FlagSet) { f.StringP(domains, d, , parent domain(s) to use for DNS c2) f.BoolP(no-canaries, c, false, disable dns canary detection) f.Uint32P(lport, l, generate.DefaultDNSLPort, udp listen port) })实际使用示例sliver jobs dns --domains example.com,example.net --lport 53参数说明参数缩写默认值说明--domains-d空用于 DNS C2 的父域名多个域名以逗号分隔--no-canaries-cfalse禁用 DNS 蜜标canary检测--lport-lgenerate.DefaultDNSLPort53UDP 监听端口注意 DNSListenerCmd() 会对每个域名自动补全末尾点号例如example.com→example.com.再通过 RPC 调用StartDNSListener创建监听任务成功后会返回任务编号job #N。DNS 监听器同时承担两类职责HandleDNSRequest()C2 请求当查询域名是父域名的子域时进入handleC2消息分发Canary 蜜标当启用 canary 且查询命中数据库中的 canary 域名时handleCanary() 会异步触发CanaryEvent告警事件并更新触发计数——这是 Sliver 用来感知流量是否被外部解析/监控的典型机制。八、资源控制与安全设计DNS 信道天然是“无连接、高延迟、易滥用”的Sliver 服务端为此设计了多层资源护栏常量定义见 server/c2/dns.go#L62-L86常量值作用defaultMaxTXTLength254TXT 响应最大切分长度defaultMaxPendingDNSInits1024未完成 INIT 重组状态的上限防止内存无限增长defaultPendingDNSInitTTL2 分钟未完成 INIT 的过期时间超时被后台 GC 回收defaultPendingDNSInitGCInterval30 秒INIT 过期回收的扫描间隔defaultMaxDNSInitSize16 KB单次 INIT 密钥交换材料的最大尺寸defaultMaxDNSOutgoingMessages128每会话暂存待下发的最大消息数defaultMaxDNSOutgoingBytes16 MB每会话暂存待下发消息的总字节上限dnsMessageIDCount2558 位消息 ID 的可寻址空间其中“待下发消息保留上限”ErrDNSOutgoingLimit的设计意图在源码注释中说明得很清楚植入体必须通过 POLL 与 CLEAR 来领取并释放暂存消息若某个不可达或恶意的植入体不执行领取服务端也不能无限期为其保留出站信封因此达到上限时会主动关闭对应会话见 startDNSSessionSendLoop()。此外消息 ID 是 8 位空间长生命周期会话的计数回绕必须避开仍被暂存占用的 ID服务端通过nextAvailableMsgIDLocked在 255 个候选 ID 中寻找空闲者server/c2/dns.go#L171-L179。九、客户端可调参数与 C2 URI 选项implant 端 DNS 客户端支持通过 C2 URI 查询参数调优参数解析见 ParseDNSOptions()URI 参数默认值说明timeout5s单次 DNS 查询超时retry-wait1s查询失败后的重试等待retry-count3单次查询重试次数workers-per-resolver2每个解析器的并发 worker 数小于 1 时回退默认值max-errors10容忍的最大错误数notxtfalse为 true 时使用 AAAA 记录代替 TXT 记录传输force-resolv-conf空强制指定 resolv.conf 内容resolvers空强制指定解析器地址列表空格分隔支持ip:port显式端口这些参数共同决定了 DNS 信道在高延迟、丢包环境下的稳定性与吞吐表现。十、参考文件导航协议定义protobuf/dnspb/dns.proto、protobuf/dnspb/dns.pb.go包说明protobuf/dnspb/README.mdimplant 端客户端实现implant/sliver/transports/dnsclient/dnsclient.go 及配套测试 dnsclient_test.go服务端监听器实现server/c2/dns.go 及配套测试 dns_test.go客户端命令入口client/command/jobs/dns.go、client/command/jobs/commands.go结语dnspb 包虽然只有“一个枚举、一个消息结构”却是 Sliver DNS C2 信道的核心协议层它以极小的 protobuf 定义配合 Base32/Base64 编码、24 位会话 ID 8 位消息 ID 的位掩码复用、INIT 密钥交换与 POLL/MANIFEST 拉取式传输在 DNS 协议严苛的长度约束下实现了可靠、加密的 C2 通信。理解这一层消息绑定是深入分析 Sliver 网络层架构、排查 DNS 信道问题乃至扩展自定义传输通道的起点。赞分享网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载相关推荐Sliver 植入体 DNS 传输客户端深度解析低慢速 C2 通信的协议设计与实现Sliver 植入体 DNS 传输客户端深度解析低慢速 C2 通信的协议设计与实现 导读 本文以 Sliver 植入端 DNS 传输模块 implant/s网络安全Slack Go库RTM协议深入解析实时消息传输机制终极指南Slack Go库RTM协议深入解析实时消息传输机制终极指南 Slack Go库的RTM协议为开发者提供了强大的实时消息传输能力让应用能够即时接收和发送消息后端API设计即时通讯Klipper 主机与微控制器通信协议深度解析消息声明、二进制编码与传输机制Klipper 主机与微控制器通信协议深度解析消息声明、二进制编码与传输机制 Klipper 主机软件与微控制器固件之间通过一套自定义的二进制消息协议进行底层嵌入式智能硬件工业制造创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表