2
头图
Pay attention to WeChat public account: Brother K crawler, continue to share advanced crawler, JS/Android reverse engineering and other technical dry goods!

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 consequences arising therefrom will have nothing to do with the author. Infringement, please contact me to delete it immediately!

Reverse target

  • Target: Ether Rock (a digital currency) airdrop interface AES256 encryption analysis
  • Homepage: aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Av
  • Interface: aHR0cHM6Ly9ldGhlcnJvY2submV0L2FpcmRyb3Atc3VibWl0
  • Reverse parameters: Form Data: content: U2FsdGVkX1/XnffSPZONOHb... key: jrwBwX2ll38bu/FFql+bAUYrRG8Ij...

Reverse analysis

When you come to the airdrop page, just enter an ETH wallet address, click Submit, you can capture the package to the submission interface, the content and key parameters in the POST request, Form Data are encrypted, as shown in the following figure:

01.png

The old method, try to search directly, there are many results, which is not conducive to fast positioning, XHR breakpoints, it is easy to locate the encrypted location, as shown in the following figure:

02.png

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 screen-related information, user_agent is browser information, and cpu is processing The number of devices, lang is the language. These values can be fixed except address.

Next, define a key: var key=random_string(36); , follow up the random_string() method, you can see that some random value and exponentiation operations have been performed, which can be copied directly, as shown in the following figure:

03.png

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

04.png

05.png

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

Next, look at the key value. This is even simpler. Obviously, the jsencrypt library is used. The original 36-bit string key is encrypted by RSA, and the library can be directly referenced locally.

06.png

Complete code

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

following 1619f50a02bc13 only demonstrates part of the key code and cannot be run directly! complete 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
}

// 测试样例
// console.log(getContentAndKey("xxxxxxxxxxxxxxxx"))

Python code

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-24
# @Author  : 微信公众号:K哥爬虫
# @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 = "脱敏处理,完整代码关注 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": "脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler",
        "Origin": "脱敏处理,完整代码关注 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("请输入ETH钱包地址领取空投: ")
    content_and_key_dict = get_content_and_key(address)
    airdrop_submit(content_and_key_dict)


if __name__ == '__main__':
    main()


K哥爬虫
169 声望162 粉丝

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