freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

JS逆向-数据包解签名实战案例
2023-08-03 14:29:20
所属地 上海

免责声明:本文涉及到的所有技术仅用来学习交流,严禁用于非法用途,未经授权请勿非法渗透。否则产生的一切后果自行承担!

0x00 前言

通常情况下,数据包中的签名字段会包含sign字符串,如signappsign等等,然后根据字段在JS代码中寻找签名的过程,并进行分析。
以下内容为一个真实案例,撒,哈气灭路!

0x01 获取被签名数据

通过数据包可知该网站的签名字段是 sign
image.png
在浏览器中的源代码搜索字段sign,找到签名代码
首当其冲的就是app.*.js样式的文件,在app.72b81572.js文件中发现了可疑点
image.png
分析 function k(e) 可知,是要对 r 参数进行签名的,r 又跟e、t、a、n 这几个参数相关,因此要搞清楚这几个参数的值是怎么来的,所有就在函数开始的第一行就下断点来跟进。

function k(e) {
    const t = g()
      , n = m();
    let a;
    a = e ? b(e) : {};
    const r = e ? `${c.a.stringify(a.hasParams)}&${t}&time=${n}` : `&${t}&time=${n}`;
    return e = Object.assign({}, a.params, {
        sign: h()(decodeURIComponent(r)),
        time: n
    }),
    e
}

首先是 t,跟进 g 函数,执行到 return 就可以发现 t = 年 + "5616" + 月 + 日
image.png
然后就是 n,这个 e 的值呢,根据跟进可知是 cookie 里面的,不过这个 e 在本案例的作用不大,最后通过 return 就可以知道返回的值就是:当前时间戳- e
image.png
接下来就是 a,根据返回的结果可知 a 的值和 e 相同,通过分析数据包可知,e的内容即请求参数(GET)或请求体(POST)
image.png
然后 r 的值就出来了,也就是签名的明文
image.png
综上所述
r = 请求内容 + & + t + & + n(这里的请求内容需要注意的是,POST Data是Json格式的要转换为:key1=value1&key1=value1...)
其中
t = 年 + "5616" + 月 + 日
n = 当前时间戳(本案例可不减e也可成功)

0x02 分析加密算法

将断点打在签名代码行,跟进代码的执行,发现加密的类名是 Md5,所有可以计算 r 的 md5 值,与代码执行的 sign 结果进行比较。
image.png
可以看到,该网站使用的是 md5 计算的 sign
image.png

0x03 编写 mitmproxy 脚本

mitmproxy 脚本是实时加载的,因此 mitmproxy 只要带着脚本运行,就可以边调试了。
mitmproxy 使用命令

mitmdump -p 777 -s .\mitmscript.py --flow-detail 0

image.png
关于脚本的编写这里给出以下 mitmproxy 的一些常用的属性和方法
ctx:

  • ctx.log.info(): 用于在 mitmproxy 的日志中输出信息。
  • ctx.options: 用于访问 mitmproxy 的配置选项,您可以在配置文件中定义这些选项。
  • ctx.master: mitmproxy 的 Master 对象,提供了一些控制代理行为的方法。
  • ctx.proxy: mitmproxy 的 ProxyConfig 对象,提供了有关代理配置的信息。
  • ctx.client: mitmproxy 的 ClientConnection 对象,表示客户端连接的相关信息。
  • ctx.server: mitmproxy 的 ServerConnection 对象,表示服务器连接的相关信息。
  • ctx.protocol: mitmproxy 的 ProtocolHandler 对象,表示处理请求和响应的协议处理器。


    在 mitmproxy 脚本中,可以使用以下一些回调函数来处理不同阶段的 flow 对象:
  • def request(flow: mitmproxy.http.HTTPFlow) -> None: 当 mitmproxy 拦截到请求时调用此回调函数,可以获取和处理请求的各种信息。
  • def response(flow: mitmproxy.http.HTTPFlow) -> None: 当 mitmproxy 拦截到响应时调用此回调函数,可以获取和处理响应的各种信息。
  • def error(flow: mitmproxy.http.HTTPFlow) -> None: 当请求或响应出现错误时调用此回调函数,可以获取和处理错误信息。
  • def clientconnect(flow: mitmproxy.tcp.TCPFlow) -> None: 当客户端连接到 mitmproxy 时调用此回调函数,可以获取和处理客户端连接的相关信息。
  • def serverconnect(flow: mitmproxy.tcp.TCPFlow) -> None: 当 mitmproxy 连接到服务器时调用此回调函数,可以获取和处理服务器连接的相关信息。
  • flow.request.method: 获取请求的方法(GET、POST等)。
  • flow.request.scheme: 获取请求的协议(http 或 https)。
  • flow.request.host: 获取请求的主机名。
  • flow.request.port: 获取请求的端口号。
  • flow.request.path: 获取请求的路径部分。
  • flow.request.url: 获取完整的请求URL。
  • flow.request.headers: 获取请求的头部信息,是一个字典对象,可以通过键来访问特定的头部字段。
  • flow.request.cookies: 获取请求中的Cookie信息,是一个字典对象,可以通过键来访问特定的Cookie。
  • flow.request.query: 获取请求的查询参数,是一个字典对象,可以通过键来访问特定的查询参数。
  • flow.request.content: 获取请求的内容,如果请求是POST请求且带有内容,则可以通过该属性来访问请求的内容。
  • flow.request.text: 获取请求的内容,并以文本形式返回。
  • flow.request.urlencoded_form: 获取请求的URL编码表单数据,是一个字典对象,可以通过键来访问特定的表单字段。
  • flow.request.multipart_form: 获取请求的多部分表单数据,是一个列表对象,列表中的每个元素都是一个字典,表示一个表单字段。
  • flow.request.content: 获取请求的原始内容,以字节形式返回。


    附上本案例的代码供参考
from datetime import datetime
from mitmproxy import ctx
import hashlib
import random
import json
import time


class Modify:
    def __init__(self):
        self.plaintext = ''
        self.new_sign = ''
        current_datetime = datetime.now()
        self.today = f"{current_datetime.year}5616{current_datetime.month:02d}{current_datetime.day:02d}"

    def md5(self):
        md5_hash = hashlib.md5()
        md5_hash.update(self.plaintext.encode('utf-8'))
        self.new_sign = md5_hash.hexdigest()

    def request(self, flow):
        if flow.request.method == "POST":
            ctx.log.info(f'\n原POST请求体:{flow.request.text}')
            data = json.loads(flow.request.get_text())
            if data != '{}' and 'sign' in data:
                del data['sign']
                del data['time']
                for k in data:
                    self.plaintext += f"{k}={str(data[k])}&"
                timestamp = int(time.time() * 1000)
                self.plaintext += f"{self.today}&time={timestamp}"
                self.md5()

                data["sign"] = self.new_sign
                data["time"] = timestamp
                flow.request.set_text(json.dumps(data).replace(' ', ''))
                ctx.log.info(f'\n新POST请求体:{flow.request.text}')
                self.plaintext = ''
            else:
                ctx.log.info('无参数,无需改签')
        elif flow.request.method == "GET":
            ctx.log.info(f'\n原GET请求体:{flow.request.query}')
            query = flow.request.query
            if query != '{}' and 'sign' in query:
                del query['sign']
                del query['time']
                if query:
                    for k in query:
                        self.plaintext += f"{k}={str(query[k])}&"
                else:
                    self.plaintext = "&"
                timestamp = int(time.time()) * 1000
                self.plaintext += f"{self.today}&time={timestamp}"
                self.md5()

                query["sign"] = self.new_sign
                query["time"] = timestamp
                ctx.log.info(f'\n新GET请求体:{flow.request.query}')
                self.plaintext = ''
            else:
                ctx.log.info('无参数,无需改签')

    @staticmethod
    def response(flow):
        if '签名校验失败!' in flow.response.text:
            ctx.log.error(f'\n签名异常:\nurl => {flow.request.path}\n异常信息 => {flow.response.text}')


addons = [
    Modify()
]
# 渗透测试 # web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者