[JS reverse hundred examples] XHR breakpoint debugging, Steam login reverse

Posted by Frapster on Wed, 15 Dec 2021 00:33:38 +0100

statement

All contents in this article are for learning and communication only. The packet capturing content, sensitive website and data interface have been desensitized. It is strictly prohibited to use them for commercial and illegal purposes, otherwise all the consequences have nothing to do with the author. If there is infringement, please contact me and delete them immediately!

Reverse target

  • Target: Steam login

  • Home page: aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2lu

  • Interface: aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v

  • Reverse parameters:

    Form Data:

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

Reverse process

Packet capture analysis

Go to Steam's login page and enter an account and password to log in. The packet capture location is aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v. POST request. In Form Data, donotcache is a 13 bit timestamp. The password is encrypted. captchagid and rsatimestamp don't know what it is. captcha_text is the verification code:

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

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

You can find that the rsatimestamp parameter of the login request is the timestamp here, and other parameters will be used later.

XHR breakpoint location

In this case, we use the XHR breakpoint to locate the location of encryption. First, we know what XHR is. XHR's full name 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 by using the browser console.

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

There are two methods 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 XHR/fetch Breakpoints on the right, as shown in the following figure. It has been successfully disconnected:

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

Parameter inversion

No matter which XHR method is used, the location is the same. Check the Call Stack on the right, that is, the Call Stack, and check the called function step by step in login JS, you can find the statement var encryptedpassword = RSA encrypt(password, pubKey);, Very obvious RSA encryption:

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 the results are the data returned by the previous getrsakey request:

RSA.getPublicKey and RSA Encrypt is RSA getPublicKey and encrypt methods of RSA in JS:

The entire RSA JS is copied for local debugging. It will prompt that BigInteger is not defined. When you put the mouse over it, you will see that JSBN is used The methods in jsbn.js will be more troublesome if they are deducted one by one. The whole jsbn.js JS file code can be copied:

Complete code

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

**The following only demonstrates part of the key code and cannot be run directly** Full 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 functions are omitted here

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
}

// Test sample
// 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 = 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler'
login_url = 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler'
get_rsa_key_url = 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler'
render_captcha_url = 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler'
refresh_captcha_url = 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler'

headers = {
    'Host': 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler',
    'Origin': 'Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler',
    'Referer': 'Desensitization, complete code attention 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):
    # Get gid first
    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']

    # Carry gid to obtain verification code
    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('Please enter the verification code: ')
    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('Please enter login account: ')
    password = input('Please enter the login password: ')

    # Get cookies
    cookies = get_cookies()

    # Get verification code and gid
    captcha, gid = get_captcha(cookies)

    # Obtain key and other information required for RSA encryption
    rsa_key_dict = get_rsa_key(username, cookies)

    # Get encrypted password
    encrypted_password = get_encrypted_password(password, rsa_key_dict)

    # Log in with user name, encrypted password, cookies, verification code, etc
    login(username, encrypted_password, cookies, rsa_key_dict, captcha, gid)


if __name__ == '__main__':
    main()

Topics: Python Javascript crawler Python crawler