日高校体育(2.8.4)

Table of Contents

高校体育爪巴

起因

从这个学期开始,我们学校刷锻就要用上臭名昭著,隔壁交大大师用了都说好的 高校体育 辣!

不会真的有人想照着这个跑步吧,不会吧不会吧不会吧?

(专家提示:在适当的时候保持大量运动有助于健康生活和下半辈子的幸福。)

流量劫持

有网络通信就要尝试抓包:

  • 一定要用新版本的工具(Burpsuite 或 Fiddler)。
  • Android >=(?) 7 时系统不会信任用户安装的 CA 证书,需要把 CA 证书安装到系统证书下。
  • 对于上一条,执行 openssl x509 -inform PEM -subject_hash_old -in ******.pem | head -1 得到 hash 值(类似于 8efb32d4);重命名证书为 <hash>.0,拷贝到 /system/etc/security/cacerts/chmod 644

但是这几把东西上了 SSL Pinning 使用 OKHttp(这是结论,忽略了发现过程)

在电脑上做准备:

首先你要有个 Python 3 和对应的 pip

pip install Frida
pip install objection
pip install frida-tools

使用 frida-server(无论是 Magisk 方式还是直接把可执行文件拷贝进去):

./frida-server [-l 0.0.0.0] # 方括号内为使用网络时的监听地址

使用线缆连接时此时应能通过 frida-pa -U 查看进程。网络的话请参阅 frida 的文档。

使用 objection -g com.example.gita.gxty explore 尝试进行注入。网络的话请使用 -h-p 来指定主机名和端口。

如果成功的话,直接进行一个 android sslpinning disable 的执行来禁用 SSL Pinning

协议(?)分析

CalvinXu17 在 2019-04-21 的分析

签名

最终发送的内容主题会包括 datasign 两部分。

data 是一个最小化的 JSON 格式字符串(没有换行,没有段间空格)。

import json

def dump_dict(incoming_dict: dict) -> str:
    return json.dumps(incoming_dict, separators=(',', ':'))

sign 是一个 MD5 算法的信息摘要。构造过程为

md5(`前缀` + "data" + `你实际传输的 JSON 字符串`)
import hashlib
MD5_PREFIX = ******

def sign_request(raw_payload: str) -> str:
    md5 = hashlib.md5()
    str_to_digest = f"{MD5_PREFIX}data{raw_payload}"
    md5.update((str_to_digest).encode('ascii'))
    return md5.hexdigest()

这里 raw_payload 就是你实际发送的 data

在实际观测过程中,GET 方法不会指定 Content-Type 头; POST 方法指定 Content-Type: application/x-www-form-urlencoded

    def send_payload_get(self, endpoint: str, payload_dict: dict) -> dict:
        payload_json_str, sign = self.generate_signed_payload(payload_dict)
        r = requests.get(self.api_base_endpoint + endpoint,
                         params=(("sign", sign), ("data", payload_json_str)),
                         headers=self.real_headers)
        return json.loads(r.text)

    def send_payload_post(self, endpoint: str, payload_dict: dict) -> dict:
        payload_json_str, sign = self.generate_signed_payload(payload_dict)
        urlencoded_body_dict = {
            'sign': sign,
            'data': payload_json_str
        }
        r = requests.post(self.api_base_endpoint + endpoint,
                          data=urlencoded_body_dict,
                          headers=self.real_headers)
        return json.loads(r.text)

身份验证

  • 返回中不相关的部分会被省略。

登录

/api/reg/login

POST

{"info":"<uuid>","mobile":"<mobile>","password":"<password>","type":"<device_type>"}

Response:

{"code":"200","msg":"登录成功","data":{"userid":"<userid>","utoken":"<utoken>"}}

使用身份验证:

加入如下 HTTP 头:

"headers": {
    "BDA9F42E0C8A294ECDF5CC72AAE6A701": "1,0,0,0,0,0",
    "uuid": "<uuid>",
    "ntoken": "",
    "utoken": "<utoken>",
    "Cookie": "PHPSESSID=<php_sessionid>",
    "User-Agent": "okhttp-okgo/jeasonlzy",
    "xxversionxx": "20180601",
    "versionName": "2.8.4",
    "versionCode": "440",
    "platform": "android"
}
Nemo Xiong avatar
Nemo Xiong
ex-Cybersecurity Executor, now a student in Unimelb
comments powered by Disqus