【技术分享】NetGear多款路由器远程命令注入漏洞分析(更新补丁分析)

阅读量242583

|

发布时间 : 2016-12-15 11:21:21

http://p5.qhimg.com/t0185de1ec0f771e174.jpg

作者:k0pwn_ko

预估稿费:600RMB(不服你也来投稿啊!)

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

0x01 前言

前两天NTP刚搞完事情,NetGear路由器(网件路由器)又来搞事了T.T。目前CERT在上周五已发布公告,“如果用户使用涉及到的路由器,建议停止使用,直到官方发布补丁修复”。此漏洞是由Acew0rm发现的,之后报给NetGear,今天,他又在Twitter上上传了关于这个漏洞利用的视频。

同时也发布了一个Exploit地址,有两个,一个是嵌入html的利用,另一个是比较直接的利用,当然,需要获取路由器的IP地址。

https://github.com/Acew0rm/Exploits/blob/master/Netgear_R7000.html

https://www.exploit-db.com/exploits/40889/

但实际上,这个漏洞影响的路由版本,远不止现在曝光的这么少,目前曝光的是R6400,R7000版本,后来CERT又曝光了R8000版本。我看了此漏洞公开后,下面有很多在使用NetGear路由的老外在聊天,其中总结了一下受漏洞影响版本的路由。

l  R6400 (AC1750): confirmed
l  R7000 Nighthawk (AC1900, AC2300): confirmed (by myself)
l  R7500 Nighthawk X4 (AC2350): confirmed (by [2])
l  R7800 Nighthawk X4S(AC2600): confirmed (by [2])
l  R8000 Nighthawk (AC3200): confirmed
l  R8500 Nighthawk X8 (AC5300): confirmed (by [2])
l  R9000 Nighthawk X10 (AD7200): confirmed (by [2])
l  R6250
l  R6700

几乎所有R系列的路由都受此漏洞影响,当然有一些R系列路由虽然受影响,但因为固件的不同,部分固件是不受此漏洞影响的。

目前此漏洞未提供补丁,官方在Twitter上的回复是正在抓紧时间修复此漏洞,因此应该还有不少设备受漏洞影响。

开始分析前,感谢Spongebobb在微博上和我的讨论,让我从莫名其妙的脑洞中跳转出来,23333。


0x02 检测方式

浏览器访问路由器地址:

http://[router-address]/cgi-bin/;uname$IFS-a

如果返回的页面是错误或者非空的,那么该路由器可能存在这个漏洞。


0x03 为了分析我掉到了好几个坑里

在昨天看到这个漏洞曝光后,我下载了对应版本的固件【_R7000-V1.0.7.2_1.1.93.chk】,分析的过程中当然碰到了不少坑,这里稍微总结一下。

关于这个漏洞,主要问题发生在/usr/sbin/httpd里,但在/www/cgi-bin/下也有一个可执行文件genie.cgi,其中也履行了CGI程序的部分功能,刚开始我比较坚定的认为在genie.cgi中,也找到了比较有趣的调用位置。

  v6 = getenv("QUERY_STRING");
  ptr = (void *)sub_A304(dword_1385C);
  if ( ptr )
  {
    v0 = sub_9560((int)v6);
    if ( v0 != -1 )
    {
      sub_9C78(v0);
      v4 = 0;
      sub_ABAC(0xB348, &v4, &v3);
    }
  }

这里调用getenv获取了QUERY_STRING环境变量,这个变量就是通过GET方法接收到URL中参数的时候,会获取参数,并且给QUERY_STRING赋值,这个setenv赋值过程是在httpd中完成的,genie.cgi只负责getenv。而随后这里调用了一个函数sub_ABAC,跟入这个函数,我发现了在这个程序中唯一一次会调用到系统函数的位置。

.text:0000ABAC                 STMFD           SP!, {R11,LR}
.text:0000ABB0                 ADD             R11, SP, #4
.text:0000ABB4                 SUB             SP, SP, #0x420
.text:0000ABB8                 STR             R0, [R11,#command]
.text:0000ABBC                 STR             R1, [R11,#var_414]
.text:0000ABC0                 STR             R2, [R11,#var_418]
.text:0000ABC4                 LDR             R0, [R11,#command] ; command
.text:0000ABC8                 MOV             R1, #aR_0 ; modes
.text:0000ABD0                 BL              popen

popen可以执行系统函数,正是符合我们exp中的条件,但是却失落的发现,这里传递的值是sub_ABAC函数第一个参数,也就是0xB348,这是一个常量。

.rodata:0000B348 aInternetSetCon DCB "internet set connection genieremote 1",0

刚开始我脑洞有点开大了,想到的是类似于php的变量覆盖,会不会是URL传入的值,由于某些原因会覆盖到这个常量,后来还是否决了这个过程,一筹莫展的时候我想到了对比一下没有漏洞的版本(后来事实证明,我分析所谓没有漏洞的版本,也是有这个漏洞的),对比的时候发现R7000以后的路由版本采取https,在看配置文件的时候无意中发现了R7000中的/usr/sbin/httpd。

按照同样的思路,我找到了httpd中有两处函数调用可能调用到了系统函数,一处是popen,另一处是system。

.plt:0000E6BC ; FILE *popen(const char *command, const char *modes)
.plt:0000E6BC popen                                   ; CODE XREF: sub_73F40+3D4_x0019_p
.plt:0000E6BC                                         ; sub_95B44+1BC_x0019_p
.plt:0000E6BC                 ADRL            R12, 0x1086C4
.plt:0000E6C4                 LDR             PC, [R12,#(popen_ptr - 0x1086C4)]! ; __imp_popen
.plt:0000D69C ; int system(const char *command)
.plt:0000D69C system                                  ; CODE XREF: sub_147A0+D2C_x0019_p
.plt:0000D69C                                         ; sub_147A0+D94_x0019_p ...
.plt:0000D69C                 ADRL            R12, 0x1076A4
.plt:0000D6A4                 LDR             PC, [R12,#(system_ptr - 0x1076A4)]! ; __imp_system

为了分析执行路径,我用了xrefs的功能,先来看看popen的。

http://p0.qhimg.com/t015a5d534a5ede73fd.png

执行路径比较简单,再来看看system的。

http://p9.qhimg.com/t0192f9641757f10e3b.png

我整个人都崩溃了……后来我想到用cgi-bin搜索一下关键字,结果真的还有收获。

.text:000110E8 off_110E8       DCD aCgiBin             ; DATA XREF: sub_100A0+1808_x0019_r
.text:000110E8                                         ; "cgi-bin/"

通过这种方法,我找到了比较外层的函数调用sub_100A0,随后终于抓出了一条线。

http://p2.qhimg.com/t013d7bd93c027b7248.png

这次对我这样对路由比较感兴趣的人来说也是一次学习的过程,下面进入对这个漏洞的详细分析。


0x04 命令注入漏洞分析

首先我们下载R系列路由器的固件。

下载地址:http://support.netgear.cn/doucument/detail.asp?id=2251

然后用binwalk -eM来迭代解压这个固件,获得这个固件的squashfs文件系统,这里生成的.squashfs文件需要用7zip来解压。

http://p7.qhimg.com/t0156aaeb2bcb747f56.png

生成之后用IDA打开/usr/sbin/httpd,跟入sub_100A0函数,在这个函数中有一处调用。

        return sub_19600((const char *)v9, v246, v4);

这里会调用到sub_19600,其中涉及到三个参数,这里v9是我比较关心的,v9是什么呢,在sub_100A0函数中其实比较容易猜测。

if ( !strstr((const char *)v9, "unauth.cgi")
        && !strstr((const char *)v9, "securityquestions.cgi")
        && !strstr((const char *)v9, "passwordrecovered.cgi")
        && !strstr((const char *)v9, "userlogin.cgi")
        && !strstr((const char *)v9, "multi_login.cgi")
        && (strncmp((const char *)v9, "cgi-bin/", 8u) || strstr((const char *)v9, "RMT_invite_")) )

在这个函数中涉及到大量的strstr子字符串比较,其中比较的内容就是某个常量和v9变量,猜测v9变量就是url的值,这里我们就假设v9的值就是exp的定义  /IP-Addr/cgi-bin/;killall$IFS’httpd’

 这里$IFS是Linux的内部域分隔符,这里可以看做是一个空格。

 那么接下来跟入sub_19600。

char *__fastcall sub_19600(const char *a1, const char *a2, int a3)
{
  const char *v3; // r6@1
  const char *v4; // r4@1
  int v5; // r5@1
  char *result; // r0@1
  v3 = a2;
  v4 = a1;
  v5 = a3;
  result = strstr(a1, "cgi-bin");
  if ( result )
  {
    if ( acosNvramConfig_match((int)"cgi_debug_msg", (int)"1") )
      printf("rn##########%s(%d)url=%srn", "handle_options", 1293, v4);
    result = (char *)sub_36C34(v3, v5, v4, 2);
  }
  return result;
}

比较简短,这里会打印一个url字符串,而url后面跟的%s就是v4,v4由a1而来,a1就是此函数第一个参数,所以第一个参数的确是url的值,接下来v4会作为第三个参数传入sub_36C34函数,漏洞就是在此函数中发生。

这里我先分段讲解这个函数中产生漏洞的整个过程,最后,我再贴上这个函数的完整伪代码。我们重点关注第三个参数v4。

进入后,首先第三个参数,也就是url会交给v6。

  v6 = a3;

然后会判断v6中是否包含cgi-bin,如果包含,则进入内部处理,这里根据exp,是存在cgi-bin的,接下来进入处理,在处理的过程中,会判断是否包含?,如果包含?,则会给v47赋值,v47这个值我们要记住,在后面设置QUERY_STRING,我们会用到,但是实际上跟此漏洞没有关系。

这里为什么要用到?,就是QUERY_STRING是CGI接收GET参数的,这里默认GET参数是在?后面,就是由此而来。

    v12 = strstr(v6, "cgi-bin");
    if ( v12 )
    {
      if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
        printf("rn##########%s(%d)rn", "netgear_commonCgi", 76);
      if ( strchr(v12, 63) )
      {
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
          printf("rn##########%s(%d)rn", "netgear_commonCgi", 80);
        v13 = strchr(v12, 63);
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
          printf("rn#############%s(%d)tmp1=%s,tmp2=%srn", "netgear_commonCgi", 83, v12, v13 + 1);
        strcpy((char *)&v47, v13 + 1);
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          printf("rn###############%s(%d)query_string=%srn", "netgear_commonCgi", 86, &v47);
        v14 = strchr(v6, 47);

当然,在exp中是不包含?的,因此这个if ( strchr(v12, 63) )

语句不成立,则不进入这个处理,看一下下面的else语句。

在else语句中会进行字符串切割,切割的就是v12,也就是cgi-bin/;killall,这里注释里我写出了切割后地址指针指向的字符串内容。

比较关心的就是v20,v21和v22,其中由于切割后,后面不再包含47,也就是/的ascii码,因此v22为0,之后会对v50进行初始化。

  else
      {
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          printf("rn##########%s(%d)rn", "netgear_commonCgi", 99);
        v19 = strchr(v12, 47);
        v20 = v19 + 1;                          // ;kill
        v21 = v19;                              // /;kill
        v22 = strchr(v19 + 1, 47);              // v22=NULL
        memset(&v50, 0, 0x40u);                 // v50init
        v23 = (char)v21;

然后就进入一系列的判断,判断的内容就是切割之后;kill后面还包含不包含/。

        if ( v21 )
          v23 = 1;
        v24 = v22 == 0;
        if ( v22 )
          v24 = v21 == 0;
        if ( v24 )

 这里显然是不包含的,因此v22的值为0,那么v24的值为1,进入在if语句处理中,会将v50赋值,也就是赋值成;killall,可以看到这个过程没有任何过滤 。

        if ( v24 )
    {
        if ( v22 )
			v25 = 0;
        else
            v25 = v23 & 1;
        if ( v25 )
            strcpy((char *)&v50, v20);
    }

随后会进入连续的goto跳转,跳转过程中主要还是打印一些信息,随后会进入到v7处理,v7处理代码挺长的,其中涉及到了QUERY_STRING环境变量的赋值,赋值内容就是v47。当然,我们漏洞的流程,由于没有?,所以不会进入这个流程。

          if ( (_BYTE)v47 )
            setenv("QUERY_STRING", (const char *)&v47, 1);

接下来就是漏洞触发的关键位置,由于我们不满足条件,就会执行下面的语句。

        v26 = "OPTIONS";
        v27 = (char *)&v53;

之后出来后会跳转,这里会拷贝v26,也就是OPTIONS到v27中,v27的值就是v53的地址值,之后跳转。

    strcpy(v27, v26);
    goto LABEL_47;

跳转之后,会进入一系列判断,判断v53的值

          if ( !strcmp((const char *)&v53, "POST") )
{
            v33 = (const char *)&unk_F062B;
            v34 = (char *)&v45;
}
          else if ( !strcmp((const char *)&v53, "OPTIONS") )
          {
          }
          else
          {
……
          }

这里省略了一部分过程,由于v53的值是OPTIONS,最后会有一处赋值,v34会赋值为v45的地址值,之后就进入漏洞触发的关键位置,这里会调用sprintf将v50,也就是我们命令的值交给v34。而v34的值就是v45地址的值,这样调用system(&45)的时候,就执行了系统命令。

          sprintf(v34, v33, &v50);
          system((const char *)&v45);
          memset(&v49, 0, 0x40u);
          memset(&v48, 0, 0x40u);
          memset(&v51, 0, 0x20u);
          memset(&v52, 0, 0x10u);

而在我们分析的过程中,没有一处对这个命令值进行限制,最后导致了命令注入漏洞的发生。下面贴上整个源码。

int __fastcall sub_36C34(const char *a1, int a2, const char *a3, int a4)
{
  v4 = a1;
  v5 = a2;
  v6 = a3;
  v7 = a4;
  v8 = fork();
  v9 = v8;
  if ( !v8 )
  {
    if ( fork() )
    {
      v10 = v9;
      goto LABEL_101;
    }
    if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
      printf("rn###############%s(%d)url=%s,method=%drn", "netgear_commonCgi", 59, v6, v7);
    v11 = fopen("/tmp/var/readydropd.conf", "r");
    if ( v11 )
    {
      fclose(v11);
    }
    else
    {
      system("cp -f /www/cgi-bin/readydropd.conf /tmp/var/");
      if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
        puts("rn###################copy readydropd.confr");
    }
    v12 = strstr(v6, "cgi-bin");
    if ( v12 )
    {
      if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
        printf("rn##########%s(%d)rn", "netgear_commonCgi", 76);
      if ( strchr(v12, 63) )
      {
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
          printf("rn##########%s(%d)rn", "netgear_commonCgi", 80);
        v13 = strchr(v12, 63);
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
          printf("rn#############%s(%d)tmp1=%s,tmp2=%srn", "netgear_commonCgi", 83, v12, v13 + 1);
        strcpy((char *)&v47, v13 + 1);
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          printf("rn###############%s(%d)query_string=%srn", "netgear_commonCgi", 86, &v47);
        v14 = strchr(v6, 47);
        if ( v14 )
        {
          v15 = &v50;
          memset(&v50, 0, 0x40u);
          strncpy((char *)&v50, v14 + 1, v13 - 1 - v14);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          {
            v16 = "rn###############%s(%d)cgi_name=%srn";
            v17 = 93;
            v18 = "netgear_commonCgi";
LABEL_34:
            printf(v16, v18, v17, v15);
            goto LABEL_40;
          }
        }
      }
      else
      {
        if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          printf("rn##########%s(%d)rn", "netgear_commonCgi", 99);
        v19 = strchr(v12, 47);
        v20 = v19 + 1;                          // ;kill
        v21 = v19;                              // /;kill
        v22 = strchr(v19 + 1, 47);              // v22=0
        memset(&v50, 0, 0x40u);                 // v50init
        v23 = (char)v21;
        if ( v21 )
          v23 = 1;
        v24 = v22 == 0;
        if ( v22 )
          v24 = v21 == 0;
        if ( v24 )
        {
          if ( v22 )
            v25 = 0;
          else
            v25 = v23 & 1;
          if ( v25 )
            strcpy((char *)&v50, v20);
        }
        else
        {
          strncpy((char *)&v50, v20, v22 - 1 - v21);// v50=;kill
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
            printf("rn#############tmp1=%s,tmp2=%s,tmp3=%s,cgi=%srn", v12, v21, v22, &v50);
          v15 = &v46;
          strcpy((char *)&v46, v22);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          {
            v16 = "rn###############%s(%d)path_info=%srn";
            v17 = 110;
            v18 = "netgear_commonCgi";
            goto LABEL_34;
          }
        }
      }
    }
LABEL_40:
    if ( v7 )
    {
      if ( v7 == 1 )
      {
        v26 = "POST";
        v27 = (char *)&v53;
      }
      else
      {
        if ( v7 != 2 )
        {
LABEL_47:
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
            printf("rn###############%s(%d)request_method=%srn", "netgear_commonCgi", 130, &v53);
          if ( (_BYTE)v46 )
            setenv("PATH_INFO", (const char *)&v46, 1);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          {
            v28 = getenv("PATH_INFO");
            printf("rn###############%s(%d)PATH_INFO=%srn", "netgear_commonCgi", 136, v28);
          }
          setenv("LD_LIBRARY_PATH", "/usr/lib", 1);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          {
            v29 = getenv("LD_LIBRARY_PATH");
            printf("rn###############%s(%d)LD_LIBRARY_PATH=%srn", "netgear_commonCgi", 140, v29);
          }
          setenv("REQUEST_METHOD", (const char *)&v53, 1);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
          {
            v30 = getenv("REQUEST_METHOD");
            printf("rn###############%s(%d)REQUEST_METHOD=%srn", "netgear_commonCgi", 144, v30);
          }
          if ( (_BYTE)v47 )
            setenv("QUERY_STRING", (const char *)&v47, 1);
          if ( !strcmp((const char *)&v53, "POST") )
          {
            v31 = fopen("/tmp/post_result", "r");
            if ( v31 )
            {
              fclose(v31);
              system((const char *)&unk_F05D8);
              if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
                puts("rn##########del post ############r");
            }
            system("rm -f /tmp/post_data.txt");
            sleep(1u);
            v32 = fopen("/tmp/post_data.txt", "w");
            if ( v32 )
            {
              fputs(v4, v32);
              fclose(v32);
            }
            v33 = (const char *)&unk_F062B;
            v34 = (char *)&v45;
          }
          else if ( !strcmp((const char *)&v53, "OPTIONS") )
          {
            v35 = fopen("/tmp/options_result", "r");
            if ( v35 )
            {
              fclose(v35);
              system("rm -f /tmp/options_result");
              if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
                puts("rn##########del option ############r");
            }
            v33 = (const char *)&unk_F06A2;
            v34 = (char *)&v45;
          }
          else
          {
            v36 = fopen("/tmp/cgi_result", "r");
            if ( v36 )
            {
              fclose(v36);
              system("rm -f /tmp/cgi_result");
              if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
                puts("rn##########delete /tmp/cgi_result ############r");
            }
            v33 = (const char *)&unk_F070F;
            v34 = (char *)&v45;
          }
          sprintf(v34, v33, &v50);
          system((const char *)&v45);//key!!!
          memset(&v49, 0, 0x40u);
          memset(&v48, 0, 0x40u);
          memset(&v51, 0, 0x20u);
          memset(&v52, 0, 0x10u);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
            printf("rn###############%s(%d)rn", "netgear_commonCgi", 200);
          if ( !strcmp((const char *)&v53, "POST") )
          {
            v37 = "/tmp/post_result";
          }
          else if ( !strcmp((const char *)&v53, "OPTIONS") )
          {
            v37 = "/tmp/options_result";
          }
          else
          {
            v37 = "/tmp/cgi_result";
          }
          v38 = fopen(v37, "r");
          if ( v38 )
          {
            if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
              printf("rn###############%s(%d)rn", "netgear_commonCgi", 211);
            while ( fgets((char *)&v44, 0xFFFF, v38) )
            {
              if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
                printf("rn###############%s(%d)rn", "netgear_commonCgi", 215);
              v39 = strstr((const char *)&v44, "Status:");
              if ( v39 )
              {
                strcpy((char *)&v49, v39 + 7);
                v40 = strchr((const char *)&v49, 10);
                if ( v40 )
                  *v40 = 0;
                if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
                  printf("rn###############%s(%d)status=%srn", "netgear_commonCgi", 223, &v49);
                sprintf((char *)&v43, "HTTP/1.1%srn", &v49);
              }
              else
              {
                strcat((char *)&v43, (const char *)&v44);
              }
            }
            fclose(v38);
          }
          strcat((char *)&v43, "rn");
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"1") )
            printf("rn###############%s(%d)http_hdr=%srn", "netgear_commonCgi", 276, &v43);
          v41 = strlen((const char *)&v43);
          sub_F9E0(v5, &v43, v41, 0);
          if ( acosNvramConfig_match((int)&unk_F0378, (int)"2") )
            printf("rn######======######%s(%d)rn", "netgear_commonCgi", 280);
          v10 = 0;
LABEL_101:
          exit(v10);
        }
        v26 = "OPTIONS";
        v27 = (char *)&v53;
      }
    }
    else
    {
      v26 = "GET";
      v27 = (char *)&v53;
    }
    strcpy(v27, v26);//key!
    goto LABEL_47;
  }
  if ( v8 > 0 )
    waitpid(v8, &v54, 0);
  return 0;
}


0x05 补丁对比

NetGear官方在12月14日更新了部分设备的Beta版固件,用来修补这个漏洞,对应设备如下:

http://p9.qhimg.com/t015ea47e1d5866cf63.png

链接:http://kb.netgear.com/000036386/CVE-2016-582384 

我对这次更新补丁进行了对比分析,发现此次更新主要是针对httpd进行了修补。通过Bindiff进行对比分析。

 http://p7.qhimg.com/t010411a88c684f3e95.png

右侧是补丁前,在之前的分析中,补丁前的函数是sub_36C34,补丁后变成了sub_35EB4,可以看到右侧标红部分修补的位置还是挺多的,接下来查看一下修补的结构,发现在函数入口处有较大不同。

http://p0.qhimg.com/t0135140c92b7cb2438.png

 通过Zoom to block查看详细的汇编代码,发现补丁后的httpd,在函数入口处会多出几个strchr判断语句,之后才是fork。

 http://p1.qhimg.com/t017cd5a49c0ff67af7.png

我们通过IDA pro直接跟踪到伪代码部分,结合我之前的分析,可以看到,在v6赋值为a3,也就是exp的url之后,会进行 一些strchr比较。

int __fastcall sub_36EB4(const char *a1, int a2, const char *a3, int a4)
  v4 = a1;
  v5 = a2;
  v6 = a3;
  v7 = a4;
  if ( !strchr(a3, 59) && !strchr(v6, 96) && !strchr(v6, 36) && !strstr(v6, "..") )
  {
    v8 = fork();
    v9 = v8;
    if ( !v8 )
    {
      if ( fork() )
      {
        v10 = v9;
        goto LABEL_114;
      }
      memset(&v47, 0, 0xFFFFu);

比较的内容就是59、96、36的asii码以及..,59对应的字符是“;”,96对应的字符是“`”,36对应的字符是“$”,如果url里包含这些字符之一的话,则不会进入下面的处理,以此来修补之前exp中;killall$IFS`httpd`带来的命令注入攻击。我个人感觉官网的修复不走心,经过我刚才的分析,发现Beta版固件过滤只针对了;,$,'以及..这四个字符,只过滤了[command];[command]这种情况,并对之后的$IFS等内容进行了过滤,但命令执行拼接也可以通过[command]&&[command],和[command]||[command]方法完成,只是后面拼接的命令进行了一定限制,因此我感觉还有可能存在命令注入漏洞。

0x06 解决方案(针对漏洞版本)

临时方案 : 

1.在路由器中禁用远程管理功能

2.指定特定内网IP可以通过WEB方式访问并管理路由器

3.利用这个漏洞,执行关闭WEB服务的命令,不会影响正常上网,但无法使用管理界面,重启路由器后WEB服务可重新启用。命令如下:

http://[router-address]/cgi-bin/;killall$IFS'httpd'

4.如果必要,也可以参照CERT官方建议停止使用该路由器,直到官方发布补丁修复。



本文由k0shl原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/85125

安全客 - 有思想的安全新媒体

分享到:微信
+13赞
收藏
k0shl
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66