站长说:作为本站第一个投稿的文章,向宽爹 respect !

     ____. ___________________                                        
    |    |/   _____/\______   \ _______  __ ___________  ______ ____  
    |    |\_____  \  |       _// __ \  \/ // __ \_  __ \/  ___// __ \ 
/\__|    |/        \ |    |   \  ___/\   /\  ___/|  | \/\___ \\  ___/ 
\________/_______  / |____|_  /\___  >\_/  \___  >__|  /____  >\___  >
                                                            v1.0
                                                            by Asy0y0

项目概述

项目名称:JSReverse Tool v1.0

功能介绍:帮助使用者更好的进行JS加密参数的渗透测试

开发语言:Python

项目地址:https://github.com/tianjy12/JSReverseTool

项目架构

JSReverse
        ---> conf
                ---> Config.py
        ---> core
                ---> DecryptHandler.py
                ---> EncryptHandler.py
                ---> HttpHandler.py
                ---> JSFileSelector.py
        ---> scripts
                ---> JSTemplate
                            ---> JSTemplate.js
                            ---> BrowserEnvironment.js
        ---> gui
                ---> Main.py
        ---> network
                ---> ReceiveHttpResponse.py
                ---> SendHttpRequest.py
        ---> temp
                ---> temp.txt
        ---> imgs
                ---> ico.png
        ---> JSReverse.py     

core:项目功能的核心实现模块

scripts:存放提供的浏览器环境,JS模板以及用户自定义的JS模板

gui:前端界面的展示

network:HTTP请求收发的实现模块

temp:存放临时的请求体

核心代码

JSFileSelector.py

def choose_js_template(self):
    js_template_dir = 'scripts'
    js_files = [f for f in os.listdir(js_template_dir) if f.endswith('.js')]

    if not js_files:
        messagebox.showerror("Error", "No JS templates found in the JSTemplate directory.")
        return None

    selected_template = None

    def on_select(event):
        nonlocal selected_template
        selected_template = template_listbox.get(template_listbox.curselection())
        template_window.destroy()

    template_window = tk.Toplevel(self.root)
    template_window.title("选择JS模板")

    template_listbox = tk.Listbox(template_window, height=10)
    for js_file in js_files:
        template_listbox.insert(tk.END, js_file)
    template_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    template_listbox.bind('<<ListboxSelect>>', on_select)

    self.root.wait_window(template_window)

    return selected_template
  1. 显示scripts目录下的JS文件列表

  2. 选择使用的JS文件,记录文件名

  3. 文件名用于后续拼接成相对路径调用对应的JS文件

DecryptHandler.py

def run_node_decrypt(self, selected_template):
    try:
        process = subprocess.Popen(
            ['node', f'scripts/{selected_template}', 'decrypt'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )

        stdout, stderr = process.communicate()

        if process.returncode != 0:
            print(f"Error executing Node.js script: {stderr.decode('utf-8')}")
            return "Error during decryption"

        return stdout.decode('utf-8')

    except Exception as e:
        print(f"Exception while executing Node.js script: {e}")
        return "Error during decryption"
  1. 调用选中的JS模板

  2. 执行命令:node scripts/selected_file.js decrypt

  3. 获取JS文件的输出并返回

def decrypt_request_body(self, selected_template, raw_request):
        decrypted_body = self.run_node_decrypt(selected_template)

        if "\r\n\r\n" in raw_request:
            header_part, body_part = raw_request.split("\r\n\r\n", 1)
            return f"{header_part}\r\n\r\n{decrypted_body}"
        elif "\n\n" in raw_request:
            header_part, body_part = raw_request.split("\n\n", 1)
            return f"{header_part}\n\n{decrypted_body}"
        else:
            return raw_request
  1. 将请求头和请求体分割

  2. 用解密得到的明文替换原来的请求体

  3. 与请求头拼接后返回

EncryptHandler.py

def run_node_encrypt(self, json_body):
    try:
        script_path = os.path.abspath(f'scripts/{self.selected_template}')

        json_data_str = json.dumps(json_body)

        temp_file_path = os.path.abspath('temp/temp.txt')
        with open(temp_file_path, 'w') as temp_file:
            temp_file.write(json_data_str)

        process = subprocess.Popen(
            ['node', script_path, 'encrypt'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )

        stdout, stderr = process.communicate()

        if process.returncode != 0:
            print(f"Error executing Node.js script: {stderr.decode('utf-8')}")
            return "Error during encryption"

        return stdout.decode('utf-8').strip()

    except Exception as e:
        print(f"Exception while executing Node.js script: {e}")
        return "Error during encryption"
  1. 将请求体写入temp/temp.txt文件

  2. 调用选中的JS模板

  3. 执行命令:node scripts/selected_file.js encrypt

  4. JS文件读取temp/temp.txt的内容并执行

  5. 获取JS文件的输出并返回

def encrypt_request_body(self, raw_request):
    if "\r\n\r\n" in raw_request:
        header_part, body_part = raw_request.split("\r\n\r\n", 1)
    elif "\n\n" in raw_request:
        header_part, body_part = raw_request.split("\n\n", 1)
    else:
        return raw_request

    try:
        json_body = json.loads(body_part)
    except json.JSONDecodeError:
        return "Invalid JSON in request body"

    encrypted_body = self.run_node_encrypt(json_body)

    return f"{header_part}\n\n{encrypted_body}"
  1. 将请求头和请求体分割

  2. 用加密得到的密文替换原来的请求体

  3. 与请求头拼接后返回

JSTemplate.js

const fs = require('fs');

// 模拟浏览器环境
const { navigator, window, location, document, sessionStorage } = require('./JSTemplate/BrowserEnvironment');
global.navigator = navigator;
global.window = window;
global.location = location;
global.document = document;
global.sessionStorage = sessionStorage;

// 工具函数


// 明文模板
const template = {}

const mode = process.argv[2];

if (mode === "decrypt") {
    console.log(JSON.stringify(template));
    process.exit(0);
}

if (mode === "encrypt") {
    const rawRequest = fs.readFileSync('temp/temp.txt', 'utf-8');
    const jsonData = JSON.parse(rawRequest);  // 根据要加密的参数是否为JSON格式选择
    const encryptedData = window.asy0y0(jsonData); // 假设这是你的加密函数
    const encodedData = encodeURIComponent(encryptedData);  // 可选择是否进行URL编码

    console.log('data=' + encodedData + '&crc=-1309814496');  // 输出符合原请求体格式的结果
    process.exit(0);
}

其他细节

Q:为什么加密过程中,要把请求体写入temp.txt文件,再让JS从文件中读取,为什么不直接把请求体作为参数传给JS文件?

A:一是考虑到格式的问题,请求体可能比较复杂,在命令行中作为参数传递可能导致各种意想不到的问题,二是对JS参数的维护,目前的参数只有encrypt和decrypt,后续可能添加其他参数,对应新的功能模块,命令格式均为:node scripts/selected_file.js param


Q:后续有什么更新的计划吗?

A:目前比较有思路的更新计划:

  1. 增加前端界面展示的美化

  2. 新增一些功能点,针对一些细节的JS模板配置,比如是否为JSON,是否启用URL Encode等,交互性更强

  3. 新增爆破的功能,便于对加密的用户名密码等参数进行调试,但需要找一个会GoLang的人来技术支持,或者等我抽空几个GoLang编写的工具的源码再说,Python的性能太差了

  4. 遇到新的浏览器环境,当前模板配置的环境无法,满足时,会更新新的浏览器环境配置模板