[JS reverse hundred examples] Ether Rock airdrop interface

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

  • Objective: AES256 encryption analysis of air drop interface of Ether Rock (a digital currency)
  • Home page: aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Av
  • Interface: aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Atc3VibWl0
  • Reverse parameter: Form Data: content: U2FsdGVkX1/XnffSPZONOHb... key: jrwBwX2ll38bu/FFql+bAUYrRG8Ij

backward analysis

Go to the airdrop page, randomly enter an ETH wallet address and click submit to capture the packet to the submission interface. The POST request and the content and key parameters in the Form Data have been encrypted, as shown in the following figure:

In the old method, you try to search directly, and there are many results, which is not conducive to rapid positioning. The XHR breakpoint is easy to locate the encrypted location, as shown in the figure below:

Step by step analysis, first define the content object:

var content={
    address:$(this).find('input[name=address]').val(),
    ref:$(this).find('input[name=ref]').val(),
    uuid:uuid,
    tz:tz,
    tz_offset:tz_offset,
    screen:window.screen.width+'x'+window.screen.height+'x'+window.screen.colorDepth,
    user_agent:navigator.userAgent,
    cpu:navigator.hardwareConcurrency,
    lang:navigator.language||navigator.userLanguage,
};

Address is the wallet address, ref and uuid are empty, tz is the time zone, tz_offset is the time zone offset, that is, the difference between the current time zone and Greenwich mean time (GMT). Screen is the screen related information, user_agent is the browser information, cpu is the number of processors, and lang is the language. These values can be fixed except address.

Next, a key is defined: var key=random_string(36);, Follow up random_string() method, you can see that some random value and power operations are carried out, and you can copy them directly, as shown in the following figure:

Then, the defined content and the generated key are encrypted by AES256: content=AES256.encrypt(JSON.stringify(content),key); Here, AES256 generally refers to AES encryption with a key length of 32 bytes (256 bit / 8), but don't be confused by the name. Let's follow it:

You can see that the h.AES.encrypt() method is actually called. Looking up at this h, you can see that it is a reference node-cryptojs-aes , it supports AES symmetric key encryption, which is relatively simple here. We can directly introduce this library locally. So far, the encryption method of content has been found.

Next, look at the key value, which is simpler. It is obvious that the jsencrypt library is used to RSA encrypt the key of the 36 bit string originally generated. Similarly, the library can be directly referenced locally.

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 code

function randomString(N) {
    if (!parseInt(N, 10)) N = 6;
    var rs = Math.floor(Math.pow(36, N) * Math.random()).toString(36);
    return (Math.pow(10, N) + rs).substr(-N);
}

var h = require("node-cryptojs-aes").CryptoJS
    , p = {
    stringify: function (b) {
        var e = h.enc.Hex.parse(b.salt.toString()).toString(h.enc.Latin1);
        b = b.ciphertext.toString(h.enc.Latin1);
        return h.enc.Latin1.parse("Salted__" + e + b).toString(h.enc.Base64)
    },
    parse: function (b) {
        b = h.enc.Base64.parse(b).toString(h.enc.Latin1);
        if ("Salted__" !== b.substr(0, 8))
            throw Error("Error parsing salt");
        var e = b.substr(8, 8);
        b = b.substr(16);
        return h.lib.CipherParams.create({
            ciphertext: h.enc.Latin1.parse(b),
            salt: h.enc.Latin1.parse(e)
        })
    }
};

var e = randomString(36);

function getContent(address) {
    var b = JSON.stringify({
        "address": address,
        "ref": "",
        "uuid": "",
        "tz": "Asia/Shanghai",
        "tz_offset": 8,
        "screen": "1920x1080x24",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
        "cpu": 8,
        "lang": "zh"
    })
    return h.AES.encrypt(b, e, {
        format: p
    }).toString()
}

function getKey() {
    JSEncrypt = require("jsencrypt")
    var crypt = new JSEncrypt();
    var pub = [
        '-----BEGIN PUBLIC KEY-----',
        'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVmYQhCYTnnkTPRMI5Ad3vfad9',
        'lhjzOU92FZ3reUiN/vmqP/wC1DKKExYDsqa+w5xBP0AjGkfDWk3q4PlWu0UsBGZx',
        '62Gvt0ds75u8FnmLv+ufMimF4962/9Lx7uyh9g1H3/ze5ZXscWYy3gtts9d2Ga0R',
        'pl0X49Cz0JhYYicuGwIDAQAB',
        '-----END PUBLIC KEY-----',
    ];
    crypt.setPublicKey(pub.join('\n'));
    key = crypt.encrypt(e);
    return key
}

function getContentAndKey(address) {
    result = {
        "key": getKey(),
        "content": getContent(address)
    }
    return result
}

// Test sample
// console.log(getContentAndKey("xxxxxxxxxxxxxxxx"))

Python code

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-24
# @Author: WeChat official account: K brother crawler
# @FileName: airdrop_submit.py
# @Software: PyCharm
# ==================================


import execjs
import requests


def get_content_and_key(address):
    with open("get_content_and_key.js", encoding="utf-8") as f:
        ether_rock_js = f.read()
    content_and_key_dict = execjs.compile(ether_rock_js).call('getContentAndKey', address)
    return content_and_key_dict


def airdrop_submit(content_and_key_dict):
    submit_url = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
    headers = {
        "Accept": "text/html, */*; q=0.01",
        "Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
        "X-Requested-With": "XMLHttpRequest",
        "Host": "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler",
        "Origin": "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler",
    }
    data = {
        "content": content_and_key_dict["content"],
        "key": content_and_key_dict["key"]
    }
    response = requests.post(url=submit_url, data=data, headers=headers)
    print(response.text)


def main():
    address = input("Please enter ETH Wallet address collection airdrop: ")
    content_and_key_dict = get_content_and_key(address)
    airdrop_submit(content_and_key_dict)


if __name__ == '__main__':
    main()

Posted on Wed, 24 Nov 2021 07:55:56 -0500 by ts10