0x00:准备工作

首先需要导出burp插件开发需要的API文件

位置在Extender-->APIs-->Save Javadoc files

导出后,目录如下

使用idea新建一个工程

然后导入burp文件夹

0x01:写一个基础demo

需要在导入的项目中创建一个叫BurpExtender.java的类并Implements  IBurpExtender这个类,注意,必须叫这个名字,否则burp会识别不到此插件的存在,我们实现的功能都需要在这个类中实现。

package burp;

public class BurpExtender implements IBurpExtender{
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {

    }
}

我们写一个简单的Hello World做测试

package burp;

import java.io.PrintWriter;

public class BurpExtender implements IBurpExtender{
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        // set our extension name
        callbacks.setExtensionName("Hello world extension");

        // obtain our output and error streams
        PrintWriter stdout = new PrintWriter(callbacks.getStdout(), true);
        PrintWriter stderr = new PrintWriter(callbacks.getStderr(), true);

        // write a message to our output stream
        stdout.println("Hello output");

        // write a message to our error stream
        stderr.println("Hello errors");

        // write a message to the Burp alerts tab
        callbacks.issueAlert("Hello alerts");

        // throw an exception that will appear in our error stream
        throw new RuntimeException("Hello exceptions");

    }
}

然后导出为jar包

1、点开File文件下的Project Structure,依次选择Artifacts-->+-->jar-->From modules with dependencies

2、选择主函数

.MF文件就是你生成jar包生成的签名信息,第一次生成jar包,会生成相应的.MF签名文件,若第二次再生成jar包,会报错,说已经存在,只需将.MF文件删除即可

3、选择输出的目录这个会自动在当前文件夹下生成,即Output Directory ,点击OK

4、选择Build->Build Project 将项目.java文件编译成.class文件

出现out文件夹说明已经编译成功

5、选择Build->Build Artifacts导出jar包

读条结束后即可找到导出的jar包

尝试burp导入该插件

可见已经导入成功。

0x02:认识api

由于我们使用burp多用抓包改包功能,所以其实就是对burp截获到的数据进行修改再发送,我们要解决的只是如何获取这些数据和如何修改这些数据,至于具体开发什么功能需要自己想了,这些方法都被burp封装到了其自带的api中,极大的便利了我们开发。

IBurpExtender接口:

该接口只有一个方法registerExtenderCallbacks 在该方法中实现我们的功能 当插件被加载后 burp插件通过这个回调方法调用我们实现的功能代码

callback.setExtensionName()方法用于设置插件名称

IHttpListener接口:

1、IHttpListener接口主要用于监听经过burp的http数据流,包括请求数据及响应数据。我们在插件开发中注册IHttpListener接口后,当请求数据经过burp 的各个模块(如proxy、intruder、scanner、repeater)时,我们可以拦截并修改请求数据中的host、port、protocol、请求头、请求数据包(body)后再发送出数据;同时我们也可以拦截响应数据,对它进行解密或者加密操作后,再返回响应数据。

2、IHttpListener接口包含的函数processHttpMessage,我们implements接口IHttpListener后,在processHttpMessage函数中去实现具体功能。

3、public void processHttpMessage(int toolFlag,boolean messageIsRequest,IHttpRequestResponse messageInfo),processHttpMessage包含三个参数toolFlagmessageIsRequestmessageInfo

4、toolFlag用于标识函数功能作用于哪个模块,其中包含有以下模块,如下所示:

5、messageIsRequest用于判断当前数据流量是 请求数据(Request)或者是 响应数据(Response),当messageIsRequest为true是请求数据(R equest),messageIsRequest为false是响应数据(Response)。

6、messageInfo为IHttpRequestResponse接口的实例,可以通过messageInfo获取流量数据(包括请求数据及响应数据)的详细信息(包括请求的host、port、protocol、header(请求头)、body(请求包体)等)

7、IHttpRequestResponse接口包含的函数,如下所示:

8、其中可以通过getHttpService()得到一个http信息包括(host、port、protocol)、通过getRequest()得到请求数据的完整信息、通过getResponse()得到响应数据的完整信息。

0x03:一些实例

1、修改host(m.baidu.com修改为pureqh.top),将请求数据重定向发送到其他主机.

package burp;

import java.io.PrintWriter;

public class BurpExtender implements IBurpExtender,IHttpListener{

    private static final String HOST_FROM = "www.baidu.com"; //原主机
    private static final String HOST_TO = "pureqh.top";//重定向的主机

    private PrintWriter stdout;
    private IExtensionHelpers helpers;
    @Override
    public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
        // TODO Auto-generated method stub
        if(messageIsRequest) {//判断请求是 请求数据 则替换host 重定向 host
            //获取一个httpService包含host、port、protocol
            IHttpService httpService = messageInfo.getHttpService();
            //打印原host
            stdout.println("orig host is: " + httpService.getHost());
            //判断原host是不是m.baidu.com,如果是则替换host
            if(HOST_FROM.equalsIgnoreCase(httpService.getHost())) {
                //使用helpers.buildHttpService新建一个httpService
                //新的httpService中host指定为要替换的host chls.pro
                //使用messageInfo.setHttpService 将拦截到的messageInfo的
                //httpService替换成新建的httpService 完成 host 替换
                messageInfo.setHttpService(helpers.buildHttpService(HOST_TO, httpService.getPort(), httpService.getProtocol()));
                //重新获取httpService 查看host是否替换成功
                httpService = messageInfo.getHttpService();
                //打印出替换后的host、port、protocol
                stdout.println("host is: " + httpService.getHost() + " "
                        + "and port is: " + httpService.getPort() + " "
                        + "and protocol is: " + httpService.getProtocol());
            }
        }

    }

    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        // TODO Auto-generated method stub
        helpers = callbacks.getHelpers();
        callbacks.setExtensionName("HostDemo");
        callbacks.registerHttpListener(this);
        stdout = new PrintWriter(callbacks.getStdout(),true);
    }
}

这里代码很简单,首先我们获取了每次请求的对象,然后将该对象的host取出来与我们设置好的host进行比对,如果相同,重新创建一个httpService对象发送我们设置好的host头。

可以看到,burp已经将www.baidu.com重定向到pureqh.top

2、添加x-forwarded-for请求头

package burp;

import java.io.PrintWriter;
import java.util.List;

//implements IHttpListener
public class BurpExtender implements IBurpExtender,IHttpListener{

    private IBurpExtenderCallbacks callbacks;
    private IExtensionHelpers helpers;
    private PrintWriter stdout;

    @Override
    public void processHttpMessage(int toolFlag, boolean messageIsRequest,
                                   IHttpRequestResponse messageInfo) {
        // TODO Auto-generated method stub
        if(toolFlag == this.callbacks.TOOL_PROXY) {//判断当前监听流量数据的模块是否为PROXY模块
            //如果是PROXY模块 则进入下面的数据处理
            if(messageIsRequest) { //只在请求数据时添加请求头X-Forwarded-For
                try {
                    //获取一个IRequestInfo接口实例analyIRequestInfo
                    //analyIRequestInfo中
                    //包含了 请求数据 的详细信息包括请求数据包体(body)的偏移地址
                    //请求数据中的请求头(返回List<String>列表)、请求数据的请求方法(GET、POST等)
                    //请求数据中的请求参数(返回List<IParameter>列表
                    //请求的URL
                    //我们在此处获取该实例是为了得到请求数据中的请求头列表
                    //以及请求数据包体(body)的起始偏移地址
                    IRequestInfo analyIRequestInfo = helpers.analyzeRequest(messageInfo);
                    //获取整个请求数据内容
                    String request = new String(messageInfo.getRequest(),"UTF-8");
                    //通过上面的analyIRequestInfo得到请求数据包体(body)的起始偏移
                    int bodyOffset = analyIRequestInfo.getBodyOffset();
                    //通过起始偏移点得到请求数据包体(body)的内容
                    byte[] body = request.substring(bodyOffset).getBytes("UTF-8");
                    //通过上面的analyIRequestInfo得到请求数据的请求头列表
                    List<String> headers = analyIRequestInfo.getHeaders();//获取http请求头的信息
                    //生成X-Forwarded-For请求头 包括请求头的key(X-Forwarded-For)及随机生成的IP
                    String xForwardFor = "X-Forwarded-For: "+ "127.0.0.1";
                    //在headers请求头列表中添加X-Forwarded-For请求头
                    headers.add(xForwardFor);
                    //打印X-Forwarded-For请求头测试
                    stdout.println(xForwardFor);
                    //使用我们刚添加进去X-Forwarded-For请求头的请求头列表以及
                    //上面得到的请求数据包体(body)重新生成一个HttpMessage
                    //重新构造了请求数据
                    byte[] newRequest = helpers.buildHttpMessage(headers, body);
                    //打印出重新构造的请求数据测试
                    stdout.println(helpers.analyzeRequest(newRequest).getHeaders());
                    //发送重新构造的请求数据 里面已经包含有我们添加的X-Forward-For
                    messageInfo.setRequest(newRequest); //设置最终新的请求包 添加xForwardFor 请求头
                }catch(Exception e) {
                    stdout.println(e);
                }

            }
        }
    }

    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        // TODO Auto-generated method stub
        helpers = callbacks.getHelpers();
        stdout = new PrintWriter(callbacks.getStdout(),true);
        callbacks.setExtensionName("Change_X-FORWARD-FOR"); //设置插件名称
        callbacks.registerHttpListener(this); //注册HttpListener
    }
}

这里其实就是获取原有数据包,添加XFF头后重新发送数据包。

由于使用的是IHttpListener接口,所以在burp界面中看不到已经添加的XFF头,可以在打印日志中看到。

3、操作body

之前几个都是操作http头的,那如何操作body呢,我们可以打印一下analyIRequestInfo中数据包体(body)的内容。

可见,我们输入的post内容都被编码为了十进制ascii码,

由于analyIRequestInfo获取的body是一整块的,并没有区分参数,只是做了一下ascii编码,

这里可以将数组转为字符串,再将字符串转回去数组,进行修改body。

0x04:进阶

1、检测sql注入

GET注入

首先我们需要获取参数,如何获取参数呢,IRequestInfo接口为我们提供了getParameters方法

demo:

IRequestInfo analyzeRequest = helpers.analyzeRequest(messageInfo);
                //对消息体进行解析,messageInfo是整个HTTP请求和响应消息体的总和,各种HTTP相关信息的获取都来自于它,HTTP流量的修改都是围绕它进行的。

                /*****************获取参数**********************/
                List<IParameter> paraList = analyzeRequest.getParameters();
                //获取参数的方法
                //当body是json格式的时候,这个方法也可以正常获取到键值对;但是PARAM_JSON等格式不能通过updateParameter方法来更新。
                //如果在url中的参数的值是 key=json格式的字符串 这种形式的时候,getParameters应该是无法获取到最底层的键值对的。

                for (IParameter para : paraList){// 循环获取参数,判断类型,进行加密处理后,再构造新的参数,合并到新的请求包中。
                    String key = para.getName(); //获取参数的名称
                    String value = para.getValue(); //获取参数的值
                    int type = para.getType();
                    stdout.println("参数 key value type: "+key+" "+value+" "+type);
                }

发现除了获取参数这一方式外,我们还可以直接修改等于号后的内容,

那么,url在哪里呢,之前说过整个http头都是被封装在List中的,所以我们可以逐行获取List中的值,遍历一下List。

List<String> headers = analyIRequestInfo.getHeaders();//获取http请求头的信息
                   
                    for (int i=0;i<headers.size();i++){
                        stdout.println(headers.get(i));
                    }

发现其实list中的内容正是每一行的http头。

这样我们就可以直接获取list的第一行,直接修改等于号后的内容,最后拼接url和list即可,由于get需要url编码一下,要不然可能报错,所以代码如下:

String sql = "=%31%27%20%61%6e%64%20%73%6c%65%65%70%28%35%29%20%61%6e%64%20%27%31%27%20=%20%27%31%20&";
headers.set(0,headers.get(0).replaceAll("\\=(.*?)+[&|\\s+]",sql));
byte[] newRequest = helpers.buildHttpMessage(headers, body);
stdout.println(helpers.analyzeRequest(newRequest).getHeaders());
//发送重新构造的请求数据 里面已经包含有我们添加的sql payload
messageInfo.setRequest(newRequest);

试一下结果:

发现http头内的url已经被我们成功修改。

那如何获得返回包呢,上面的if(messageIsRequest)用来获取请求包,else则用来获取响应包,

demo:

String resp = new String(messageInfo.getResponse(),"UTF-8");
stdout.println(resp);

即可输出响应内容

那么如何判断是否存在注入呢,用时间差比对即可。

完整代码

package burp;


import java.io.PrintWriter;
import java.util.List;



//implements IHttpListener
public class BurpExtender implements IBurpExtender,IHttpListener{

    private IBurpExtenderCallbacks callbacks;
    private IExtensionHelpers helpers;
    private PrintWriter stdout;
    static final int TIMEOUT = 3000;
    long sTime;

    @Override
    public void processHttpMessage(int toolFlag, boolean messageIsRequest,
                                   IHttpRequestResponse messageInfo) {
        // TODO Auto-generated method stub
        //判断当前监听流量数据的模块是否为PROXY模块
        //如果是PROXY模块 则进入下面的数据处理
        if(toolFlag == this.callbacks.TOOL_PROXY) {
            //只在请求数据时添加请求头X-Forwarded-For

            if(messageIsRequest) {
                try {
                    //获取一个IRequestInfo接口实例analyIRequestInfo
                    //analyIRequestInfo中
                    //包含了 请求数据 的详细信息包括请求数据包体(body)的偏移地址
                    //请求数据中的请求头(返回List<String>列表)、请求数据的请求方法(GET、POST等)
                    //请求数据中的请求参数(返回List<IParameter>列表
                    //请求的URL
                    //我们在此处获取该实例是为了得到请求数据中的请求头列表
                    //以及请求数据包体(body)的起始偏移地址
                    IRequestInfo analyIRequestInfo = helpers.analyzeRequest(messageInfo);
                    //获取整个请求数据内容
                    String request = new String(messageInfo.getRequest(),"UTF-8");
                    //通过上面的analyIRequestInfo得到请求数据包体(body)的起始偏移
                    int bodyOffset = analyIRequestInfo.getBodyOffset();
                    //通过起始偏移点得到请求数据包体(body)的内容
                    byte[] body = request.substring(bodyOffset).getBytes("UTF-8");
                    /*for (int i=0; i<body.length;i++){
                        stdout.println(body[i]);

                    }*/




                    //通过上面的analyIRequestInfo得到请求数据的请求头列表
                    List<String> headers = analyIRequestInfo.getHeaders();//获取http请求头的信息
                    //发现url在headers第一行
                    //stdout.println(headers.get(0));
                    //sql的payload
                    String sql = "=%31%27%20%61%6e%64%20%73%6c%65%65%70%28%35%29%20%61%6e%64%20%27%31%27%20=%20%27%31%20&";


                    headers.set(0,headers.get(0).replaceAll("\\=(.*?)+[&|\\s+]",sql));

                    //使用我们刚添加进去的headers重新生成一个HttpMessage
                    //重新构造了请求数据
                    byte[] newRequest = helpers.buildHttpMessage(headers, body);
                    //打印出重新构造的请求数据测试

                    stdout.println(helpers.analyzeRequest(newRequest).getHeaders());
                    //发送重新构造的请求数据 里面已经包含有我们添加的sql payload
                    messageInfo.setRequest(newRequest);
                    //用时间差判断是否存在注入
                    long startTime = System.currentTimeMillis();
                    sTime = startTime;

                }catch(Exception e) {
                    stdout.println(e);
                }

            }else{//获取resp
                try{
                    String resp = new String(messageInfo.getResponse(),"UTF-8");
                    long endTime = System.currentTimeMillis();
                    //对时间差进行比较,如果大于延时则可能存在注入
                    if ((endTime - sTime)>TIMEOUT){

                        stdout.println("Maybe is Inject");
                    }



                }catch (Exception e){
                    stdout.println(e);
                }
            }
        }

    }

    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        // TODO Auto-generated method stub
        helpers = callbacks.getHelpers();
        stdout = new PrintWriter(callbacks.getStdout(),true);//将消息写入输出流
        callbacks.setExtensionName("sql_fuzz"); //设置插件名称
        callbacks.registerHttpListener(this); //注册HttpListener

    }
}

来试一试:

当然这只是一个demo demo的意思就是 没卵用 这个插件有很多问题,比如线程是没有锁的,导致发包多的话会不知道哪个存在注入。。。

POST注入

同理,我们只需要将等于号后的字符修改即可。

对于注入来说其实已经有现成的项目了 ,即checkSql,我们直接拿过源码改一下规则就行了 ,

0x05:参考

https://www.jianshu.com/p/2a66782dc2ad

https://www.jianshu.com/p/5515f69a16e5

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注