头图

[100 cases of JS reverse engineering] XHR breakpoint debugging, Steam login reverse

K哥爬虫
中文

statement

All the content in this article is for learning and communication only. The captured content, sensitive URLs, and data interfaces have been desensitized, and it is strictly forbidden to use them for commercial or illegal purposes. Otherwise, all the consequences arising therefrom will have nothing to do with the author. Infringement, please contact me to delete it immediately!

Reverse target

  • Target: Steam login
  • Homepage: aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2lu
  • Interface: aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v
  • Reverse parameters:

    Form Data:

    password: MzX419b8uvaNe//lkf+15sx6hnLD/L1BX......
    captchagid: 5718995253934681478
    rsatimestamp: 374533150000

Reverse process

Packet capture analysis

Steam came to the login page, just enter a login account password, Ethereal to the login interface aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v, POST request, Form Data in, donotcache is 13 time stamp, password password is encrypted handled, captchagid and rsatimestamp do not know What, captcha_text is the verification code:

01.png

We noticed that above the login request, there is also a getrsakey request, which is obviously related to RSA encryption. It should be to get parameters such as key. You can see that the return value is similar to:

{
  "success":true,
  "publickey_mod":"b1ae3215684fd66207415e39810dcbda75c143dc8c4497994db51591ed5bd17dbaf75e1e......", 
  "publickey_exp":"010001",
  "timestamp":"288093900000",
  "token_gid":"c304e76a58481ad12"
}

02.png

Here you can find the login request rsatimestamp parameter is here timestamp , other parameters will be used later.

XHR breakpoint positioning

In this case, we use XHR breakpoints to locate the encrypted location. First, let’s understand what XHR is. The full name of XHR is XMLHttpRequest. XHR can update the web page without reloading the page, request and receive data from the server after the page has been loaded. It is the basis of Ajax and belongs to the special request type of Ajax. XHR requests can be filtered using the browser console.

Since it is an XHR breakpoint, this method can only be used for XHR requests. This is also the disadvantage of this method. Through the XHR breakpoint, the after the encryption processing is completed, and the request is ready to be sent. The advantage of this is that we can track the stack and find the encrypted place relatively easily.

There are two ways to locate XHR breakpoints. The first is to intercept a part of the URL after finding the URL to send the request. Under the Source panel, add the URL you intercepted in the XHR/fetch Breakpoints on the right, as shown in the figure below. Broken down:

03.png

In the second method, in the Network panel, click XHR to filter XHR requests. In the Initiator item, you can see the called JS. Move the mouse over the JS, and you can see the call stack. Click the first one to enter the sending request The location is the same as the first method. It should be noted that XHR filtering is not necessarily accurate, but as long as the JS can be seen in the Initiator item, it means that it can be followed up for debugging. If the request is sent through a Form form or other methods, the Initiator item will display Other , You can’t debug this way at this time.

04.png

Parameter reverse

No matter which one is used, the location of the previous two methods of XHR is the same. Check the Call Stack on the right side, that is, the call stack, step by step to view the called function, in login.js, you can find the statement var encryptedPassword = RSA.encrypt(password, pubKey); , very obvious RSA encryption:

05.png

You can rewrite the key code to facilitate local debugging:

function getEncryptedPassword(password, results) {
    var pubKey = RSA.getPublicKey(results.publickey_mod, results.publickey_exp);
    password = password.replace(/[^\x00-\x7F]/g, '');
    var encryptedPassword = RSA.encrypt(password, pubKey);
    return encryptedPassword
}

After finding the encrypted location, you can bury the breakpoint, cancel the XHR breakpoint, and re-debug. You can see that results is the data returned by the previous getrsakey request:

06.png

RSA.getPublicKey and RSA.encrypt are the getPublicKey and encrypt methods of RSA in rsa.js:

07.png

08.png

09.png

Copy the entire rsa.js for local debugging, it will prompt that BigInteger undefined. When you put the mouse on it, you will see that the methods in jsbn.js are used. It will be more troublesome if you deduct one function one by one, so directly add the entire jsbn.js Just copy the file code:

10.png

11.png

Complete code

GitHub pays attention to K brother crawler, and continues to share crawler-related code! Welcome star! https://github.com/kgepachong/

following 1613eb7b7586dd only demonstrates part of the key code and cannot be run directly! complete code warehouse address: https://github.com/kgepachong/crawler/

JavaScript encryption key code architecture

navigator = {};

var dbits;

// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary & 0xffffff) == 0xefcafe);

// (public) Constructor
function BigInteger(a, b, c) {}

// return new, unset BigInteger
function nbi() {}

// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.

// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i, x, w, j, c, n) {}

// 此处省略 N 个函数

var RSAPublicKey = function ($modulus_hex, $encryptionExponent_hex) {};

var Base64 = {};

var Hex = {};

var RSA = {};

function getEncryptedPassword(password, results) {
    var pubKey = RSA.getPublicKey(results.publickey_mod, results.publickey_exp);
    password = password.replace(/[^\x00-\x7F]/g, '');
    var encryptedPassword = RSA.encrypt(password, pubKey);
    return encryptedPassword
}

// 测试样例
// var results = {
//     publickey_exp: "010001",
//     publickey_mod: "b1c6460eb07d9a6a9de07e2d7afbbe36f30b7196a4a13b7f069e8bc6be3217fe368df46ee506ad4bbaf4190a13d3937b7cc19d081fa40c3cb431d94956804b2c80aad349fa9f95254c899d905aaaab54e7bbe95159b400fde541ec6828df76f0c7a226b38651853f6cdc67dc46e7fc3253d819e0ece8aae8551a27ebbb9f8a579ba1c4f52b69fc6605c8e11b0c00e32043c7675e268815f491be48ee644670d2d632077f8ff09d7a4928e5187d6e33279760f23b0b72a4e2928154f87326e5a57541b91862b3916e4972313ad764608d9628793eef3a0a8dcdd1ab6b908d32f56f830262fd33ed6b441e6b1e0c945508461e9c083cb10d8069f9539ca70fdd33",
//     success: true,
//     timestamp: "370921200000",
//     token_gid: "3d1df3e102d1a1d2"
// }
//
// console.log(getEncryptedPassword("12345678", results))

Python login key code

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


import time

import execjs
import requests
from PIL import Image


index_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
login_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
get_rsa_key_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
render_captcha_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
refresh_captcha_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'

headers = {
    'Host': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
    'Origin': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
    'Referer': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
    'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
session = requests.session()


def get_cookies():
    response = session.get(url=index_url, headers=headers)
    cookies = response.cookies.get_dict()
    print(cookies)
    return cookies


def get_captcha(cookies):
    # 首先获取 gid
    data = {'donotcache': str(int(time.time() * 1000))}
    refresh_captcha_response = session.post(url=refresh_captcha_url, data=data, cookies=cookies, headers=headers)
    gid = refresh_captcha_response.json()['gid']

    # 携带 gid 获取验证码
    params = {'gid': gid}
    render_captcha_response = session.get(url=render_captcha_url, params=params, cookies=cookies, headers=headers)

    with open('code.png', 'wb') as f:
        f.write(render_captcha_response.content)
    image = Image.open('code.png')
    image.show()
    captcha = input('请输入验证码: ')
    return captcha, gid


def get_rsa_key(username, cookies):
    data = {
        'donotcache': str(int(time.time() * 1000)),
        'username': username
    }
    response = session.post(url=get_rsa_key_url, data=data, cookies=cookies, headers=headers).json()
    print(response)
    return response


def get_encrypted_password(password, rsa_key_dict):
    with open('encrypt.js', 'r', encoding='utf-8') as f:
        steampowered_js = f.read()
    encrypted_password = execjs.compile(js).call('getEncryptedPassword', password, rsa_key_dict)
    print(encrypted_password)
    return encrypted_password


def login(username, encrypted_password, cookies, rsa_key_dict, captcha, gid):
    data = {
        'donotcache': str(int(time.time() * 1000)),
        'password': encrypted_password,
        'username': username,
        'twofactorcode': '',
        'emailauth': '',
        'loginfriendlyname': '',
        'captchagid': gid,
        'captcha_text': captcha,
        'emailsteamid': '',
        'rsatimestamp': rsa_key_dict['timestamp'],
        'remember_login': False,
        # 'tokentype': '-1'
    }
    print(data)
    response = session.post(url=login_url, data=data, cookies=cookies, headers=headers)
    print(response.text)


def main():
    username = input('请输入登录账号: ')
    password = input('请输入登录密码: ')

    # 获取 cookies
    cookies = get_cookies()

    # 获取验证码和 gid
    captcha, gid = get_captcha(cookies)

    # 获取 RSA 加密所需 key 等信息
    rsa_key_dict = get_rsa_key(username, cookies)

    # 获取加密后的密码
    encrypted_password = get_encrypted_password(password, rsa_key_dict)

    # 携带 用户名、加密后的密码、cookies、验证码等登录
    login(username, encrypted_password, cookies, rsa_key_dict, captcha, gid)


if __name__ == '__main__':
    main()

阅读 342

Python网络爬虫、JS 逆向等相关技术研究与分享。

61 声望
13 粉丝
0 条评论
你知道吗?

Python网络爬虫、JS 逆向等相关技术研究与分享。

61 声望
13 粉丝
文章目录
宣传栏