在渗透的"幽灵模式"中开启"透视"外挂

针对请求响应均加密的web网站的渗透测试解决方案

在渗透的"幽灵模式"中开启"透视"外挂

起因:手头上的乱七八糟的事情处理基本接近尾声了,想着休息休息挖挖src吧,然后选中了几个资产,打开第一个,请求响应全加密,算了懒得搞,换!第二个,又是请求响应全加密,6,那我再换一个,又你*请求响应全加密,干

ceeb653ely8h4ljtzrklvg20b407ke3m

功能页面如下所示

image-20240511151453212

获取验证码请求如下图所示,其实不光发送验证码功能,网站的每个请求都将我们实际的参数进行了加密,然后赋值给了data,紧接着还有sign做验证,同样的响应体res也进行了加密,这样对我们渗透测试来说非常的不便

image-20240511150802628

0x01 data加密分析

在讲核心内容之前,我们首先要依次分析下data和sign的生成, 由于data字符串太过常见,sign相对生僻一些,我这里直接sign:,搜索出来了5处,挨个点开看一下,最终发现下方代码,不用说它就是我们请求的时候赋值的代码,我这里先看data,可以看到将变量t赋值给了data,我们往上翻找变量t

image-20240511151107767

如图所示,逻辑不难看出,参数e一开始赋值给了变量t,紧接着对变量t进行了一个函数操作,将返回值重新赋值回了变量t,所以我大概率可以推断这个v就是加密函数

image-20240511151244537

接下来我们在t = v(t)那块打上断点,然后重新发请求触发js,可以看到该站采用了xml的传参方式,而函数调用前的t就包含我们原始请求的值,如手机号,时间戳等

image-20240511151643088

然后我们接着看看v函数,跟进到v函数里面,不用看了,一目了然,哎呦,还用的国密,sm4对称加密,加密模式为CBC

image-20240511152023018

这里其实关于响应体我们就不用分析了,盲猜也是用上方的对称算法进行解密,接下来我们验证一下

image-20240511152702050

一模一样吧

image-20240511152719034

同样尝试解密响应也成功

image-20240511152902240

0x02 sign分析

sign的相关的代码在data加密的代码附近,这里可以看到,sign是a的值,而a是在t没被加密前,调用了getRequstSign(t)函数,那么我们看一下这个函数做了什么

image-20240511153028592

代码如下,可以看到最后的返回值是将参数i进行了md5加密,而i的值,是t的值加上e(也就是我们的参数),外加字符串'VETRIP_B2C'的组合,而t的值是对this.COMPID进行md5得到的,this.COMPID我已经打印了,也是个字符串CLYTB,既然this.COMPID是固定的,那么它的md5加密也就是t的值也是固定的为d4a开头的一串值,逻辑就有了

image-20240511153422234

同样的我们可以尝试验证一下

image-20240511153933454

一样

image-20240511153949178

0x03 浏览器->Burp

上面的js分析很基础,接下来就到了本篇文章的核心内容了,针对于此网站,我测试的时候想达到在Burp测试的时候请求体的值是可视的也就是没有加密的,同样响应我也想要解密后的,这些条件满足的同时还不能影响实际使用,整体解决方案如下图所示

image-20240511224956086

首先我们想要让想达到请求和响应都明文,第一关就是将浏览器到Burp的参数为未加密状态,怎么实现呢?可以通过hook,但是还有更简单的方式,浏览器选择本地覆盖,新建,

image-20240511163120568

选择本地的一个空文件夹后,允许

image-20240511163209845

右键代码处

image-20240511163234809

这里因为我将上款的代码格式化了,不能直接用,这里面坑很多,最保险的做法是重新刷新一下,然后定位到对应js,注意千万不要格式化,右键选择保存并覆盖

image-20240511194417618

image-20240511194617735

这时候我们访问这个网站,这个网站加载这个js的时候就会加载我们本地的js

image-20240511194648110

接下来去Overrides标签页选择对应js,这时候就可以放心大胆的格式化了(这里卖个关子,当然也不能100%放心,还有某些情况是不能格式化的,具体什么情况呢,我不说?),然后将t加密这行删掉,这样就保证请求时候的data字段不会被加密了(devtools也就是f12不要关),别忘了ctrl+s保存一下修改后的js

image-20240511195121800

我们可以抓包看一下,请求中data的值是原值,没有被加密,这样我们就达到了Burp中的可视化目的

image-20240512175225336

0x04 Burp上游加解密

上面我们已经解决了请求中data字段将其变成了未加密前的原值,但是后续我们发包就是以原值发的所以是400,并且响应中的res解密操作也还没做

image-20240511195540215

接下来我们就要用到之前写rpc文章里面mitmproxy了,让其成为Burp的上游,在发请求和收响应的时候都改一下我们的值,另其加密解密,以及确保sign值正确,代码写到下边了,懒得写注释了,自己研究吧

import base64
import hashlib
import json
from gmssl import sm4
from mitmproxy import ctx

encrypt_key = b'd6a785b93b6c0d4a'
iv = b'2d91d2be3ad9e42c'
gmsm4 = sm4.CryptSM4()
def encryptSM4(value):
    gmsm4.set_key(encrypt_key, sm4.SM4_ENCRYPT)
    data_str = str(value)
    encrypt_value = gmsm4.crypt_cbc(iv,data_str.encode())
    text = base64.b64encode(encrypt_value)
    return text.decode('utf-8')

def decryptSM4(encrypt_value):
    encrypt_value = base64.b64decode(encrypt_value).hex()
    gmsm4.set_key(encrypt_key, sm4.SM4_DECRYPT)
    decrypt_value = gmsm4.crypt_cbc(iv,bytes.fromhex(encrypt_value))
    return decrypt_value.decode()

def sign(text):
    md5_hash = hashlib.md5()
    md5_hash.update(('d4a4a210ed3d3d367049d4d331883485'+text+'VETRIP_B2C').encode())
    return md5_hash.hexdigest()

#截获请求加密data,以及更新sign
def request(flow):
    if flow.request.urlencoded_form:
        data_value = flow.request.urlencoded_form['data']
        flow.request.urlencoded_form['data'] = encryptSM4(data_value)
        flow.request.urlencoded_form['sign'] = sign(data_value)

def response(flow):
    content_type = flow.response.headers.get("Content-Type", "")
    if "application/json" in content_type:
        # 解析 JSON 响应体
        response_body = flow.response.get_text()
        json_data = json.loads(response_body)
        json_data['res'] = decryptSM4(json_data['res'])
        flow.response.set_text(json_data['res'])

利用mitmproxy开代理,设置为Burp的上游代理

mitmproxy.exe -p 8081 -s .\mit.py

最终效果如下所示,这时候参数我们也可以放心大胆的修改了

image-20240511220502142


0x05 本地js修改

晚上正睡觉呢,突然想起来不对,少点东西,就腾的一下爬起来,有了下文

上面遗漏了一步,就是虽然响应是明文了,但是返回到浏览器它肯定也是明文,正常浏览器肯定会提取res的值,然后使用之前,前面肯定有个解密的步骤所以现在这样肯定有问题,我们要去把浏览器获取响应后的数据处理代码改一下

其实这个步骤应该放在漆面sign分析后,因为那时候还没改浏览器本地覆盖调试比较简单,但是改完之后其实也没太大关系了,如果你真的理解操作起来也一样

这是我们响应包,可以看出是json格式,并且呢里面有res字段,所以我们在js中怎么快速定位到响应包处理的代码呢,其实很简单,正常逻辑它肯定要拿到我们响应中的res然后并进行解密吗,所以这里直接搜"res"即可,so easy

image-20240513125143565

发现两处,将发现的两处都打上断点然后再次点击发送验证码触发断点,这里可以看到t.data就是我们从浏览器取出的json,然后它取了里面的res字段,将其赋值给了变量i,后边肯定有对其解密的代码,我们仔细看看

image-20240513125404408

紧接着这行可以看到对i调用了函数b,那想都不用想那个b函数肯定是解密函数,然后解密后取出了里面res值,然后发现下边那行也调用了b(i),所以这两处都需要改

image-20240513130415271

然后我们想想我们这里怎么改,这是我们正常浏览器返回的json,它是解密后的,所以直接动两处代码就好了

image-20240513125801680

image-20240513130536535

然后这时候再触发,擦,抛异常了,然后研究了一通,发现问题所在,就是这块的JSON.parse,JSON.parse() 是 JavaScript 中的一个内置函数,用于将 JSON 字符串解析为 JavaScript 对象;而这里出错的原因就是i已经是JavaScript 对象了,所以就不用多此一举了

image-20240513134024152

如下

image-20240513134238595

成功,一切正常,继续睡觉

image-20240513134335208

  • 发表于 2024-05-27 09:56:58
  • 阅读 ( 5022 )
  • 分类:渗透测试

3 条评论

china_sean
太强了
请先 登录 后评论
攻DD
刚还刷到一个上新闻的
什么?
请先 登录 后评论
请先 登录 后评论
小惜渗透
小惜渗透

8 篇文章

站长统计