某平台登录和支付接口分析与脚本实现

admin 2024年5月17日23:15:14评论3 views字数 11840阅读39分28秒阅读模式

本文转自FreeBuf.COM

站点数据已脱敏,使用 xxx 表示 本文基于黑箱分析,部分内容非有效动作,仅作为排错以及验证过程展示

** **

目标数据获取流程(用户态)

用户登录

https://xxx/plat/login?synAccessSource=h5&loginFrom=h5&type=logout

某平台登录和支付接口分析与脚本实现

跳转到主界面

https://xxx/plat/shouyeUser
某平台登录和支付接口分析与脚本实现

然后点击付款

https://xxx/plat/pay?nodeId=15&loginFrom=h5
某平台登录和支付接口分析与脚本实现

要获取的数据为40778129653692506121即 查看数字 那部分内容 而条形码与二维码的解析内容也是这串数字

过程分析

数据包分析

某平台登录和支付接口分析与脚本实现

(已过滤资源与 js 文件) 其中标红数据包(id 37,68)初步判断为关键有效项 下面分别分析

数据包 id:37

某平台登录和支付接口分析与脚本实现

认证包,POST 请求携带用户名与密码,返回包含关键参数access_token

数据包 id:68

某平台登录和支付接口分析与脚本实现

websocket 连接请求 仅有单向通讯,客户端向服务器发消息,传输数据为

某平台登录和支付接口分析与脚本实现

其中userId内容正是我们需要的支付码

推测支付码为客户端本地生成(可能是将用户 id 与时间进行加密后作为支付码),再发送给服务器进行登记使用

代码审阅

由于网页 js 文件进行了压缩,仅有单行,不方便调试,于是先保存到本地。

文件结构

├─ 付款码.html
└─ 付款码_files
        app.892a663e.css
        app.b45024b7.js.下载
        cardpay.svg
        chunk-0a3f7412.5241d975.js.下载
        chunk-0b9fab22.d78e378c.js.下载
        chunk-10eb720c.7887c8cc.css
        chunk-10eb720c.cb614830.js.下载
        chunk-1bfe6064.69249a75.css
        chunk-1bfe6064.9ebc95bb.js.下载
        chunk-2d0cf502.d3b50d1b.js.下载
        chunk-2d21a7f5.caf0d7aa.js.下载
        chunk-2d21d0c2.b51727b1.js.下载
        chunk-2d22673a.f40a8a36.js.下载
        chunk-2d22a11c.9403ebcb.js.下载
        chunk-4518e542.6dba3aad.js.下载
        chunk-4cc56ecd.865baaef.css
        chunk-4cc56ecd.dec2ba16.js.下载
        chunk-5dde23d5.3ff654ee.js.下载
        chunk-5dde23d5.c5454cce.css
        chunk-76ee7552.59423d06.js.下载
        chunk-fa993c88.7cdb37fb.css
        chunk-fa993c88.cf44569d.js.下载
        chunk-fe09330e.143bcc46.css
        chunk-fe09330e.2ac3e7c4.js.下载
        chunk-vendors.4c0400c5.css
        chunk-vendors.95895f96.js.下载
        loading.gif
        paycode.000ea068.js.下载

Edge 浏览器Ctrl+S保存,发现依赖项文件夹 付款码_files 内的 js 文件都增加了.下载后缀来防止误执行

使用 cmd 命令(PowerShell 不可用)去除.下载后缀

ren _.js.下载 _.

数据溯源

根据 WebSocket 内容首先在文件夹内搜索userId,寻找数据源头

某平台登录和支付接口分析与脚本实现

排查到引用了this.qrcode变量(chunk-fa993c88.cf44569d.js L1936) 继续溯源搜索qrcode

某平台登录和支付接口分析与脚本实现

qrcode有 4 处赋值语句,而这 4 处处理流程基本相似,下面对其一处(chunk-fa993c88.cf44569d.js L1795)进行分析

某平台登录和支付接口分析与脚本实现

进入判断后 o = this.codes.barcode 然后 L1777 进行一次过滤,L1783 删除首元素,如果仍有元素,则将首元素赋值给this.qrcode(L1795)

网页调试

为验证猜想,L1767 与 L1801 打断点debugger

var e = this;debugger;

}, 300));debugger;

Fiddler 开启 AutoResponder 用本地修改过的文件替换网页原先的 chunk-fa993c88.cf44569d.js 文件(注意浏览器 DevTools 开启禁用缓存)

某平台登录和支付接口分析与脚本实现

可惜刷新页面后,并没有停止在断点处,可能是没有通过此处进行赋值, 于是继续测试之前 4 处赋值语句中的其他位置

某平台登录和支付接口分析与脚本实现

(已加入调试语句)

campusCardAfter函数内断点会在每次页面刷新时调用,

某平台登录和支付接口分析与脚本实现
image

能够看到a中存放的正是我们想要的支付码,其数据源是this.codes.barcode

继续探查barcode变量,发现并没有明显的赋值过程(有也只是类似先复制出一个barcode对象,然后进行处理,再重新赋值给barcode

但是找到这部分代码(chunk-fa993c88.cf44569d.js L1708-L1710)

codeslocalStorage.getItem("barcode")
        ? JSON.parse(localStorage.getItem("barcode"))
        : [],

发现本地存储中存有barcode变量,里面存放着支付码信息。(实际上应该先清空localStorage,再进行测试)

localStorage.barcode = [
    {
        expires172800,
        barcode: [
            "40556116680367244682",
            "40427500396723912935",
            "40532966131530106739",
            "40616042672184885167",
            "40292904039749786708",
            "40467887275518089314",
            "40090871415248895341",
            "40274724330740455508",
            "40026998262309530846",
            "40074626173082122138",
            "40289421604037124807",
            "40020004343507194680",
            "40990309021750279243",
            "40336601800307009738",
            "40752092092809061117",
            "40018889369138192361",
            "40525889113799032635",
            "40844062976732763809",
            "40775401545052749823",
            "40946320320688595353",
        ],
        time1692168476261,
        icon"cardpay",
        id1,
    },
];

在读取本地存储前打断点,然后删除存储后继续运行(或者删除后强制刷新页面) 发现出现了一个新的网络包,在之前的抓包中未出现过,正是我们需要的支付码数据包。

某平台登录和支付接口分析与脚本实现

脚本实现

由上述分析,需要至少发送三个数据包来模拟客户端进行操作

  1. 1. 认证请求包

  2. 2. paycode 请求包

  3. 3. websocket 通讯包

python 实现

代码已脱敏,不可运行,仅供参考

import requests
import json
import time
import websockets
import asyncio


async def send_qrcode(qrcode):
    async with websockets.connect(
        "wss://xxx/websocket/mobile_service_platform/qrcode_ykt/payCode"
    ) as ws:
        ws_data = {
            "appCode""mobile_service_platform",
            "module""qrcode_ykt",
            "userId": qrcode,
        }
        s = json.dumps(ws_data)
        await ws.send(s)
        print("send", s)


if __name__ == "__main__":
    cookies = ""

    # token
    url = "https://xxx/berserker-auth/oauth/token"
    headers = {
        "Connection""keep-alive",
        "Content-Length""126",
        "sec-ch-ua"'"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
        "Content-Type""application/x-www-form-urlencoded",
        "sec-ch-ua-mobile""?0",
        "Authorization""Basic bW9iaWxlX3NlcnZpY2VfcGxhdGZvcm06bW9iaWxlX3NlcnZpY2VfcGxhdGZvcm1fc2VjcmV0",
        "User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203",
        "sec-ch-ua-platform"'"Windows"',
        "Accept""*/*",
        "Origin""https://xxx",
        "Sec-Fetch-Site""same-origin",
        "Sec-Fetch-Mode""cors",
        "Sec-Fetch-Dest""empty",
        "Referer""https://xxx/plat/login?type=logout&type=login&synAccessSource=h5&loginFrom=h5",
        "Accept-Encoding""gzip, deflate, br",
        "Accept-Language""zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
    }
    data = "username=XXXXXX&password=XXXXXX&grant_type=password&scope=all&loginFrom=h5&logintype=sno&device_token=h5&synAccessSource=h5"
    session = requests.session()
    session.headers.clear()
    session.headers.update(headers)
    response = session.post(url=url, data=data, verify=False)
    cookies = session.cookies
    content = json.loads(response.content)
    print("token", content["access_token"])
    time.sleep(3)

    # qrcode
    url1 = "https://xxx/berserker-app/ykt/tsm/batchGetBarCodeGet?account=95633&payacc=%23%23%23&paytype=1&synAccessSource=h5"
    headers1 = {
        "Connection""keep-alive",
        "sec-ch-ua"'"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
        "synjones-auth""bearer " + content["access_token"],
        "sec-ch-ua-mobile""?0",
        "User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203",
        "sec-ch-ua-platform"'"Windows"',
        "Accept""*/*",
        "Sec-Fetch-Site""same-origin",
        "Sec-Fetch-Mode""cors",
        "Sec-Fetch-Dest""empty",
        "Referer""https://xxx/plat/pay?nodeId=15&loginFrom=h5",
        "Accept-Encoding""gzip, deflate, br",
        "Accept-Language""zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
    }
    session1 = requests.session()
    session1.cookies.update(cookies)
    session1.headers.clear()
    session1.headers.update(headers1)
    response1 = session1.get(url=url1, verify=False)
    content1 = json.loads(response1.content)
    qrcode = content1["data"]["barcode"][0]
    print("qrcode", qrcode)
    time.sleep(3)

    # websocket
    asyncio.run(send_qrcode(qrcode))

js 实现(油猴)

代码已脱敏,不可运行,仅供参考

// ==UserScript==
// @name         example.com
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       BrokenClient
// @match        *://example.com/*
// @match        *://*.example.com/*
// @icon         none
// @run-at       document-end
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @require      https://cdn.jsdelivr.net/npm/[email protected]/qrcode.min.js
// @connect      xxx
// ==/UserScript==

(function () {
    "use strict";

    let token = "";
    let qrcode = "";

    //console.log(GM_info);
    //alert(GM_info.version)
    async function main() {
        token = await get_token();
        console.log("token", token);
        //alert(token);
        qrcode = await get_qrcode(token);
        console.log("qrcode", qrcode);
        //alert(qrcode);
        qrcode_img.makeCode(qrcode);

        let ws_data = {
            appCode"mobile_service_platform",
            module"qrcode_ykt",
            userId"40778129653692506121",
        };
        ws_data["userId"] = qrcode;
        let ws = new WebSocket(
            "wss://xxx/websocket/mobile_service_platform/qrcode_ykt/payCode"
        );
        ws.onopen = (params) => {
            let s = JSON.stringify(ws_data);
            console.log("send", s);
            ws.send(s);
            //alert(s);
        };
        console.log("main over");
    }

    document.getElementsByTagName("div")[0].remove();
    let div_qrcode = document.createElement("div");
    div_qrcode.id = "qrcode";
    div_qrcode.style = "position:absolute;width:80%;height:60wh;top:5rem;";
    document.body.appendChild(div_qrcode);
    const qrcode_img = new QRCode("qrcode", {
        text"qrcode",
        width0.8 * window.innerWidth,
        height0.8 * window.innerWidth,
    });
    main();
})();

function get_token() {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method"POST",
            url"https://xxx/berserker-auth/oauth/token",
            headers: {
                Connection"keep-alive",
                "Content-Length""126",
                "sec-ch-ua":
                    '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
                "Content-Type""application/x-www-form-urlencoded",
                "sec-ch-ua-mobile""?0",
                Authorization:
                    "Basic bW9iaWxlX3NlcnZpY2VfcGxhdGZvcm06bW9iaWxlX3NlcnZpY2VfcGxhdGZvcm1fc2VjcmV0",
                "User-Agent":
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203",
                "sec-ch-ua-platform"'"Windows"',
                Accept"*/*",
                Origin"https://xxx",
                "Sec-Fetch-Site""same-origin",
                "Sec-Fetch-Mode""cors",
                "Sec-Fetch-Dest""empty",
                Referer:
                    "https://xxx/plat/login?type=logout&type=login&synAccessSource=h5&loginFrom=h5",
                "Accept-Encoding""gzip, deflate, br",
                "Accept-Language""zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
            },
            data"username=xxxxxx&password=xxxxxx&grant_type=password&scope=all&loginFrom=h5&logintype=sno&device_token=h5&synAccessSource=h5",
            timeout5000,
            onload: (response) => {
                let j = JSON.parse(response.responseText);
                let token = j["access_token"];
                resolve(token);
            },
            onerror: (e) => {
                console.log(e);
            },
        });
    });
}

function get_qrcode(token) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method"GET",
            url"https://xxx/berserker-app/ykt/tsm/batchGetBarCodeGet?account=95633&payacc=%23%23%23&paytype=1&synAccessSource=h5",
            headers: {
                Connection"keep-live",
                "sec-ch-ua":
                    '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
                "synjones-auth""bearer " + token,
                "sec-ch-ua-mobile""?0",
                "User-Agent":
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203",
                "sec-ch-ua-platform"'"Windows"',
                Accept"*/*",
                "Sec-Fetch-Site""same-origin",
                "Sec-Fetch-Mode""cors",
                "Sec-Fetch-Dest""empty",
                Referer"https://xxx/plat/pay?nodeId=15&loginFrom=h5",
                "Accept-Encoding""gzip, deflate, br",
                "Accept-Language""zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
            },
            timeout5000,
            onload: (response) => {
                let j = JSON.parse(response.responseText);
                let qrcode = j["data"]["barcode"][0];
                resolve(qrcode);
            },
            onerror: (e) => {
                console.log(e);
            },
        });
    });
}

js 效果:

打开 example.com,页面会自动处理,并生成一个可以使用的支付二维码

补充

移动端使用油猴脚本:实测该js脚本不能在Via,X浏览器上使用,其搭载的扩展执行器版本过低,推荐使用狐猴浏览器安装油猴插件管理器来实现脚本运行。

结语

如有建议或优化思路,欢迎评论指正或私信

原文始发于微信公众号(合天网安实验室):某平台登录和支付接口分析与脚本实现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月17日23:15:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   某平台登录和支付接口分析与脚本实现https://cn-sec.com/archives/2046264.html

发表评论

匿名网友 填写信息