一次前端RSA加密之使用Mitmdump自动化加解密渗透测试

admin 2024年1月10日15:01:10评论9 views字数 5296阅读17分39秒阅读模式

写作背景

在一次实战渗透测试中,使用burp抓包遇到了js加密,通过js逆向找到了加密算法,最终利用mitmdump联动burpsuite实现了自动化加解密

遇到的问题

在得到授权的前提下,心情愉悦地打开burp抓包,进行日常地抓包改包测试,但是测试时发现请求体和响应体均被加密了,且密文都是d1,d2,d3格式,这就难受了≧ ﹏ ≦

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308164635930

js逆向(大佬可直接越过此部分看Mitmdump)

遇到上面地问题,首先当然是得定位加密函数了,F12打开浏览器控制台,直接搜索:"d1:"

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308165347802

可以看到此处可以获得d1,d2,d3,其中d1是由a的值获取的,d2是由s的值获取的,d3是由u的值获取的,那么我们直接下断点在a的生成处,重新刷新网页,在a处断下,F9单步步入

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试

先是来到v["f"]的生成处,再单步两次回到之前的界面,再单步一次即可进入Object(v["f"])函数内

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308165734530

可以看出来是使用了cbc模式,猜测使用了AES加密,此时我们在右侧作用域查看一下变量值:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308165822099

可以推测e为key值,i为iv值(e和i的值为前面n和o的值,后面会讲n和o怎么生成的),t为加密前的请求体值,但是此处有一个问题:iv值通常应该是16位才对啊,看来此处不能直接调用aes解密了(没学过密码学只能偷偷哭泣了/(ㄒoㄒ)/),看来只能硬抠代码了

这里d1的值已经弄明白了,我们再去看看d2和d3的值是怎么生成的,此处还是刷新网页,重新断下:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308170156718

可以看到d2也就是s的值是由Object(v["c"])函数生成的,传入两个参数,生成d2时传入n和r,生成d3时传入o和r,而r的值在上面可以看出来是rsa值,此时看一下这个值是啥:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308170329231

已经明确了rsa的值,此处推测是服务端的公钥,那么再来看n和o的值,可以看出来是调用y(32)函数生成的,跟进查看:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308170515117

简简单单的根据"0123456789ABCDEFabcdef"生成的32位随机值,那么到这里,加密过程就已经弄清楚了:

d1是由n和o作为key和iv值,对原始报文使用aes加密生成的,d2由服务端的公钥和随机值n生成的,d3由服务端的公钥和随机值o生成的

那么我们前面说到了响应体也是有d1,d2,d3,那么再去看解密流程,同样的思路分析下来可以知道:

响应体中的d2可以使用客户端私钥解密出n,d3可以使用客户端私钥解密出o,d1可以使用n和o解密出原始报文(而私钥的获取方式同上公钥获取一样,下断点即可查看,解密函数就在加密函数下方

咳咳,这个加解密,属实厉害,接下来就是抠js代码了

抠js代码

这个过程属实痛苦,js挨个抠的话,还好,但是我们仔细观察一下这个网站,发现是webpack的,vue开发的网站,硬抠肯定得掉不少头发,此处讲一个快捷的webpack通用抠代码的方法:

首先抠webpack入口,通常会在html主页面内嵌入,此处讲一个快速定位入口函数的方法,在索引页面内搜索call,挨个查看一下,会有如下类似代码:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image.png

将函数抠下来,放在js文件里,定义成如下格式:

var test_module;
(function(e) {
var u={};
function f(c) {
if (u[c])
return u[c].exports;
var n = u[c] = {
i: c,
l: !1,
exports: {}
};
// console.log(c)
return e[c].call(n.exports, n, n.exports, f),
n.l = !0,
n.exports
}
test_module=f;
})({
//此处留下贴之后抠的代码
})

在开头写个test_module是为了后面好调用,接下来就是抠加密和解密函数,先抠aes的加密函数,往上找找到aes加密函数的最前面

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308192049978

往上找到如图所示位置:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308192113699

往下找到如图所示位置

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308192151896
"4b2a": function(t, e, i) {
     "use strict";
     i.d(e, "a", (function() {
         return n
    }
    )),
     i.d(e, "c", (function() {
         return o
    }
    )),
     i.d(e, "b", (function() {
         return a
    }
    )),
     i.d(e, "d", (function() {
         return u
    }
    )),
     i.d(e, "f", (function() {
         return c
    }
    )),
     i.d(e, "e", (function() {
         return f
    }
    ));
     i("d3b7");
     var r = i("7c74").sm2;
     function n() {
         var t = r.generateKeyPairHex();
         return new Promise((function(e) {
             t && e(t)
        }
        ))
    }
     function o(t, e) {
         var i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1
          , n = "04" + r.doEncrypt(t, e, i);
         return n
    }
     function a(t, e) {
         var i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1
          , n = r.doDecrypt(t.substring(2), e, i);
         return n
    }
     var s = i("7c74").sm3;
     function u(t) {
         var e = s(t);
         return e
    }
     var h = i("7c74").sm4;
     function c(t, e, i) {
         var r = h.encrypt(t, e, {
             mode: "cbc",
             iv: i
        });
         return r
    }
     function f(t, e, i) {
         var r = h.decrypt(t, e, {
             mode: "cbc",
             iv: i
        });
         return r
    }
},

抠下来代码如上,放置在之前抠的入口函数下的后面的中括号里,接下来就是挨个抠,js逆向俗称:缺啥补啥。为了方便,查看缺啥补啥,我们在入口处修改如下图所示:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308192404280

这样做的好处是会提示我们缺啥函数,再去js文件里抠出来,抠的过程中会遇到:i("d3b7");如果是像上面这样调用函数且没有声明变量去获取返回值,可以把(i("d3b7"))注释掉,不然还得挨个抠下来,会很麻烦,而且也用不上,遇到后面这个情况得保留(例:h=i("d3b7")),接下来的过程便不多说了,漫长的抠代码过程

这部分算是讲完了,那么最后贴一个调用方法(var test=test_module("7a74");然后就可以用test去调用其方法了):

function test_enc(t, e, i) {
var test=test_module("7a74");
var r = test.encrypt(t, e, {
mode: "cbc",
iv: i
});
return r
}

mitmdump中间人代理

抠完代码后,我们直接使用mitmdump对burp做上下游代理,这样我们可以在mitmdump代理层去修改数据包,然后在burp层看到的就是我们日常渗透未加密的数据,是真的很方便

该方法适用于如下三个场景(其中app和小程序需能看到加解密或防篡改的源代码):

  1. web网页端做了防篡改以及加解密
  2. app端做了防篡改以及加解密
  3. 小程序端做了防篡改以及加解密

这里往下叙述共分为三个模块:

第一个模块为编写使用mitmdump的脚本

第二个模块为响应体解密,此处使用二级代理即可(即burp上游代理)

第三个模块为请求体以及响应体加解密,此处需使用三级代理(即burp上下游代理)

示例脚本

编写好js的加解密脚本后,使用如下模板(修改加解密函数以及request和response函数为适应自己的加解密代码,并命名为test.py):

import execjs
from mitmproxy import flowfilter
from mitmproxy.http import HTTPFlow

#解决js代码中中文编码错误
import subprocess
import json
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')

with open("test.js","r",encoding="utf-8") as f:
js_code=f.read()
f.close()
ctx=execjs.compile(js_code)

#请先修改公钥
publickey=""
#请先修改私钥
privatekey=""
def enc(data):
[d1,d2,d3]=ctx.call("req_enc",data,publickey)
return [d1,d2,d3]
def dec(d1,d2,d3):
result=ctx.call("res_dec",d1,d2,d3,privatekey)
return result

class FilterFlow:

def request(self, flow):
    if flow.request.url.startswith("https://x.x.x.x/"):
        req=flow.request.get_text()
        if '"d1":' in req:
            return
        print("加密数据前:".format(req))
        data=enc(req)
        d1=data[0]
        d2=data[1]
        d3=data[2]
        json_data={"d1":d1,"d2":d2,"d3":d3}
        result=json.dumps(json_data)
        print("加密数据后:".format(result))
        flow.request.set_text(result)

def response(self, flow:HTTPFlow):
    # print(flow.response.get_text())
    if flow.request.url.startswith("https://x.x.x.x/"):
        resp=flow.response.get_text()
        data=json.loads(resp)
        d1=data["d1"]
        d2=data["d2"]
        d3=data["d3"]
        result=dec(d1,d2,d3)
        flow.response.set_text(result)


addons = [
FilterFlow(),
]

响应体解密

此处仅需设置burp上游代理,方法如下:

mitmdump -p 9090 -s .test.py --ssl-insecure

burp端设置:User Option => Upstream Proxy Servers => Add

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308160158798

请求体响应体加解密

此处需设置burp上下游代理,burp上游代理开启同上,此处只讲开启burp下游代理,即开启mitmdump的上游代理(需新开一个终端):

mitmdump -p 7070 -s .test.py  --mode upstream:https://127.0.0.1:8080 --ssl-insecure

这样就算配置完成了,在burp中已经可以正常测试了,如果需要跑sqlmap等其它工具,请将流量代理至burp即可,如:

python sqlmap.py -u "https://x.x.x.x/?id=1" --batch --proxy=http://127.0.0.1:8080

最后贴图纪念一下成果,已经可以正常加解密进行渗透测试了:

一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
image-20230308161345524

参考文章

利用mitmproxy+burpsuite实现请求和响应加解密

原文始发于微信公众号(不懂安全的果仁):一次前端RSA加密之使用Mitmdump自动化加解密渗透测试

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月10日15:01:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一次前端RSA加密之使用Mitmdump自动化加解密渗透测试http://cn-sec.com/archives/2336353.html

发表评论

匿名网友 填写信息