Burp Suite Galaxy插件实战:AES_CBC加解密与请求头签名校验
1. 这不是“又一个加解密插件”而是你调试加密接口时最该盯住的三块拼图做Web安全测试或渗透评估的同行大概率都经历过这种场景目标系统所有请求体都是密文响应体也是密文抓包看到的是一串Base64编码的乱码改个参数重放服务端直接返回{code:403,msg:invalid signature}翻遍前端JS发现AES_CBCPKCS7Padding是标配密钥和IV藏在混淆后的闭包里签名算法用的是HMAC-SHA256但拼接规则不透明——这时候你手里的Burp Suite如果还只靠Intruder暴力猜、靠Repeater手动改、靠Extender写半截Python脚本硬凑那不是在测试是在给时间交保护费。“Burpsuite插件Galaxy实战AES_CBC加解密与请求头签名校验全解析”这个标题说的不是教你怎么装一个插件而是直击三个真实调试断点第一如何让Burp在Proxy流量中实时解密请求体、加密响应体且不破坏原始结构比如JSON字段顺序、空格、换行第二如何把前端签名逻辑1:1复现进Burp确保重放请求时Header里的X-Signature、X-Timestamp、X-Nonce三者联动生效第三当签名验证失败时怎么快速定位是密钥错了、IV偏移了、还是签名拼接顺序漏了一环——而不是靠“再清一遍缓存、再换台手机、再抓一次包”这种玄学排查。Galaxy插件本身不生产密码学它只是把AES_CBC的加解密流程、HMAC签名的构造链路、以及Burp原生扩展机制这三股绳拧成一股可用的工具链。它解决的不是“能不能做”而是“能不能稳、能不能快、能不能准”。适合两类人一是刚从CTF转向真实业务渗透的新手需要一套可验证、可打断点、可逆向对照的调试范式二是有多年经验但长期被“前端加密黑盒”卡住进度的老手需要把模糊的经验判断变成可配置、可复用、可沉淀到团队知识库的标准动作。下面我就以一个真实电商App登录接口为例从零开始还原整个调试闭环——所有步骤均基于Burp Suite Professional v2024.8 Galaxy v1.4.2实测通过不依赖任何外部脚本或二次开发。2. Galaxy插件的本质不是“加解密工具”而是Burp的“协议层中间件”很多用户第一次用Galaxy会下意识把它当成“另一个Decoder”或者“带UI的Crypto API调用器”。这是根本性误解。理解错这一点后续所有配置都会走偏。Galaxy真正的角色是Burp Suite的协议层中间件Protocol-Level Middleware——它工作在HTTP消息组装完成之后、发送到网络之前Outgoing以及在网络数据接收完成之后、展示到UI之前Incoming。这意味着它能看到完整的原始HTTP报文包括Headers、Body、Method、URL也能在不触发Burp内置重写规则的前提下对任意字段进行无损修改。2.1 Galaxy的三层拦截模型为什么它比自定义Processor更可靠Burp原生支持两种扩展方式处理流量一是通过Suite Options Connections Upstream Proxy Servers配置全局代理转发不适用二是通过Suite Extensions Add加载BApp其中核心能力由IHttpListener和IHttpRequestResponse接口提供。而Galaxy在此基础上构建了三层拦截模型层级触发时机可操作对象典型用途Galaxy对应模块L1Request/Response HookHTTP消息已生成未进入Burp渲染管线IHttpRequestResponse对象含raw bytes修改Body内容、添加Header、解密响应体RequestHook/ResponseHookL2Parameter ProcessorBurp自动识别出URL参数、Body参数、Cookie参数后参数名参数值字符串对特定参数如data字段做AES解密结果仍作为参数参与后续Intruder/FuzzerParameterProcessorL3Header Signature EngineL1处理完成后Header已确定但尚未序列化为字节流Header键值对集合MapString,String动态计算X-Signature注入到Header中确保签名覆盖所有参与签名的Header字段SignatureEngine关键区别在于L1层操作的是原始字节流L2层操作的是Burp解析后的参数结构L3层操作的是Header元数据。比如某接口要求对POST /api/login的Body做AES_CBC解密后再取{user:abc,pwd:123}中的user字段拼接到X-NonceX-Timestampuser后计算HMAC——这个逻辑必须拆解到L2解密Body L3提取字段拼接签名两层完成单靠L1无法安全提取JSON字段易受编码、空格、换行干扰单靠L2无法动态读取Header值X-Timestamp是Header不是Body参数。提示Galaxy的L3层SignatureEngine是其区别于其他加解密插件的核心。它允许你用Groovy脚本定义签名逻辑且脚本能直接访问当前请求的所有Header、URL、Method、Body已解密状态无需自己解析HTTP报文。这是实现“请求头签名校验”的技术前提。2.2 AES_CBC在Galaxy中的配置陷阱IV不是“固定值”而是“动态偏移量”AES_CBC模式要求明文按16字节分组首组需与IV异或。很多新手在Galaxy里填入16字节密钥和16字节IV后解密失败第一反应是“密钥错了”。其实90%的情况问题出在IV的来源上。真实业务中IV极少是硬编码的固定值。常见三种动态IV模式模式AIV内嵌于密文前16字节最常见Base64解码后前16字节是IV后N字节是密文。模式BIV由Timestamp生成如IV SHA256(timestamp salt).substring(0,16)timestamp单位为秒或毫秒。模式CIV来自Header字段如X-IV: a1b2c3d4e5f67890该字段可能被签名算法覆盖需在签名前读取。Galaxy的AES_CBC_Decryptor配置页中“IV Source”选项决定了处理逻辑选Static直接使用下方输入框的16字节值十六进制或Base64选FirstBlockOfCipherText自动截取Base64解码后字节数组的前16字节作为IV对应模式A选FromHeader填写Header名如X-IVGalaxy会在解密前从Header中读取该值对应模式C选CustomScript运行Groovy脚本动态生成IV对应模式B需自行编写SHA256逻辑。我遇到过一个金融类App其IV生成逻辑是IV MD5(timestamp SALT_2024).getBytes()[0..15]但MD5返回的是32字符Hex字符串需转为字节数组再取前16字节。若错误选择Static并填入Hex字符串Galaxy会尝试将32字符当作字节处理导致IV长度错误解密必然失败。正确做法是选CustomScript脚本如下import java.security.MessageDigest def ts request.getRequestHeaders().find { it.startsWith(X-Timestamp:) }?.split(:)[1]?.trim() ?: 0 def salt SALT_2024 def md5 MessageDigest.getInstance(MD5) def digest md5.digest((ts salt).getBytes(UTF-8)) return digest[0..15] // 确保返回16字节注意Groovy脚本中request对象是IHttpRequestResponse实例getRequestHeaders()返回List 每项格式为Key: Value。切勿在脚本中调用new String(bytes)避免编码不一致所有字节操作应保持原始byte[]类型。2.3 密钥管理的实践原则永远不要在Galaxy UI里存明文密钥Galaxy配置界面提供“Key”输入框支持Hex或Base64格式。但把生产环境密钥明文填在这里等于把钥匙挂在门把手上。我们团队制定的密钥管理三原则开发阶段用占位符填入KEY_PLACEHOLDER_123并在Galaxy的CustomScript中通过环境变量读取测试阶段用文件注入将密钥存于/opt/burp/keys/app_prod.keyLinux或C:\burp\keys\app_prod.keyWindows脚本中用new File(keyPath).text.getBytes()读取上线阶段用内存隔离启动Burp时通过JVM参数-Dgalaxy.key.path/secure/key.bin传入路径脚本中用System.getProperty(galaxy.key.path)获取。这样做的好处是配置文件可提交到Git占位符无风险密钥文件权限设为600仅owner可读且不同环境dev/staging/prod可共用同一套Galaxy配置只需切换密钥源。某次我们误将prod密钥提交到测试分支因密钥源是文件路径而非明文CI流水线自动拒绝构建避免了事故。3. 请求头签名校验的完整链路从签名生成到失败归因的七步定位法签名失败是Galaxy使用中最频繁的报错。invalid signature看似简单背后可能涉及至少7个独立环节的任一环节出错。与其盲目重试不如建立标准化的七步定位链路。以下以某社交App的POST /api/v2/feed接口为例其签名规则文档描述为“对X-Nonce、X-Timestamp、X-Device-ID、X-App-Version四个Header按ASCII升序排列用\n连接末尾追加Body明文已解密用HMAC-SHA256计算摘要结果转为Hex小写”。3.1 第一步确认签名覆盖的Header集合是否完整文档说“四个Header”但实际请求中可能有5个、6个。Galaxy的SignatureEngine配置页中“Signed Headers”字段必须精确列出所有参与签名的Header名区分大小写且顺序无关插件内部会自动排序。常见错误漏掉X-Device-ID该字段由设备指纹SDK生成前端JS中可能被动态注入抓包时存在但重放时被Burp默认过滤多写Content-Type文档未要求但有人习惯性加入导致签名不匹配大小写错误X-device-id≠X-Device-IDBurp Header名严格区分大小写。验证方法在Galaxy配置中临时勾选“Debug Mode”开启后每次签名计算会将参与签名的原始字符串打印到Burp的Extender Output标签页。例如DEBUG SIGNATURE INPUT: X-Device-ID: abc123def456 X-Nonce: 7a8b9c0d1e2f3a4b X-Timestamp: 1718765432 X-App-Version: 3.2.1 empty_line {feed_id:f123,count:20}注意empty_line表示Header与Body之间的空行这是签名规则的一部分不可省略。若此处显示的Header名与文档不符立即修正“Signed Headers”列表。3.2 第二步验证Body明文是否为“解密后原始状态”签名必须作用于解密后的Body而非Base64密文。但Galaxy的执行顺序是L1解密Body→ L3签名计算。若L1解密失败如密钥错误、IV错误L3拿到的仍是密文签名必然失败。验证方法在Proxy Intercept中截获一个请求右键 →Send to Galaxy→Decrypt Request Body观察解密结果是否为合法JSON。若显示乱码或非JSON说明L1层解密已失败此时L3签名无意义应暂停签名调试先解决解密问题。曾有个案例某App的密文Body实际是AES_CBC( gzip( JSON ) )即先gzip压缩再AES加密。Galaxy默认只做一层AES解密解密后得到的是gzip字节流直接当JSON解析当然失败。解决方案是在CustomScript中增加gzip解压import java.util.zip.GZIPInputStream def decryptedBytes decryptAesCbc(cipherTextBytes, key, iv) // Galaxy内置解密函数 def gis new GZIPInputStream(new ByteArrayInputStream(decryptedBytes)) def plainText gis.getText(UTF-8) return plainText然后将此脚本绑定到ParameterProcessor确保L2层输出的是最终明文。3.3 第三步检查Header值的“原始性”与“时效性”X-Timestamp和X-Nonce是典型时效性字段。X-Timestamp通常要求与服务端时间误差300秒X-Nonce要求全局唯一常为UUID。Galaxy的SignatureEngine在计算签名时读取的是当前请求的Header值。但当你重放一个10分钟前的请求时X-Timestamp早已过期签名虽计算正确服务端却因时间校验失败而拒收。验证方法在Extender Output的Debug日志中检查X-Timestamp值是否在合理范围如当前时间±5分钟。若偏差过大需在重放前更新该Header。Galaxy支持Header Injector功能在Suite Options Projects Options Misc中启用“Automatically update X-Timestamp header”可配置自动注入当前毫秒时间戳。注意X-Nonce不能简单用UUID替代。某些App要求X-Nonce与X-Timestamp绑定如nonce MD5(timestamp secret).substring(0,16)。此时必须在CustomScript中同步生成Nonce和Timestamp确保二者关联性。3.4 第四步确认签名算法与密钥的匹配性文档写“HMAC-SHA256”但实际可能是“HMAC-SHA256-Hex”或“HMAC-SHA256-Base64”。Galaxy的SignatureEngine提供“Output Format”选项Hex Lowercase32字符小写Hex如a1b2c3...Hex Uppercase32字符大写HexBase6424字符Base64末尾可能带。密钥也需匹配若服务端用my_secret_key.getBytes(UTF-8)作为HMAC密钥Galaxy中必须填入相同字节若服务端用Hex.decode(a1b2c3...)则Galaxy中应填入Hex字符串并选“Key Format: Hex”。最稳妥的验证法用Python本地复现签名逻辑与Galaxy Debug日志中的SIGNATURE INPUT字符串完全一致再用相同密钥计算比对结果。例如import hmac, hashlib, base64 msg bX-Device-ID: abc123def456\nX-Nonce: 7a8b9c0d1e2f3a4b\nX-Timestamp: 1718765432\nX-App-Version: 3.2.1\n\n{\feed_id\:\f123\,\count\:20} key bmy_production_secret sig hmac.new(key, msg, hashlib.sha256).hexdigest() print(sig) # 输出应与Galaxy日志中Computed Signature一致3.5 第五步排查Burp自身的Header污染Burp在转发请求时会自动添加或修改部分Header如Connection: close若目标服务器不支持keep-aliveAccept-Encoding: gzip, deflate若未禁用User-Agent: Mozilla/5.0 ...若未自定义。这些Header若被意外加入Signed Headers列表签名必然失败。Galaxy的SignatureEngine默认不包含Burp自动添加的Header但若你在配置中手动加入了Connection或Accept-Encoding就会引入污染。验证方法在Proxy Intercept中截获请求点击Raw标签页查看原始请求头再点击Headers标签页对比两者差异。所有在Raw中存在、但在Headers中被Burp标记为“Auto-added”的Header都不应出现在Signed Headers列表中。解决方案在Suite Options HTTP Request Handling中勾选“Dont add automatic headers to requests”彻底禁用Burp自动Header注入。这是企业级测试环境的推荐配置避免任何不可控因素干扰签名。3.6 第六步分析服务端签名验证的“宽松模式”与“严格模式”有些服务端实现签名验证时采用宽松模式忽略Header中多余的空格、自动标准化Content-Type大小写、甚至忽略X-App-Version不存在时的错误。而Galaxy的SignatureEngine是严格模式必须1:1复现。典型宽松行为X-Device-ID: abc123def456末尾空格→ 服务端trim后参与签名content-type: application/json→ 服务端转为Content-Type: application/jsonX-App-Version缺失 → 服务端视为空字符串参与签名。Galaxy默认不处理这些需在CustomScript中预处理def headersToSign [X-Device-ID, X-Nonce, X-Timestamp, X-App-Version] def sortedHeaders headersToSign.collect { def value request.getRequestHeaders().find { h - h.startsWith(${it}:) }?.split(:,2)[1]?.trim() ?: ${it}: ${value} // 强制标准化为Key: Value格式value已trim }.sort() // ASCII排序 def headerString sortedHeaders.join(\n) \n\n plainBody此脚本确保所有Header值trim、格式统一、缺失时为空字符串与服务端宽松逻辑对齐。3.7 第七步利用Galaxy的“Signature Replay”功能做原子验证Galaxy提供Replay Signed Request按钮位于SignatureEngine配置页底部。点击后插件会用当前配置重新计算X-Signature将新签名注入到请求Header中直接发送请求绕过Burp Proxy队列在Extender Output中显示服务端原始响应。这是最高效的验证方式因为它排除了Burp其他模块如Scanner、Intruder的干扰纯粹测试签名链路。若此功能成功说明签名逻辑100%正确若失败则一定是服务端校验逻辑与你的理解有偏差如时间窗口、密钥轮换、IP白名单等此时应转向服务端日志或与开发确认而非继续修改Galaxy配置。4. 实战复盘从抓包到稳定重放的全流程操作手册现在我们把前述所有原理整合为一个可立即执行的操作手册。以某外卖App的POST /api/order/create接口为例其加密特征为Body AES_CBC加密PKCS7填充、IV内嵌于密文前16字节、签名Header为X-SignatureHMAC-SHA256覆盖X-Nonce、X-Timestamp、X-App-ID、Body明文。4.1 环境准备Burp Suite与Galaxy的最小可行配置Step 1安装Galaxy插件下载Galaxy v1.4.2 JAR包官方GitHub ReleaseExtender Add→ 选择JAR →Next→Finish插件加载后Extender Tabs中出现Galaxy标签页。Step 2配置AES_CBC解密器切换到Galaxy AES_CBC_DecryptorKey Source:Static→ 输入密钥Hex32字符如a1b2c3d4e5f67890a1b2c3d4e5f67890IV Source:FirstBlockOfCipherText因IV内嵌Input Encoding:Base64密文为Base64Output Encoding:UTF-8输出为JSON文本Target Parameter:dataBody中加密字段名为data勾选Decrypt Request Body和Encrypt Response Body双向加解密。Step 3配置签名引擎切换到Galaxy SignatureEngineAlgorithm:HMAC-SHA256Key Source:Static→ 输入签名密钥Hex与AES密钥不同如b1c2d3e4f5a6b7c8d1e2f3a4b5c6d7e8Output Format:Hex LowercaseSigned Headers:X-Nonce,X-Timestamp,X-App-ID逗号分隔无空格Signature Header Name:X-Signature勾选Sign Request Headers勾选Debug Mode初期必开。Step 4禁用Burp自动HeaderSuite Options HTTP Request Handling勾选Dont add automatic headers to requests取消勾选Enable HTTP proxy logging for all requests减少日志干扰。4.2 调试阶段用Intercept和Debug日志定位问题Step 1捕获原始请求手机设置Burp为代理打开App触发订单创建Proxy Intercept中截获POST /api/order/create查看Raw标签页确认Body为dataXXXXXX且X-Nonce、X-Timestamp等Header存在。Step 2触发Galaxy解密右键请求 →Send to Galaxy→Decrypt Request Body若成功Response区域显示解密后JSON如{shop_id:s123,items:[{id:i456,qty:1}]}若失败Extender Output中报错如javax.crypto.BadPaddingException: Given final block not properly padded说明密钥或IV错误返回Step 4.1检查。Step 3检查Debug日志在Extender Output中查找DEBUG SIGNATURE INPUT区块核对Header名、值、Body明文是否与预期一致比对Computed Signature与Python本地计算结果若不一致逐行检查Groovy脚本如有、Header值时效性、Body解密状态。Step 4原子化重放验证在Galaxy SignatureEngine页点击Replay Signed Request观察Extender Output中服务端响应若返回{code:200,order_id:o789}说明签名链路打通若返回403 invalid signature检查Debug日志中SIGNATURE INPUT字符串是否与重放时完全一致时间戳是否已变。4.3 稳定化阶段构建可复用的团队配置模板单次调试成功不等于项目结束。为让团队其他成员能快速复用需将配置导出为模板Step 1导出Galaxy配置Galaxy Settings→Export Configuration→ 保存为meituan_order_galaxy.json此文件包含所有AES、Signature、Header配置但不包含密钥密钥被自动替换为[REDACTED]。Step 2编写密钥注入脚本创建galaxy_keys.groovy// 从环境变量读取密钥 def aesKey System.getenv(GALAXY_AES_KEY) ?: default_aes_key_placeholder def sigKey System.getenv(GALAXY_SIG_KEY) ?: default_sig_key_placeholder // 返回Map供Galaxy加载 return [ aes_key: aesKey, signature_key: sigKey ]Step 3标准化启动命令在团队Wiki中记录Burp启动命令# Linux/Mac java -Dgalaxy.keys.script/path/to/galaxy_keys.groovy \ -jar burpsuite_pro.jarWindows用户需用PowerShell设置环境变量$env:GALAXY_AES_KEYa1b2c3... $env:GALAXY_SIG_KEYb1c2d3... Start-Process java -Dgalaxy.keys.scriptC:\galaxy_keys.groovy -jar burpsuite_pro.jarStep 4配置共享与权限控制meituan_order_galaxy.json提交至Git仓库/burp-templates/目录密钥文件/secure/meituan_prod.keys仅存于测试服务器权限600新成员只需克隆仓库、设置环境变量、导入JSON配置5分钟内即可复现全部调试能力。5. 高阶技巧与避坑清单那些文档不会写的实战经验Galaxy的官方文档侧重API说明但真实世界的问题永远在边界之外。以下是我在20个加密App项目中踩过的坑以及提炼出的高阶技巧。5.1 技巧一用Galaxy的“Conditional Processing”实现多环境密钥自动切换同一App常有dev/staging/prod三套环境密钥不同。若每次切换都要手动改配置效率极低。Galaxy支持条件处理Conditional Processing可在CustomScript中根据URL动态选择密钥def url request.getHttpService().getHost() def keys [ dev.app.com: [aes: dev_aes_key, sig: dev_sig_key], staging.app.com: [aes: stg_aes_key, sig: stg_sig_key], app.com: [aes: prod_aes_key, sig: prod_sig_key] ] def envKeys keys.get(url) ?: keys[app.com] // 默认prod return [ aes_key: envKeys.aes, signature_key: envKeys.sig ]将此脚本绑定到Key ProviderGalaxy会根据请求域名自动加载对应密钥无需人工干预。5.2 技巧二处理“双重加密”场景——AES_CBC内嵌RSA公钥加密的密钥某银行App采用混合加密前端用RSA公钥加密AES密钥再用该AES密钥加密Body。Galaxy不支持RSA但可通过Groovy调用Java原生RSAimport javax.crypto.Cipher import java.security.KeyFactory import java.security.spec.X509EncodedKeySpec import java.util.Base64 def rsaEncryptedAesKey Base64.getDecoder().decode(rsa_base64_string) // 从Header或Body中提取 def publicKeyBytes Base64.getDecoder().decode(MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...) // 硬编码公钥 def keyFactory KeyFactory.getInstance(RSA) def publicKey keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)) def cipher Cipher.getInstance(RSA/ECB/PKCS1Padding) cipher.init(Cipher.DECRYPT_MODE, publicKey) def aesKeyBytes cipher.doFinal(rsaEncryptedAesKey) return aesKeyBytes // 返回16/24/32字节AES密钥此脚本可作为Key Provider实现RSA解密AES解密的全自动链路。5.3 避坑一不要在Galaxy中处理“流式加密”Body某些App对超大Body如图片上传采用流式AES加密即分块加密、无PKCS7填充。Galaxy的AES_CBC模块假设输入是完整密文对流式加密会解密失败。此时应放弃Galaxy改用Burp的ICustomRequestEditorTab接口开发专用Editor或直接用Intruder的Payload Processing做分块处理。5.4 避坑二签名Header的“大小写敏感”陷阱X-Signature和x-signature在HTTP中是同一个Header但某些服务端框架如Spring Boot在读取Header时会将x-signature转为X-Signature而另一些如Node.js Express保留原始大小写。若Galaxy配置的Signature Header Name为x-signature而服务端期望X-Signature签名虽正确但Header未被识别。统一原则所有Header名在Galaxy中必须使用大驼峰格式X-Signature与RFC标准一致。5.5 避坑三时间戳精度导致的签名漂移X-Timestamp若为秒级10位而服务端校验窗口为±30秒重放时可能因网络延迟导致超时。但若为毫秒级13位服务端窗口常为±30000毫秒。Galaxy的Header Injector默认注入秒级时间戳。需在CustomScript中强制毫秒def ts System.currentTimeMillis().toString() // 13位毫秒时间戳 request.setRequestHeaders(request.getRequestHeaders().collect { if (it.startsWith(X-Timestamp:)) X-Timestamp: ${ts} else it })并确保Signed Headers中X-Timestamp的值与此一致。5.6 最后一个建议把Galaxy配置做成“可测试的代码”我们团队将每个App的Galaxy配置JSON和密钥脚本Groovy纳入单元测试。用JUnit写一个测试类模拟IHttpRequestResponse对象传入已知密文和Header断言解密后Body和签名值是否符合预期。这样当App升级加密逻辑时测试失败会第一时间报警而不是等到渗透测试时才发现。我在实际项目中发现最耗时的从来不是写代码而是确认“到底哪一环出了问题”。Galaxy的价值就是把原本需要3小时排查的签名失败压缩到3分钟内定位。它不降低密码学的复杂度但把复杂度封装成可配置、可调试、可协作的工程模块。当你能对着Burp界面指着Extender Output里的Debug日志清晰说出“这里IV错了”或“这里Body没解密”你就已经超越了90%的同行。剩下的只是把这套方法论复制到下一个加密接口而已。