^

Un1kHacking - Burpsuite插件开发笔记

前言

之前开发了一款插件Un1kFiles,一开始的意图仅仅只是快速得从远端API获取我需要的文件信息。通过不断的更新修正,目前已经解决了断网导致菜单卡死、频繁访问接口等问题。

但是这款插件的开发是非常简单的,仅仅需要设置菜单工厂,监听点击事件,从远端api获取信息拷贝到剪切板即可。我这个不会java代码的人看了一遍rebootORZ师傅写的文章花半小时就能写出来了。

基本上Un1kFiles的生涯就到头了,没什么功能需要拓展了,需要解决的也就是一些文件编码等无关紧要的问题。

于是我想着思考设计下一款插件,一款为了快速接入云函数代理测试的插件。

设计思路

由于目前存在的一些云函数代理工具仅仅是做了流量转发,也就是你访问网站的时候一股脑儿的把流量全部通过你的vps再转发到你的云函数接口去,达到通过使用云函数代理池从而防止被攻击溯源。这样的操作是可行的,但是是存在许多缺点的。

首先是流量->vps->云函数,需要通过两层中转,第二是云函数或者是vps的性能、带宽不足,导致访问百度这种速度较快的网站都会加载全部资源导致卡死。所以我们可以简化一下,抽取其中的部分功能给repeater用即可。

编写代码

我将仔细的写明我从插件设计到遇到问题再到解决问题的一系列过程,由于我是一个java小白,希望我的开发经历能够给你有所帮助。

本文通过java代码开发,python开发未研究。

1.阅读文档

rebootORZ师傅已经把开发插件的一些基本流程写明在文章里了,通过一开始的demo,我们多试几遍基本能大致掌握常用的一些方法,包括服务注册、jar包生成等。

https://mp.weixin.qq.com/s/qSyV7m3mIBvP43jTg6TVvg

2.我们需要编写的一些代码

我们需要了解我们的需求,也就是需要在repeater模块发送http请求的时候,使用插件发送流量给云函数接口处理,最后返回信息到response即可。

而判断在哪个模块,burpsuite api给了通用方法,并且模块可以根据toolflag来判定使用。所以我们需要用到一个监听http请求的模块。

也就是IHttpListener

使用该接口前,需要先通过 IBurpExtenderCallbacks.registerHttpListener() 注册一个 HTTP 监听器,这样所有的HTTP请求和响应都会通知这个监听器,就可以在这个监听器对HTTP数据包进行分析或修改了。

代码如下:


需要注意的是,在最前面需要接口继承IHttpListener,我经常忘记这里,导致代码错误


该接口只提供了一个方法,processHttpMessage。

这个方法拥有三个属性


通过toolflag属性,判断是否为repeater模块 在repeater才生效,代码就可以写

 if(toolFlag == 0x00000040) {
   // To do here
}

这样第一个要求就轻松解决了,接着就是后面的操作,转发流量到云函数服务接口,并且取消原始请求

这里是这次开发的最大坑点,这个方法自带一个接口IHttpRequestResponse。我们需要使用这个接口去改变请求和设置响应,他的方法如下:


所以我们一开始需要使用setRequest方法去修改请求,代码如下


(截图是把响应包的设置到请求了 只需要把string的字符串变成请求包字符串即可)

虽然请求是被修改了,测试blog.happysec.cn域名下,不管repeater模块的request是什么代码,请求包都是bytes中的请求包,效果是会发送一个get请求/scf.php?body=,从而无视你的包体。

但是测试其他域名就出现问题了,由于setrequest只是修改了request中的报文,但是该请求包的service中的host、port、Protocol,还是原来的a.com,http,8081。

当目标主机host与请求包中的host不一致的时候,会发生无法访问的情况。

所以我把注意力放在了,setHttpService这个方法上面。如果我们用这个方法,把host、port、protocol改成我们的云函数相关信息,那么就可以解决我们这个问题。但由于HttpService是一个接口,不是对象,一开始由于java不太会,接口这种都忘记了,卡壳了很久。

后面通过看一些burpsuite插件项目代码,写出了解决办法。

我们通过做一个接口实现

class WSHttpService implements IHttpService

让WSHttpService类实现接口IHttpService中所有的方法,之后定义三个属性,并重载getHost,getPort,getProtocol方法。

      private String host;
        private int port;
        private String protocol;

        WSHttpService() {
            host = "blog.happysec.cn";
            protocol = "https";
            port = 443;
        }

        @Override
        public String getHost() {
            return host;
        }

        @Override
        public int getPort() {
            return port;
        }

        @Override
        public String getProtocol() {
            return protocol;
        }

在类实例化构造函数中,将三个参数默认定义为云函数的接口地址、端口、协议。

之后我们实例化一个类WSHttpService,并用setService方法重新设置messageInfo的service信息。

代码如下:

            WSHttpService iHttpService = new WSHttpService();
            messageInfo.setHttpService(iHttpService);
            IHttpService iHttpService2 = messageInfo.getHttpService();
            this.stdout.println(iHttpService.getHost());
            this.stdout.println(iHttpService.getPort());
            this.stdout.println(iHttpService.getProtocol());

重新打包插件后我们访问网站测试


发现无论请求包设置的host为啥,都走的是WSHttpService类下的host、port、Protocol。

解决了这个问题,其他的也就是写云函数接口、打包原始请求headers、发送请求包至云函数接口的简单代码即可了。

3.打包原始请求代码编写

我们需要把原始http请求报文打包发送给云函数接口,通过

String body =(helpers.bytesToString(messageInfo.getRequest())); //获取请求数据

这行代码我们可以获取字符串类型的原始请求数据,也就是


这个框里的内容

为了防止编码错误,我们使用base64对string做编码

                try {
                    base64encodedString = Base64.getEncoder().encodeToString(body.getBytes("utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }

接着我们只需要 重新修改原始请求即可


所以这样能够理解了,前面修改service的目的只是为了防止host不一致导致请求无法传递而已。

然后我们修改body后的参数为我们需要传递的base64代码即可

4.云函数代码编写

此时burp插件已经编写完毕了。

接下来就是云函数对请求包的处理。处理原始请求包提取里面的body、host、uri、method、content-type等重要参数,使用云函数重新请求即可。

这里需要注意的是,由于请求和响应都会被这个监听器捕获,所以我们需要设置条件


默认对获取响应不做处理即可。

我通过

    String body = (helpers.bytesToString(messageInfo.getRequest())); //获取请求数据包

再将body进行base64编码,这样就可以发包给云函数接口原请求的http报文。做接口的话选择很多,我这里选择php代码来写。

首先,对原请求重新发包,拿的是http报文。就需要使用socket来write,再重新发包。

这里的坑点也很多,一开始我用的是fsockopen这个函数,但是后面遇到证书错误的问题,于是改用stream_socket_client,并在上下文stream_context_create创建了一个ssl,把verify_peer_name和verify_peer都设置成false,这样就可以忽略证书文件校验了。

但是发现上传文件的时候会上传错误,某些接口网站访问空白,并且不是云函数的问题,是代码的问题。是在write的时候发生了什么不可预见错误,我通过反复diff请求包也没有找到问题,接口收到的请求包和原始请求包怎么看都是一个请求包,于是我放弃了这个思路,只能做报文解析了。然后用curl去发包

analyzeRequest给了几个好用的方法,geturl和getHeaders,这样我们可以快速获取url和头部信息,但是需要注意这里的headers的第一个元素是类似
POST /api HTTP/1.1

的一行,method也可以用getmethod获取,uri在url里,所以我们可以把headers这个list的第一个元素remove,然后都编码base64发给接口。

至此,headers、url、method的问题都可以解决,接下来就是解决body中的文件内容或者请求参数的获取了。

对于analyzeRequest,burpsuite api没有给出拿到body内容的接口。但是给了获取body的偏移量getbodyoffset。所以我们可以用以下代码获得请求body:

String sendData = body.substring(reqInfo.getBodyOffset());

也就是把请求包从偏移量开始的地方截取全部,就是body的内容了。

后面拿到值然后用curl_setopt即可,就不多写了,效果如下(解决了蛋疼的编码问题)



实际使用还是存在很多需要提升的地方,比如云函数接口是硬编码的,不是动态可变的。

导入配置我倒是思考过,但是发现需要绘制ui和使用另外的加载器,比较麻烦,还不如在burpsuite的文件夹下做一个ini的默认载入,不存在就提示用什么命令加载。

mac效果如下:



之后写一个菜单 做监听 假设我修改了配置文件中的接口 那么点击就会重载



对接口代码作修改 在响应内容中加入使用的api host 便于区分



基本上这样就可以投入使用啦~

项目地址

https://github.com/h4ckdepy/Un1kHacking