自己使用burp进行测试的过程中遇到好些接口是有sign的,如果修改了请求参数都需要重新计算sign值,所以有用python实现过一个简单的插件,来自动计算sign值,以达到和普通接口测试一样方便的效果。
后来,好基友在做一个APP的测试的时候,发现有类似的问题,接口的所有参数都有使用AES加密,返回也是一样。他通过逆向获得了加密的算法,我们就通过如下的插件实现了自动加解密的过程。在整个过程中有一点点收获,现分享出来。
具体bug请移步:
Zealer_android客户端安全检测(从脱壳到burp自动加解密插件案例/SQL注入/逻辑漏洞/附AES加解密脚本POC)
闲话少说,上代码,BurpExtender主类代码如下,进行了较为详细的注释。AES算法类的参考链接:
http://www.wenhq.com/article/view_716.html
package burp; import java.util.ArrayList; import java.util.List; import java.io.PrintWriter; import java.net.URLEncoder; import burp.AESOperator; //AES加解密算法的实现类 import burp.Util; //unicode解码的实现类 public classBurpExtender implements IBurpExtender, IHttpListener { privateIBurpExtenderCallbacks callbacks; privateIExtensionHelpers helpers; privatePrintWriter stdout;//用于输出,主要用于代码调试 // implement IBurpExtender @Override publicvoidregisterExtenderCallbacks(IBurpExtenderCallbacks callbacks) { stdout = new PrintWriter(callbacks.getStdout(), true); //PrintWriter stdout = new PrintWriter(callbacks.getStdout(),true); 这种写法是定义变量和实例化一并进行,会覆盖之前的同名变量。 //stdout.println("testxx"); //System.out.println("test"); 不会输出到burp的 this.callbacks = callbacks; helpers = callbacks.getHelpers(); callbacks.setExtensionName("AES encrypt Java edition");//插件名称 callbacks.registerHttpListener(this); //如果没有注册,下面的processHttpMessage方法是不会生效的。处理请求和响应包的插件,这是必要的。 } @Override publicvoidprocessHttpMessage(int toolFlag,boolean messageIsRequest,IHttpRequestResponsemessageInfo) { try{ List<String> paraWhiteList = newArrayList<String>(); //参数白名单,白名单中的参数值不进行加密计算 paraWhiteList.add("android"); if (toolFlag == 64 || toolFlag == 16 || toolFlag == 32 || toolFlag == 4){ //不同的toolflag代表了不同的burp组件。参考链接https://portswigger.net/burp/extender/api/constant-values.html#burp.IBurpExtenderCallbacks if (messageIsRequest){ //对请求包进行处理 IRequestInfoanalyzeRequest= helpers.analyzeRequest(messageInfo); //对消息体进行解析 List<String>headers= analyzeRequest.getHeaders();//获取http请求头的信息,返回可以看作是一个python中的列表,java中是叫泛型。 boolean isFileUploadRequest=false; for (String header : headers){ //stdout.println(header); if (header.toLowerCase().indexOf("content-type")!=-1&& header.toLowerCase().indexOf("boundary")!=-1){//通过http头中的内容判断这个请求是否是文件上传的请求。 isFileUploadRequest= true; } } if (isFileUploadRequest== false){//对文件上传的请求,对其中的参数不做加密处理 List<IParameter>paraList= analyzeRequest.getParameters();//获取参数列表,参数分为三种类型,URL中的参数,cookie中的参数,body中的参数。 byte[] new_Request = messageInfo.getRequest(); for (IParameter para : paraList){// 循环获取参数,判断类型,进行加密处理后,再构造新的参数,合并到新的请求包中。 if ((para.getType() == 0 || para.getType() == 1)&& !paraWhiteList.contains(para.getName())){ //getTpe()就是来判断参数是在那个位置的,cookie中的参数是不需要进行加密处理的。还要排除白名单中的参数。 Stringkey= para.getName();//获取参数的名称 Stringvalue= para.getValue();//获取参数的值 //stdout.println(key+":"+value); AESOperatoraes= newAESOperator(); //实例化加密的类 Stringaesvalue; aesvalue = aes.encrypt(value); //对value值进行加密 aesvalue = URLEncoder.encode(aesvalue); //进行URL编码,否则会出现“=”等特殊字符导致参数判断异常 stdout.println(key+":"+value+":"+aesvalue); //输出到extender的UI窗口,可以让使用者有一些判断 IParameternewPara= helpers.buildParameter(key, aesvalue, para.getType()); //构造新的参数 new_Request = helpers.updateParameter(new_Request, newPara); //构造新的请求包 } } messageInfo.setRequest(new_Request);//设置最终新的请求包 } /* to verify the updated result for(IParameter para : helpers.analyzeRequest(messageInfo).getParameters()){ stdout.println(para.getValue()); } */ } else{ //处理返回,响应包 IResponseInfoanalyzedResponse= helpers.analyzeResponse(messageInfo.getResponse()); //getResponse的返回类型是Byte[] List<String>header= analyzedResponse.getHeaders(); short statusCode = analyzedResponse.getStatusCode(); int bodyOffset = analyzedResponse.getBodyOffset(); if (statusCode==200){ try{ AESOperatoraes= newAESOperator(); Stringresp= newString(messageInfo.getResponse());//Byte[] to String String body = resp.substring(bodyOffset); String deBody= aes.decrypt(body); deBody = deBody.replace("/"","///""); String UnicodeBody = (new Util()).unicodeDecode(deBody); //String newBody = body +"/r/n" +UnicodeBody; //将新的解密后的body附到旧的body后面 String newBody = UnicodeBody; byte[] bodybyte = newBody.getBytes(); messageInfo.setResponse(helpers.buildHttpMessage(header, bodybyte)); }catch(Exception e){ stdout.println(e); } } } } } catch(Exception e){ stdout.println(e); } } }
1.能用java就不要用其他
我平常python用得比较多的,之前也用python写过几个简单插件。但是在开发burp插件的时候,发现还是Java更合适。上面的这个插件,最初就是用py实现的,但是,当这个py文件调了python的其他类,如下图。通过Jython去解析执行,遇到pyd文件就无法进行下去了,因为pyd是C写的,Jython是无法使用C写的模块的。burp本事是Java写的,使用Java去开发插件兼容性最高,会少很多莫名其妙的错误。
下面这个链接对此有详细说明:
http://stackoverflow.com/questions/16218183/using-pyd-library-in-jython
2.代码的适当分离
我会分别将插件的代码和AES算法的代码分别写在两个不同文件中。这样可以单独调试算法的代码,也可以让插件代码更简洁不易出错。因为插件的代码每修改一次都需要重新在burp中加载才可以看到效果,不像一般的程序在IDE中就可以调试,所以个人认为这样比较好。
3.插件代码的模式
插件代码的结构基本是固定的。比如,如果想要写一个对http请求和响应进行操作的插件,那么基本上如图的这段代码是可以直接copy使用的,下图标红的几个方法就都是必须的。我想我们大多数时候都是在对http的包进行处理。有了大的框架之后,再进行修改相对会容易很多。所以,如果你想写一个什么样的插件,你完全可以去找一个类似的插件,看他的代码,copy他的代码,改他的代码(比如我的,呵呵)。
你要问怎么样查看已有插件的代码?怎样查API文档?
首先安装一个已有插件。
找到burp所在路径下的bapps目录,里面就是你安装了的插件。
拖到JD-Gui中就可以看代码了,这种一般是不会做混淆的,至少我还没发现~。Py的就更不用说了,直接文件右键打打开。
4.怎样阅读API文档
关于API文档,我是通过溯源的方法,对于0基础的读者比较实用。比如我的目的是加密各个参数,那么首先要获取请求中的参数。我先去API库中搜索关键词parameter,可以找到多个相关方法,通过对比,我确定List<IParameter> getParameters();是我需要的。找到这个方法后,查看它的参数、返回值类型、所属的类这三个关键因素。它属于IRequestInfo类,只有 IRequestInfo类型的对象才可以调用它, 那么,有哪些方法会返回这个类型的对象呢?再去找那些方法可以返回这个类型的方法。依次类推,可以知道需要使用哪些方法,哪些类,就能梳理清除大致的思路了。
向先行者致敬,让我们少走弯路。
java 篇:
BurpSuite 扩展开发[1]-API与HelloWold http://drops.wooyun.org/papers/3962
BurpSuite 插件编写教程(第一篇) http://www.moonsos.com/Article/penetration/107.html
国产BurpSuite插件Assassin V1.0发布 http://www.moonsos.com/tools/webscan/97.html
BurpSuite 插件开发指南之 API 上篇 http://www.evil0x.com/posts/17487.html
BurpSuite插件开发指南之 API 下篇 http://drops.wooyun.org/tools/14685
BurpSuite 插件开发指南之 Java 篇 http://drops.wooyun.org/tools/16056
python 篇:
burpsuite 扩展开发之Python(change unicode to chinese) 我是从这入门的 http://drops.wooyun.org/tools/5751
BurpSuite 插件开发之过狗菜刀 http://www.secpulse.com/archives/44241.html
* 作者:bit4,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)