长亭百川云 - 文章详情

burpsuite插件开发安全指南 - 飘渺红尘✨

博客园 - 飘渺红尘✨

94

2024-07-19

   我想写一篇文章,关于burpsuite插件开发入门。去年我写了一些burp插件,用于辅助渗透和漏洞挖掘,这给我带来了很多方便,可以捡到一些安全漏洞。

   本人以第一视角说下本人是如何学习burpsuite插件开发的。本文只是入门,如果想要深入学习插件开发,还需要更多的学习和参考。

   1.环境配置和搭建  

idea+maven+jdk14(可按照需求,自定义设置jdk版本):

(1)idea创建maven项目  忽略步骤:

  

   删除掉标签里的内容

  (2)新增Burp API依赖,pom中引入相关依赖:

   

<dependencies\>
        <!-- https://mvnrepository.com/artifact/net.portswigger.burp.extender/burp-extender-api \-->
        <dependency\>
            <groupId\>net.portswigger.burp.extender</groupId\>
            <artifactId\>burp-extender-api</artifactId\>
            <version\>1.7.22</version\>
        </dependency\>
    </dependencies\>

 刷新maven加载jar包:

  

 至此引入依赖成功:

 2. 插件开发基础部分:

 需求:UI控制台打印hello world:  

package com.test.DevDemo;

import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;

import java.io.PrintWriter;

public class BurpExtender implements IBurpExtender {
    private IExtensionHelpers helpers;
    private  IBurpExtenderCallbacks callbacks;
    private PrintWriter stdout;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
        this.callbacks = iBurpExtenderCallbacks;
        //设置插件名字
        this.callbacks.setExtensionName("第一个程序,这是我们的插件名字");
        //打印信息在UI控制台页面,打印内容为hello world
        this.stdout = new PrintWriter(callbacks.getStdout(),true);
        stdout.println("hello world");
    }
}

目录名必须设置成burp目录,类名即文件名必须是BurpExtender:

**

**

(2)mvn打包jar包,配置打包jar包所需的依赖:

<build\>
        <plugins\>
            <plugin\>
                <groupId\>org.apache.maven.plugins</groupId\>
                <artifactId\>maven-assembly-plugin</artifactId\>
                <executions\>
                    <execution\>
                        <phase\>package</phase\>
                        <goals\>
                            <goal\>single</goal\>
                        </goals\>
                    </execution\>
                </executions\>
                <configuration\>
                    <descriptorRefs\>
                        <descriptorRef\>jar-with-dependencies</descriptorRef\>
                    </descriptorRefs\>
                </configuration\>
            </plugin\>
        </plugins\>
    </build\>

(3)idea打开当前终端,输入打包jar包命令:

mvn clean install

(4)打开当前目录→target

open . 打开当前目录

点击target目录,选择BurpPlugDev-1.0-SNAPSHOT-jar-with-dependencies.jar 这个带dependencies的jar包

解释:

BurpPlugDev-1.0\-SNAPSHOT.jar 为不带依赖的jar包
BurpPlugDev\-1.0\-SNAPSHOT-jar-with-dependencies.jar 为带依赖的jar包

(5)burpsuite引入我们打包的jar包

1.选择插件,选择ADD添加

2.选择jar包,点击NEXT下一步

3.加载成功,显示插件名字并且打印输出hello world

第一部分结束。

Burp监听器,侦听器 测试demo使用:

 

IExtensionStateListener
IHttpListener
IProxyListener
IScannerListener
IScopeChangeListener

编写测试代码,参考官方案例:

https://github.com/PortSwigger/example-event-listeners/blob/master/java/BurpExtender.java

复制粘贴抄下:

package burp;

import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;

import java.io.PrintWriter;

public class BurpExtender implements IBurpExtender, IHttpListener,
        IProxyListener, IScannerListener, IExtensionStateListener{
    private IExtensionHelpers helpers;
    private  IBurpExtenderCallbacks callbacks;
    private PrintWriter stdout;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
        this.callbacks = iBurpExtenderCallbacks;
        //设置插件名字
        this.callbacks.setExtensionName("第一个程序,这是我们的插件名字");
        //打印信息在UI控制台页面,打印内容为hello world
        this.stdout = new PrintWriter(callbacks.getStdout(),true);
        stdout.println("hello world");
        //一定要注册监听器,不然下面的函数无法生效
        callbacks.registerHttpListener(this);
        callbacks.registerProxyListener(this);
        callbacks.registerScannerListener(this);
        callbacks.registerExtensionStateListener(this);
    }

    @Override
    public void extensionUnloaded() {
        stdout.println("Extension was uploaded");
    }

    @Override
    public void processHttpMessage(int i, boolean b, IHttpRequestResponse iHttpRequestResponse) {
        stdout.println(
                (b ? "HTTP request to ":"HTTP response from ")+iHttpRequestResponse.getHttpService()+"\["+callbacks.getToolName(i)+"\]"
        );
    }

    @Override
    public void processProxyMessage(boolean b, IInterceptedProxyMessage iInterceptedProxyMessage) {
        stdout.println(
                (b ? "Proxy request to ":"Proxy response from ")+iInterceptedProxyMessage.getMessageInfo().getHttpService()
        );
    }

    @Override
    public void newScanIssue(IScanIssue iScanIssue) {
        stdout.println("New scan issue "+ iScanIssue.getIssueName());
    }
}

按照前面说的方法,进行打包运行:

挂上代理访问网站:

可以发现在控制台可以看到Proxy请求信息和HTTP request的请求信息

发现其他信息并没有打印输出?

首先是IScannerListener接口含义,简单来说就是想触发这个接口,需要调度Burpsuite的Scanner扫描功能

其他的类似接口是类似的情况。需要特定场景才可以触发。

结论:访问一个http/https请求,默认触发IHttpListener和IProxyListener

如果写测试案例的话,需要大量依赖这两个监听器。

插件开发第二部分:http数据包获取

http请求数据包的几个部分

http请求:
1.header 请求头
2.body 请求body->非get/options请求,多于POST,PUT等
3.parameter 请求参数 
4.主机头端口协议

http响应:
1.header 请求头
2.body
3.主机头端口协议

测试用例,只涉及查,不涉及修改确认等操作,仔细看注释部分:

package burp;

import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;

import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class BurpExtender implements IBurpExtender, IHttpListener{
    private IExtensionHelpers helpers;
    private  IBurpExtenderCallbacks callbacks;
    private PrintWriter stdout;
    private PrintWriter stderr;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
        this.callbacks = iBurpExtenderCallbacks;
        //设置插件名字
        callbacks.setExtensionName("熟悉HTTP数据包各种操作");
        this.helpers = callbacks.getHelpers();
        //打印信息在UI控制台页面,打印内容为hello world
        this.stdout = new PrintWriter(callbacks.getStdout(),true);
        this.stderr = new PrintWriter(callbacks.getStderr(),true);
        stdout.println("hello world");
        //一定要注册监听器,不然下面的函数无法生效
        callbacks.registerHttpListener(this);
    }


    @Override
    public void processHttpMessage(int i, boolean b, IHttpRequestResponse iHttpRequestResponse) {
       //切换http监听模块为Burpsuiteproxy模块
        if(i==IBurpExtenderCallbacks.TOOL\_PROXY){
            //对请求包进行处理
            if(b){
                //对消息体进行解析,messageInfo是整个HTTP请求和响应消息体的总和,各种HTTP相关信息的获取都来自于它,HTTP流量的修改都是围绕它进行的。
                IRequestInfo analyzeRequest = helpers.analyzeRequest(iHttpRequestResponse);
                /\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*获取参数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
                List<IParameter> parameList = analyzeRequest.getParameters();
                //获取参数的方法
                //遍历参数
                for(IParameter para:parameList){
                    //获取参数
                    String key = para.getName();
                    //获取参数值(value)
                    String value = para.getValue();
                    int type = para.getType();
                    stdout.println("参数key value type:"+key+" "+value+" "+type);
                }

                //获取headers方法:
                List<String> headers = analyzeRequest.getHeaders();
                //新增header
                headers.add("myheader:hello world");
                //遍历请求头
                for(String header:headers){
                    stdout.println("header: "+header);
                }

                //获取协议 端口 和主机名
                IHttpService service = iHttpRequestResponse.getHttpService();
                stdout.println("协议 主机 端口 "+service.getProtocol()+" "+service.getHost()+" "+service.getPort());
            }

        }else{//这个逻辑是处理响应包
            IResponseInfo analyzeResponse = helpers.analyzeResponse(iHttpRequestResponse.getResponse());
            //获取响应码信息
            short statusCode = analyzeResponse.getStatusCode();
            stdout.println("status= "+statusCode);
            //获取响应头信息
            List<String> headers = analyzeResponse.getHeaders();
            for(String header:headers){
                stdout.println("header:"+header);
            }
            // 获取响应信息
            String resp = new String(iHttpRequestResponse.getResponse());
            int bodyOffset = analyzeResponse.getBodyOffset();
            String body \= resp.substring(bodyOffset);
            stdout.println("response body="+body);
        }
    }
}

一图胜千言,图片和代码参考于:https://github.com/bit4woo/burp-api-drops

演示运行效果,当访问网站时,插件中会显示详细信息:

包含(参数,类型,请求头,主机端口协议等)

                  

     学到了这里,我们直接分析一个现成的安全burp插件工具:

     实战案例分析2个burp安全插件:

      **分析案例1:**https://github.com/Daybr4ak/ShiroScan

   

   这款工具主要用于检测是否使用Shiro框架,以及是否存在shiro key,如果存在key,我们才好方便下一步跑利用链。

 直接下载源码:

   主要看BurpExtender.java这个文件,idea导入相关代码文件

   发现除了BurpExtender类,其余都是接口类。它这个项目没有使用maven工程,使用的是导入接口

    

  首先是查看实现类:

  继承和实现了如下接口

  

extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController

  我们知道java实现接口,必须实现接口的对应方法:

   

其中doPassiveScan为被动扫描,doActiveScan为主动扫描,因为shiroscan是被动扫描器,所以这里的大部分业务逻辑都会写在doPassiveScan中。

再往下说:

其中比较重要的就是IScannerCheck接口

ITab, IMessageEditorController为GUI设计,我们只需要copy。

自定义部分字段:

   

 

  大部分都是GUI需要使用的字段,可以学习了解,也可以不学习,其余定义的字段,在前面我们已经学到。

  字段定义如下:  

private IBurpExtenderCallbacks callbacks;
    private IExtensionHelpers helpers;
    private PrintWriter stdout;
    private JSplitPane mjSplitPane;
    private List<TablesData> Udatas = new ArrayList<TablesData>();
    private List<Ulist> ulists = new ArrayList<Ulist>();
    private IMessageEditor HRequestTextEditor;
    private IMessageEditor HResponseTextEditor;
    private IHttpRequestResponse currentlyDisplayedItem;
    private URLTable Utable;
    private JScrollPane UscrollPane;
    private JSplitPane HjSplitPane;
    private JPanel mjPane;
    private JTabbedPane Ltable;
    private JTabbedPane Rtable;

  查看接口所需要实现的方法:

  

   

  加载插件就会显示这部分内容,即上面标注的代码含义。

  继续往下看代码:

  

   

  为ui图形化界面,这块先忽略,晚点再看。继续往下看代码:

  注册扫描器,注释写的很清楚了。因为想要使用被动/主动扫描功能,必须先注册。

  往下看被动扫描操作函数,因为shiro检测是被动扫描,所以几乎所有核心逻辑的实现都会在doPassiveScan函数内:

  我们都知道shiro检测的特征是:cookie中输入rememberMe=1,响应中输出rememberMe=deleteMe;

  

  那么我们的检测逻辑很简单:cookie中植入rememberMe=1,响应中如果包rememberMe=deleteMe

  即存在使用了shiro框架,存在shiro框架后,随之检测key

  核心逻辑第一部分:

  

byte\[\] request = baseRequestResponse.getRequest();
        // 设置参数
        IParameter newParameter = helpers.buildParameter("rememberMe","1", (byte) 2);
        // 为request包添加设置好的参数
        byte\[\] newRequest = helpers.updateParameter(request, newParameter);
        // 创建一个新的HTTP请求
        IHttpService httpService = baseRequestResponse.getHttpService();
        IHttpRequestResponse newIHttpRequestResponse \= callbacks.makeHttpRequest(httpService,newRequest);
        // 获取新HTTP请求的响应
        byte\[\] response = newIHttpRequestResponse.getResponse();

  这段很重要,代表着设置cookie中,植入参数rememberMe=1

IParameter newParameter = helpers.buildParameter("rememberMe","1", (byte) 2);

  

  这个2,代表声明的参数类型是cookie。

  

  所以这里的代码还可以这样写:

  

  继续往下解读:

  带着新的rememberMe=1后,开始走到判断响应包含逻辑:

  

if (getRememberMeNumber(response) > 0 && checUrl(httpService.getHost(), httpService.getPort()))

  

   重复url检测,对url做去重:

  

 

  符合条件就新增host和端口到gui中:

  

  都是gui操作,继续往下看,看业务逻辑,发现shiro框架后,下面就是检测key:

  检测出来,就在Scanner的issue中新增,在GUI中新增

  

List<Object> mes = FindKey(newIHttpRequestResponse, getRememberMeNumber(response));

  跟进FinkKey:

public List<Object> FindKey(IHttpRequestResponse baseRequestResponse, int num){
        try {
            SimplePrincipalCollection simplePrincipalCollection \= new SimplePrincipalCollection();
            byte\[\] exp = getBytes(simplePrincipalCollection);
            String\[\] keys \= new String\[\]{
                    "kPH+bIxk5D2deZiIxcaaaA==", "Z3VucwAAAAAAAAAAAAAAAA==", "wGiHplamyXlVB11UXWol8g==", "2AvVhdsgUs0FSA3SDFAdag==", "3AvVhmFLUs0KTA3Kprsdag==", "4AvVhmFLUs0KTA3Kprsdag==", "bWljcm9zAAAAAAAAAAAAAA==", "WcfHGU25gNnTxTlmJMeSpw==", "fCq+/xW488hMTCD+cmJ3aQ==", "kPv59vyqzj00x11LXJZTjJ2UHW48jzHN",
                    "6ZmI6I2j5Y+R5aSn5ZOlAA==", "1QWLxg+NYmxraMoxAXu/Iw==", "a2VlcE9uR29pbmdBbmRGaQ==", "5aaC5qKm5oqA5pyvAAAAAA==", "1AvVhdsgUs0FSA3SDFAdag==", "5RC7uBZLkByfFfJm22q/Zw==", "3AvVhdAgUs0FSA4SDFAdBg==", "a3dvbmcAAAAAAAAAAAAAAA==", "eXNmAAAAAAAAAAAAAAAAAA==", "U0hGX2d1bnMAAAAAAAAAAA==",
                    "Ymx1ZXdoYWxlAAAAAAAAAA==", "L7RioUULEFhRyxM7a2R/Yg==", "UGlzMjAxNiVLeUVlXiEjLw==", "bWluZS1hc3NldC1rZXk6QQ==", "ZUdsaGJuSmxibVI2ZHc9PQ==", "7AvVhmFLUs0KTA3Kprsdag==", "MTIzNDU2Nzg5MGFiY2RlZg==", "OY//C4rhfwNxCQAQCrQQ1Q==", "bTBANVpaOUw0ampRWG43TVJFcF5iXjdJ", "FP7qKJzdJOGkzoQzo2wTmA==",
                    "nhNhwZ6X7xzgXnnZBxWFQLwCGQtJojL3", "LEGEND-CAMPUS-CIPHERKEY==", "r0e3c16IdVkouZgk1TKVMg==", "ZWvohmPdUsAWT3=KpPqda", "k3+XHEg6D8tb2mGm7VJ3nQ==", "U3ByaW5nQmxhZGUAAAAAAA==", "tiVV6g3uZBGfgshesAQbjA==", "ZAvph3dsQs0FSL3SDFAdag==", "0AvVhmFLUs0KTA3Kprsdag==", "25BsmdYwjnfcWmnhAciDDg==",
                    "3JvYhmBLUs0ETA5Kprsdag==", "5AvVhmFLUs0KTA3Kprsdag==", "6AvVhmFLUs0KTA3Kprsdag==", "6NfXkC7YVCV5DASIrEm1Rg==", "cmVtZW1iZXJNZQAAAAAAAA==", "8AvVhmFLUs0KTA3Kprsdag==", "8BvVhmFLUs0KTA3Kprsdag==", "9AvVhmFLUs0KTA3Kprsdag==", "OUHYQzxQ/W9e/UjiAGu6rg==", "aU1pcmFjbGVpTWlyYWNsZQ==",
                    "bXRvbnMAAAAAAAAAAAAAAA==", "5J7bIJIV0LQSN3c9LPitBQ==", "bya2HkYo57u6fWh5theAWw==", "f/SY5TIve5WWzT4aQlABJA==", "WuB+y2gcHRnY2Lg9+Aqmqg==", "3qDVdLawoIr1xFd6ietnwg==", "YI1+nBV//m7ELrIyDHm6DQ==", "6Zm+6I2j5Y+R5aS+5ZOlAA==", "2A2V+RFLUs+eTA3Kpr+dag==", "6ZmI6I2j3Y+R1aSn5BOlAA==",
                    "SkZpbmFsQmxhZGUAAAAAAA==", "2cVtiE83c4lIrELJwKGJUw==", "fsHspZw/92PrS3XrPW+vxw==", "XTx6CKLo/SdSgub+OPHSrw==", "sHdIjUN6tzhl8xZMG3ULCQ==", "O4pdf+7e+mZe8NyxMTPJmQ==", "HWrBltGvEZc14h9VpMvZWw==", "rPNqM6uKFCyaL10AK51UkQ==", "Y1JxNSPXVwMkyvES/kJGeQ==", "lT2UvDUmQwewm6mMoiw4Ig==",
                    "MPdCMZ9urzEA50JDlDYYDg==", "xVmmoltfpb8tTceuT5R7Bw==", "c+3hFGPjbgzGdrC+MHgoRQ==", "ClLk69oNcA3m+s0jIMIkpg==", "Bf7MfkNR0axGGptozrebag==", "1tC/xrDYs8ey+sa3emtiYw==", "ZmFsYWRvLnh5ei5zaGlybw==", "cGhyYWNrY3RmREUhfiMkZA==", "IduElDUpDDXE677ZkhhKnQ==", "yeAAo1E8BOeAYfBlm4NG9Q==",
                    "cGljYXMAAAAAAAAAAAAAAA==", "2itfW92XazYRi5ltW0M2yA==", "XgGkgqGqYrix9lI6vxcrRw==", "ertVhmFLUs0KTA3Kprsdag==", "5AvVhmFLUS0ATA4Kprsdag==", "s0KTA3mFLUprK4AvVhsdag==", "hBlzKg78ajaZuTE0VLzDDg==", "9FvVhtFLUs0KnA3Kprsdyg==", "d2ViUmVtZW1iZXJNZUtleQ==", "yNeUgSzL/CfiWw1GALg6Ag==",
                    "NGk/3cQ6F5/UNPRh8LpMIg==", "4BvVhmFLUs0KTA3Kprsdag==", "MzVeSkYyWTI2OFVLZjRzZg==", "CrownKey==a12d/dakdad", "empodDEyMwAAAAAAAAAAAA==", "A7UzJgh1+EWj5oBFi+mSgw==", "c2hpcm9fYmF0aXMzMgAAAA==", "i45FVt72K2kLgvFrJtoZRw==", "66v1O8keKNV3TTcGPK1wzg==", "U3BAbW5nQmxhZGUAAAAAAA==",
                    "ZnJlc2h6Y24xMjM0NTY3OA==", "Jt3C93kMR9D5e8QzwfsiMw==", "MTIzNDU2NzgxMjM0NTY3OA==", "vXP33AonIp9bFwGl7aT7rA==", "V2hhdCBUaGUgSGVsbAAAAA==", "Q01TX0JGTFlLRVlfMjAxOQ==", "Is9zJ3pzNh2cgTHB4ua3+Q==", "SDKOLKn2J1j/2BHjeZwAoQ==", "NsZXjXVklWPZwOfkvk6kUA==", "GAevYnznvgNCURavBhCr1w=="};
            for (int i = 0; i < keys.length; i++) {
                String rememberMe \= shiroEncrypt(keys\[i\], exp);
                IParameter newParameter \= helpers.buildParameter("rememberMe", rememberMe, (byte) 2);
                byte\[\] newRequest = helpers.updateParameter(baseRequestResponse.getRequest(), newParameter);
                IHttpService httpService \= baseRequestResponse.getHttpService();
                IHttpRequestResponse newIHttpRequestResponse \= callbacks.makeHttpRequest(httpService, newRequest);
                byte\[\] response = newIHttpRequestResponse.getResponse();
                if (getRememberMeNumber(response) < num) {
                    return Arrays.asList("\[+\] Found Shiro Key:" + keys\[i\],newIHttpRequestResponse);
                }
            }
        } catch (Exception e) {
            stdout.println(e);
        }
        return Arrays.asList("\[-\] Not Found Shiro Key...", baseRequestResponse);
    }

  这里的检测逻辑还是容易看懂的

1.构造一个继承 PrincipalCollection 的序列化对象。
2.key正确情况下不返回 deleteMe ,key错误情况下返回 deleteMe 。

  所以会有这个逻辑

  

  key检测参考:http://www.lmxspace.com/2020/08/24/一种另类的shiro检测方式/

  核心逻辑到此结束。

  再往下就是GUI自定义Table和显示数据存储,直接copy即可。

  

  数据存储:

  

  总结:

1.GUI可以抄
2.修改参数->重放->生成新的数据 (buildParameter,updateParameter,makeHttpRequest,getResponse)

分析burp插件2:https://github.com/YoDiDi/cors-jsonpz

  这里不看GUI了,GUI代码直接copy即可。看核心逻辑怎么写的

  首先是继承:

extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController

  看下GUI定义类:

  发现GUI声明类几乎一致。说明作者有参考另一个作者的代码

  因为是检测jsonp,那么稍微要比shiro检测复杂一些,shiro检测的话,cookie里面输入rememberMe即可,而jsonp检测,肯定要处理下请求后缀:

  这里的作者是获取请求头的第一行:

String firstrequest\_header = request\_header.get(0); //第一行请求

        if(firstrequest\_header.contains(".png") || firstrequest\_header.contains(".js") || firstrequest\_header.contains(".jpg") || firstrequest\_header.contains(".jpeg") || firstrequest\_header.contains(".svg")  || firstrequest\_header.contains(".mp4") || firstrequest\_header.contains(".css") || firstrequest\_header.contains(".mp3")        ){
            return null;
        }

else里面的逻辑

String\[\] firstheaders = firstrequest\_header.split(" ");
            if(firstheaders\[1\].contains("callback="))
                firstheaders\[1\] = firstheaders\[1\].replace("callback=","callback=testjsonp"); // 原始请求含有callback,直接替换
            else {
                if (firstheaders\[1\].endsWith("?"))
                    firstheaders\[1\] = firstheaders\[1\] + "callback=testjsonp"; // 含有参数的项,?结尾
                else if (firstheaders\[1\].contains("?") && !firstheaders\[1\].endsWith("?"))
                    firstheaders\[1\] = firstheaders\[1\] + "&callback=testjsonp"; // 含有参数的项,含有?且不是?结尾
                else
                    firstheaders\[1\] = firstheaders\[1\] + "?callback=testjsonp"; // 含有参数的项,直接参数后面加callback参数
            }
                        //重新设置header,封装到request\_header中
                        request\_header.set(0,firstheaders\[0\] + " " + firstheaders\[1\] + " " + firstheaders\[2\]);

  这里作者的注释写的很清楚了。

  三种情况

?callback=\* 替换成 callback=testjsonp
?a=1&callback=testjsonp
?callback=testjsonp

  简单解释代码含义:

  以空格分割 

String firstrequest\_header = request\_header.get(0);
String\[\] firstheaders \= firstrequest\_header.split(" ");
GET /ssrf.php?a=1 HTTP/1.1

  

  按照burpsuite的显示方式,firstheaders[1]一定是路径,对路径进行判断即可。

  因为他又做了cors劫持的检测:

// 去除源请求包里的Origin参数
            /\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*删除header\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
            request\_header.removeIf(header \-> header.startsWith("Origin"));
            request\_header.add("Origin: baitdu.com"); // 请求头增加

    

  注释写的很清楚。这块代码,自己在平时开发的时候,也能用得上。

  修改了headers,删除了原有的请求头,修改成了新的唯一性的Origin,下面就是具体的逻辑判断

  正常的获取body方法,这段代码可以在https://github.com/bit4woo/burp-api-drops中找到:

  

   作者这里采用了获取body方法1

  

  再往下是核心逻辑:重新构建整个请求header

String reqMethod = this.helpers.analyzeRequest(baseRequestResponse).getMethod();
    //stdout.println(newParameter);
    ////构建一个请求、响应消息,常用于拦截请求并修改其中的参数时会使用
    byte\[\] newRequest = this.helpers.buildHttpMessage(request\_header, request\_bodys);
    IHttpService httpService \= baseRequestResponse.getHttpService();
    IHttpRequestResponse newIHttpRequestResponse \= this.callbacks.makeHttpRequest(httpService, newRequest);
    byte\[\] response = newIHttpRequestResponse.getResponse();
    IResponseInfo analyzedResponse \= helpers.analyzeResponse(response);

  buildHttpMessage然后makeHttpRequest:

  此方法生成包含指定标头和消息体的 HTTP 消息。如果适用,将根据主体的长度添加或更新 Content-Length 标头

  

  

  可参考:

https://portswigger.net/burp/extender/api/burp/iextensionhelpers.html

https://portswigger.net/burp/extender/api/burp/iburpextendercallbacks.html

 修改重建请求后,就是做判断逻辑:

 定义了三个变量

int IsCorsControl = 0;
int IsCorstrue = 0;
int IsJsonp = 0;

  然后遍历请求headers,符合这些条件就变量就自增

  通过这段代码,我们就能知道了cors检测的逻辑,这里不多说了,代码写的很清楚了。

  继续往下看

  如果变量>0,就在GUI中输出信息

this.Udatas.add(new TablesData(row, reqMethod, url.toString(), this.helpers.analyzeResponse(response).getStatusCode() + "", "Cors vuln  " + corsword, newIHttpRequestResponse, httpService.getHost(), httpService.getPort()));

  其实他可以直接输出结果的,但是他做了一层标识符的操作

  这层标识符的深意:jsonp劫持和cors劫持误报很多。只有存在敏感信息的response,才有价值。

    

    这里的检测逻辑是这样的

1.替换header,让特殊变量自增
2.拿到特殊的自增变量,二次判断输出,输出的是必须有敏感标识符的jsonp和cors劫持

    

  如果IsJsonp>1,说明是存在jsonp劫持的:

  

  这个插件的判断劣势如下:

1.存在callback,但是不一定存在jsonp劫持,可以说存在jsonp
2.如果要jsonp劫持的话,需要删除referer
3.需要判断请求header\[1\]中是否存在csrf\_token等字段
4.最后的标识符判断可以修改成regexp正则匹配
。。。。

  自己动手写一个简单的漏洞检测程序,案例如下:

    web缓存xfh头攻击

  

  原理很简单,请求头插入X-Forwarded-Host: asdasd.com ,如果response中包含asdasd.com即存在web cache xfh头注入

      来试试

  

  

  

  GUI部分直接依葫芦画瓢抄即可。

  完整代码:  

package burp;

import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionHelpers;

import javax.swing.\*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.awt.\*;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class BurpExtender extends AbstractTableModel implements IBurpExtender, IScannerCheck, ITab, IMessageEditorController{
    private IBurpExtenderCallbacks callbacks;

    private IExtensionHelpers helpers;

    private PrintWriter stdout;

    private JSplitPane mjSplitPane;

    private List<TablesData> Udatas = new ArrayList<>();

    private IMessageEditor HRequestTextEditor;

    private IMessageEditor HResponseTextEditor;

    private IHttpRequestResponse currentlyDisplayedItem;

    private URLTable Utable;

    private JScrollPane UscrollPane;

    private JSplitPane HjSplitPane;

    private JSplitPane HjSplitPane2;

    private JPanel mjPane;

    private JTabbedPane Ltable;

    private JTabbedPane Rtable;

    private JPanel panel1;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        this.callbacks = callbacks;
        this.helpers = callbacks.getHelpers();
        this.stdout = new PrintWriter(callbacks.getStdout(), true);
        callbacks.setExtensionName("cache\_vuln\_test");
        this.stdout.println("===========================");
        this.stdout.println("\[+\]   load successful!     ");
        this.stdout.println("\[+\]   cache vulnerability test v0.1       ");
        this.stdout.println("\[+\]   code by test     ");
        this.stdout.println("\[+\]  ");
        this.stdout.println("===========================");
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mjSplitPane \= new JSplitPane(JSplitPane.VERTICAL\_SPLIT);

                Utable \= new URLTable(BurpExtender.this);
                UscrollPane \= new JScrollPane(Utable);

                HjSplitPane \= new JSplitPane();
                HjSplitPane.setDividerLocation(0.5D);
                Ltable \= new JTabbedPane();
                HRequestTextEditor \= BurpExtender.this.callbacks.createMessageEditor(BurpExtender.this,false);
                Ltable.addTab("Request",HRequestTextEditor.getComponent());
                Rtable \= new JTabbedPane();
                HResponseTextEditor \= BurpExtender.this.callbacks.createMessageEditor(BurpExtender.this,false);
                Rtable.addTab("Response",HResponseTextEditor.getComponent());
                HjSplitPane.add(Ltable,"left");
                HjSplitPane.add(Rtable,"right");

                mjSplitPane.add(UscrollPane,"left");
                mjSplitPane.add(HjSplitPane,"right");
                BurpExtender.this.callbacks.customizeUiComponent(mjSplitPane);
                BurpExtender.this.callbacks.addSuiteTab(BurpExtender.this);
            }
        });
        callbacks.registerScannerCheck(this);
    }




    @Override
    public IHttpService getHttpService() {
        return this.currentlyDisplayedItem.getHttpService();
    }

    @Override
    public byte\[\] getRequest() {
        return this.currentlyDisplayedItem.getRequest();
    }

    @Override
    public byte\[\] getResponse() {
        return currentlyDisplayedItem.getResponse();
    }

    @Override
    public List<IScanIssue> doPassiveScan(IHttpRequestResponse iHttpRequestResponse) {
        byte\[\] request = iHttpRequestResponse.getRequest();
        URL url \= this.helpers.analyzeRequest(iHttpRequestResponse).getUrl();
        String method \= this.helpers.analyzeRequest(iHttpRequestResponse).getMethod();
        IRequestInfo iRequestInfo \= this.helpers.analyzeRequest(request);
        List<String> headers = this.helpers.analyzeRequest(request).getHeaders();
        //删除header
        headers.removeIf(header -> header.startsWith("X-Forwarded-Host"));
        //新增一个header头
        headers.add("X-Forwarded-Host: asdasd.com");
        int bodyOffset = iRequestInfo.getBodyOffset();
        byte\[\] byte\_Request = iHttpRequestResponse.getRequest();
        String request2 \= new String(byte\_Request);
        String body \= request2.substring(bodyOffset);
        byte\[\] byte\_body = body.getBytes();

        byte\[\] newRequest = this.helpers.buildHttpMessage(headers, byte\_body);
        IHttpService httpService \= iHttpRequestResponse.getHttpService();
        IHttpRequestResponse newIHttpRequestResponse \= this.callbacks.makeHttpRequest(httpService, newRequest);
        byte\[\] response = newIHttpRequestResponse.getResponse();
        String string\_response \= new String(response);
        this.stdout.println(string\_response);
        if(string\_response.contains("asdasd.com")){
            synchronized (this.Udatas) {
                int row = this.Udatas.size();
                this.Udatas.add(new TablesData(row, method, url.toString(), this.helpers.analyzeResponse(response).getStatusCode() + "", "cache vuln" , newIHttpRequestResponse));
                fireTableRowsInserted(row, row);
                List<IScanIssue> issues = new ArrayList<>(1);
                return issues;
            }
        }
        return null;
    }

    @Override
    public List<IScanIssue> doActiveScan(IHttpRequestResponse iHttpRequestResponse, IScannerInsertionPoint iScannerInsertionPoint) {
        byte\[\] request = iHttpRequestResponse.getRequest();

        return null;
    }

    @Override
    public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) {
        if (existingIssue.getIssueName().equals(newIssue.getIssueName()))
            return -1;
        else return 0;
    }

    @Override
    public String getTabCaption() {
        return "cache\_scan";
    }

    @Override
    public Component getUiComponent() {
        return mjSplitPane;
    }

    @Override
    public int getRowCount() {
        return this.Udatas.size();
    }

    @Override
    public int getColumnCount() {
        return 5;
    }

    public String getColumnName(int columnIndex) {
        switch (columnIndex) {
            case 0:
                return "#";
            case 1:
                return "Method";
            case 2:
                return "URL";
            case 3:
                return "Status";
            case 4:
                return "Issue";
        }
        return null;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        TablesData datas \= this.Udatas.get(rowIndex);
        switch (columnIndex) {
            case 0:
                return Integer.valueOf(datas.Id);
            case 1:
                return datas.Method;
            case 2:
                return datas.URL;
            case 3:
                return datas.Status;
            case 4:
                return datas.issue;
        }
        return null;
    }

public class URLTable extends JTable{
    public URLTable(TableModel tableModel) {
        super(tableModel);
    }

    public void changeSelection(int row, int col, boolean toggle, boolean extend) {
        TablesData dataEntry \= BurpExtender.this.Udatas.get(convertRowIndexToModel(row));
        HRequestTextEditor.setMessage(dataEntry.requestResponse.getRequest(), true);
        HResponseTextEditor.setMessage(dataEntry.requestResponse.getResponse(),false);
        currentlyDisplayedItem \= dataEntry.requestResponse;
        super.changeSelection(row, col, toggle, extend);
    }
}

public static class TablesData {
    final int Id;
    final String Method;
    final String URL;
    final String Status;
    final String issue;
    final IHttpRequestResponse requestResponse;
//        final String host;
//        final int port;

    public TablesData(int id, String method, String url, String status, String issue,IHttpRequestResponse requestResponse) {
        this.Id = id;
        this.Method = method;
        this.URL = url;
        this.Status = status;
        this.issue = issue;
        this.requestResponse = requestResponse;
//            this.host = host;
//            this.port = port;
    }
    }
}

  演示运行效果:

  测试demo:http://119.45.227.86/header.php

  

  抓包访问,查看GUI:

    

  至此,本人先分享到这里。

    此文只能算burpsuite插件入门文章,想要更深入的学习,了解burpsuite插件开发,可以移步到:https://github.com/pmiaowu  进行深层次的学习,参考其他人写的burpsuite插件也可以。   

TRANSLATE with x

English

Arabic

Hebrew

Polish

Bulgarian

Hindi

Portuguese

Catalan

Hmong Daw

Romanian

Chinese Simplified

Hungarian

Russian

Chinese Traditional

Indonesian

Slovak

Czech

Italian

Slovenian

Danish

Japanese

Spanish

Dutch

Klingon

Swedish

English

Korean

Thai

Estonian

Latvian

Turkish

Finnish

Lithuanian

Ukrainian

French

Malay

Urdu

German

Maltese

Vietnamese

Greek

Norwegian

Welsh

Haitian Creole

Persian

 

TRANSLATE with

COPY THE URL BELOW

Back

EMBED THE SNIPPET BELOW IN YOUR SITE

Enable collaborative features and customize widget: Bing Webmaster Portal

Back

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2