JsRpc+Galaxy 实现网站HTTP报文自动加解密+自动更新签名

最新发现一款Burp插件Galaxy,可以做到在请求/响应在客户端/Burp/服务端流转时加入自己的处理逻辑,这可以用来实现请求/响应自动解密。但它有一些不足的地方,比如你必须抠出报文的加密逻辑,再用Galaxy实现解密逻辑才能达到目的,而有时客户端的代码比较复杂的情况下,完全抠出来加密逻辑再实现一遍过于复杂,而JsRpc可以做到与客户端通信,那如果把它们组合起来岂不是完美。

工具简介

Galaxy

​ 可以做到在请求/响应在客户端/Burp/服务端流转时加入自己的处理逻辑,这可以用来实现请求/响应自动解密。详情见https://github.com/outlaws-bai/Galaxy

JsRpc

​ 在网站的控制台新建一个WebScoket客户端链接到服务器通信,调用服务器的接口 > 服务器会发送信息给客户端 > 客户端接收到要执行的方法执行完js代码后把获得想要的内容发回给服务器 > 服务器接收到后再显示出来。详情见https://github.com/jxhczhl/JsRpc

实现

环境

环境靶场:

http://39.98.108.20:8085/

项目地址:

https://github.com/0ctDay/encrypt-decrypt-vuls/

JS逆向分析

网站数据包如下,数据进行加密处理,请求包的请求头内三个参数requestId、timestamp、sign缺一不可,改动任何一个都失效,且无法使用repeater重放数据

浏览器控制台定位相关js代码,其中关于请求头三个参数如下:

加密js代码如下,采用AES CBC key和iv都写在js内

js内打上断点

可以看到其中n为需要传输的数据、timestamp通过Date.parse(new Date)获取、requestId通过函数p()获取、sign为a.a.MD5函数对传输的数据n加上timestamp加上requestId做MD5处理。l(n)为加密函数对传输的数据n进行加密。

控制台调用函数打印相关数据

注入JsRpc客户端及获取签名方法

在这里使用jsrpc调用接口执行js获取timestamp、requestId、sign三个请求头数据,而加密采用Galaxy自带加密模板。

jsrpc连接成功后,在浏览器控制台对相关函数进行注册

window.requestId=p
//requestId
window.v1 = v
//函数v
window.sign=a.a.MD5
//签名sign

连接通信

demo.regAction("hello",function (resolve,param) {
    n=JSON.stringify(v1(param))
    var time = Date.parse(new Date);
    var id=requestId()
    var sg = sign(n+id+time).toString()
    var data={"time":"","id":"","sign":""}
    data["time"]=time.toString()
    data["id"]=id
    data["sign"]=sg
    resolve(data);
})

这里可以直接通过浏览器访问查看对应结果

在Galaxy的hook脚本中调用JsRpc服务端

加解密

此处加密采用模板加解密python文件AES_CBC来实现

脚本内写入key、iv和对应算法

该网站内数据包采用GET和POST两种方式进行请求,且只有POST请求的数据进行加密处理,因此请求数据发送到burp解密和burp明文数据到服务器加密需要区分开

JsRpc引入

引入jsrpc需要jython引入http客户端

from org.m2sec.core.outer import HttpClient

按照格式构建引入jsrpc和替换请求头的代码

burpRequestBody=request.getBody()            #获取传输数据,也就是上文中的n
url="http://127.0.0.1:12080/go?group=zzz&action=hello&param="
jsrpcUrl=url+burpRequestBody                 #将n拼接url
jsrpcRequest=request.of(jsrpcUrl)            #构建request请求
jsrpcRespone=HttpClient.send(jsrpcRequest)   #发送数据包
jsrpcResponeJson=jsrpcRespone.getJson()      #获取返回包内数据
headData=jsrpcResponeJson["data"]            #提取data中内容
headData=json.loads(headData)                #将JSON数据的字符串转换为Python中的数据类型
time=headData["time"]                        #提取时间
requestId=headData["id"]                     #提取requestId
sign=headData["sign"]                        #提取签名
head=request.getHeaders()                    #提取原始数据包请求头
head.put("sign",sign)                        #替换sign
head.put("requestId",requestId)              #替换requestId
head.put("timestamp",time)                   #替换时间
request.setHeaders(head)                     #替换请求头

将构建好的代码写入到burp发送到服务器的函数zhong

效果图

自动加解密实现效果

重放数据实现效果

Galaxy完整的hook脚本

import json
import base64
from org.m2sec.core.utils import (
    CodeUtil,
    CryptoUtil,
    HashUtil,
    JsonUtil,
    MacUtil,
    FactorUtil,
)
from org.m2sec.core.models import Request, Response
from org.m2sec.core.outer import HttpClient
from java.lang import String


"""
内置示例,需要自定义代码文件时查看该文档:https://github.com/outlaws-bai/Galaxy/blob/main/docs/Custom.md
按 Ctrl(control) + ` 可查看内置函数
"""

ALGORITHM = "AES/CBC/PKCS5Padding"
secret = b"1234567891234567"
iv = b"1234567891234567"
paramMap = {"iv": iv}
jsonKey = "data"
log = None

def hook_request_to_burp(request):
    """HTTP请求从客户端到达Burp时被调用。在此处完成请求解密的代码就可以在Burp中看到明文的请求报文。

    Args:
        request (Request): 请求对象

    Returns:
        Request: 经过处理后的request对象,返回null代表从当前节点开始流量不再需要处理
    """
    if(request.getMethod()=="GET"):
        return request

    else:
    # 获取需要解密的数据
       encryptedData = CodeUtil.b64decode(request.getBody())
    # 调用内置函数解密
       data = decrypt(encryptedData)
    # 更新body为已加密的数据
       request.setContent(data)
       return request



def hook_request_to_server(request):
    """HTTP请求从Burp将要发送到Server时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到Server。

    Args:
        request (Request): 请求对象

    Returns:
        Request: 经过处理后的request对象,返回null代表从当前节点开始流量不再需要处理
    """
    if(request.getMethod()=="GET"):
        burpRequestBody=''
        url="http://127.0.0.1:12080/go?group=zzz&action=hello&param="
        jsrpcUrl=url+burpRequestBody
        jsrpcRequest=request.of(jsrpcUrl)     
        jsrpcRespone=HttpClient.send(jsrpcRequest)
        jsrpcResponeJson=jsrpcRespone.getJson()
        headData=jsrpcResponeJson["data"]
        headData=json.loads(headData)
        time=headData["time"]
        requestId=headData["id"]
        sign=headData["sign"]
        head=request.getHeaders()
        head.put("sign",sign)
        head.put("requestId",requestId)
        head.put("timestamp",time)
        request.setHeaders(head)
        return request
    # 获取被解密的数据
    else:
        data = request.getContent()

        burpRequestBody=request.getBody()
        url="http://127.0.0.1:12080/go?group=zzz&action=hello&param="
        jsrpcUrl=url+burpRequestBody
        jsrpcRequest=request.of(jsrpcUrl)     
        jsrpcRespone=HttpClient.send(jsrpcRequest)
        jsrpcResponeJson=jsrpcRespone.getJson()
        headData=jsrpcResponeJson["data"]
        headData=json.loads(headData)
        time=headData["time"]
        requestId=headData["id"]
        sign=headData["sign"]
        head=request.getHeaders()
        head.put("sign",sign)
        head.put("requestId",requestId)
        head.put("timestamp",time)
        request.setHeaders(head)


    # 调用内置函数加密回去
        encryptedData = encrypt(data)
    # 将已加密的数据转换为Server可识别的格式
        body = CodeUtil.b64encode(encryptedData)
    # 更新body
        request.setContent(body)
        log.info("header2: {}",request)

        return request

def hook_response_to_burp(response):
    """HTTP请求从Server到达Burp时被调用。在此处完成响应解密的代码就可以在Burp中看到明文的响应报文。

    Args:
        response (Response): 响应对象

    Returns:
        Response: 经过处理后的response对象,返回null代表从当前节点开始流量不再需要处理
    """
    # 获取需要解密的数据
    encryptedData = CodeUtil.b64decode(response.getBody())
    # 调用内置函数解密
    data = decrypt(encryptedData)
    # 更新body
    response.setContent(data)
    return response

def hook_response_to_client(response):
    """HTTP请求从Burp将要发送到Client时被调用。在此处完成响应加密的代码就可以将加密后的响应报文返回给Client。

    Args:
        response (Response): 响应对象

    Returns:
        Response: 经过处理后的response对象,返回null代表从当前节点开始流量不再需要处理
    """
    # 获取被解密的数据
    data = response.getContent()
    # 调用内置函数加密回去
    encryptedData = encrypt(data)
    # 更新body
    # 将已加密的数据转换为Server识别的格式
    body = CodeUtil.b64encode(encryptedData)
    # 更新body
    response.setContent(body)
    return response

def decrypt(content):
    return CryptoUtil.aesDecrypt(ALGORITHM, content, secret, paramMap)


def encrypt(content):
    return CryptoUtil.aesEncrypt(ALGORITHM, content, secret, paramMap)



def set_log(log1):
    """程序在最开始会自动调用该函数,在上方函数可以放心使用log对象"""
    global log
    log = log1
  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值