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
網絡安全專家