一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
2023-3-17 14:19:23 Author: www.freebuf.com(查看原文) 阅读量:27 收藏

写作背景

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

遇到的问题

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

image-20230308164635930

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

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

image-20230308165347802

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

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

image-20230308165734530

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

image-20230308165822099

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

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

image-20230308170156718

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

image-20230308170329231

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

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,挨个查看一下,会有如下类似代码:

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加密函数的最前面

image-20230308192049978

往上找到如图所示位置:

image-20230308192113699

往下找到如图所示位置

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逆向俗称:缺啥补啥。为了方便,查看缺啥补啥,我们在入口处修改如下图所示:

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

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

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

image-20230308161345524

参考文章

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


文章来源: https://www.freebuf.com/articles/web/360596.html
如有侵权请联系:admin#unsafe.sh