XX客户端APP签名分析之bp插件打造篇
VSole2021-08-02 19:09:58
前言
本篇主要思路及方法来自于bit4woo和CC11001100两位巨佬的成果。
学习链接https://github.com/bit4woo/burp-api-drops
根据大佬的建议,本次插件使用java进行编写。而且bp插件的编写对于类名也有严格的限制。
主要的调用逻辑如下所示
根据该图,对于插件编写的流程其实也很清楚了,我们只需要先调用这些之前写好的类,然后只需要编写BurpExtender这一个类就好了。
01 热编写流程
首先因为不同网站对应的Key不同,先创建个字典进行存放。
public BurpExtender() { this.signMap = new ConcurrentHashMap<>(); signMap.put("class.mosaic.cn", "ac6190d7dfaa77df726f0a82244d3eda68675ccd4e95de802f5042e91d15edc7bae3026d8f0fb2a8287446bb289563970264"); signMap.put("bbsapi.mosaic.cn", "Wj8BI3VUZ6BuojAkqzBM3HWHNHv08xdZEtaksbRg6snnuLsvivwa8IvR6PvQ76H0IQQsqkIsa5OKJtg6QcBMfCblMMywgZaA8co"); }
然后是IBurpExtender必须实现的方法registerExtenderCallbacks
@Overridepublic void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { this.callbacks = callbacks; callbacks.setExtensionName("xxsign重放辅助渗透"); helpers = callbacks.getHelpers(); stdout = new PrintWriter(callbacks.getStdout(), true); callbacks.registerHttpListener(this);//如果不注册,processHttpMessage方法不会生效。 }
接下来就是processHttpMessage方法,首先要确定toolFlag,不同的toolFlag代表不同的burp组件,此处我们只处理repeater发出的消息。
if (toolFlag != burp.IBurpExtenderCallbacks.TOOL_REPEATER) { return; } // 只处理请求,响应则忽略 if (!messageIsRequest) { return; } byte[] request = messageInfo.getRequest(); if (request == null || request.length == 0) { return; } // 只有特定的两个域名才会生效 final IHttpService httpService = messageInfo.getHttpService(); final String host = httpService.getHost(); if (!signMap.containsKey(host)) { return; }
接下来是获取参数,由于上一节中已经分析出了参数组成,key是固定值,noncestr是随机生成。timestamp是当前时间戳,因此实际上参数的获取所产生交互是比较少的。只需要生成这些参数的拼接格式,以及SHA1算法。
long now = System.currentTimeMillis();
private static String randomNoncestr() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 8; i++) { char c = (char) (ThreadLocalRandom.current().nextInt(10) + 48); sb.append(c); } return sb.toString(); }
final String sign = DigestUtils.sha1Hex(sb.toString());
最后每次重放就替换掉参数即可。
final IParameter newTimestampParam = helpers.buildParameter("timestamp", now + "", IParameter.PARAM_URL); final IParameter newSignParam = helpers.buildParameter("sign", sign, IParameter.PARAM_URL); final IParameter newNoncestrParam = helpers.buildParameter("noncestr", noncestr, IParameter.PARAM_URL); request = helpers.updateParameter(request, newTimestampParam); request = helpers.updateParameter(request, newSignParam); request = helpers.updateParameter(request, newNoncestrParam); messageInfo.setRequest(request); stdout.println("更新了sign: " + sign); stdout.println("更新了timestamp: " + now); stdout.println("更新了noncestr: " + noncestr); stdout.println("本次处理完毕");
所有的关键操作就这样完成了。
02 总结
最终BurpExtender类代码
public class BurpExtender implements IBurpExtender, IHttpListener { private IBurpExtenderCallbacks callbacks; private IExtensionHelpers helpers; private PrintWriter stdout; private ConcurrentHashMap signMap; public BurpExtender() { this.signMap = new ConcurrentHashMap<>(); signMap.put("class.mosaic.cn", "ac6190d7dfaa77df726f0a82244d3eda68675ccd4e95de802f5042e91d15edc7bae3026d8f0fb2a8287446bb289563970264"); signMap.put("bbsapi.mosaic.cn", "Wj8BI3VUZ6BuojAkqzBM3HWHNHv08xdZEtaksbRg6snnuLsvivwa8IvR6PvQ76H0IQQsqkIsa5OKJtg6QcBMfCblMMywgZaA8co"); } @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { this.callbacks = callbacks; callbacks.setExtensionName("xx客户端sign重放辅助渗透"); helpers = callbacks.getHelpers(); stdout = new PrintWriter(callbacks.getStdout(), true); callbacks.registerHttpListener(this); } @Override public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { // 只处理repeater发出的消息 if (toolFlag != burp.IBurpExtenderCallbacks.TOOL_REPEATER) { return; } // 只处理请求,响应则忽略 if (!messageIsRequest) { return; } byte[] request = messageInfo.getRequest(); if (request == null || request.length == 0) { return; } // 只有特定的两个域名才会生效 final IHttpService httpService = messageInfo.getHttpService(); final String host = httpService.getHost(); if (!signMap.containsKey(host)) { return; } stdout.println("检测到,开始处理..."); final IRequestInfo requestInfo = helpers.analyzeRequest(request); TreeMap signParamsMap = new TreeMap<>(); requestInfo.getParameters().forEach(x -> { if (IParameter.PARAM_URL != x.getType()) { return; } if ("timestamp".equals(x.getName()) || "sign".equals(x.getName()) || "noncestr".equals(x.getName())) { return; } signParamsMap.put(URLDecoder.decode(x.getName(), UTF_8), URLDecoder.decode(x.getValue(), UTF_8)); }); String appSignKey = signMap.get(host); signParamsMap.put("appSignKey", appSignKey); long now = System.currentTimeMillis();// long now = 1626237221383L; signParamsMap.put("timestamp", now + ""); String noncestr = randomNoncestr();// String noncestr = "73699378"; signParamsMap.put("noncestr", noncestr); StringBuilder sb = new StringBuilder(); signParamsMap.forEach((key, value) -> { sb.append(key).append("=").append(value).append("&"); }); if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } stdout.println("用来SHA1加密的字符串: " + sb.toString()); final String sign = DigestUtils.sha1Hex(sb.toString()); // 替换掉参数 final IParameter newTimestampParam = helpers.buildParameter("timestamp", now + "", IParameter.PARAM_URL); final IParameter newSignParam = helpers.buildParameter("sign", sign, IParameter.PARAM_URL); final IParameter newNoncestrParam = helpers.buildParameter("noncestr", noncestr, IParameter.PARAM_URL); request = helpers.updateParameter(request, newTimestampParam); request = helpers.updateParameter(request, newSignParam); request = helpers.updateParameter(request, newNoncestrParam); messageInfo.setRequest(request); stdout.println("更新了sign: " + sign); stdout.println("更新了timestamp: " + now); stdout.println("更新了noncestr: " + noncestr); stdout.println("本次处理完毕"); } private static String randomNoncestr() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 8; i++) { char c = (char) (ThreadLocalRandom.current().nextInt(10) + 48); sb.append(c); } return sb.toString(); } public static void main(String[] args) { System.out.println(randomNoncestr()); } }

VSole
网络安全专家