NETGEAR 系列路由器命令执行漏洞

1.测试目标查找:shodan

NETGEAR R6250*

NETGEAR R6400*

NETGEAR R6700*

NETGEAR R6900*

NETGEAR R7000*

NETGEAR R7100LG*

NETGEAR R7300DST*

NETGEAR R7900*

NETGEAR R8000*

NETGEAR D6220*

NETGEAR D6400*

通过ZoomEye网络空间搜素引擎我们可以寻找此次受影响的设备的ip

curl -v //仔细查看"https://ip:port/cgi-bin/;echo$IFS"testt" --insecure//也可以用-k //证书问题解决

2.漏洞分析

①灵感篇

此次漏洞分析的灵感源于小伙伴检测中发现的一个问题,让我们走了不少弯路,顺利定位到问题所在。 当我们执行ps命令时,出现了如下的结果: 很有意思的是,我们请求的url出现在了以nobody用户运行的进程里,这让我们可以根据关键词"sh -c"和"/tmp/cgi_result"去定位关键代码的位置。

在漏洞分析的过程中,另一个小伙伴给出了一个链接,DD-WRT HTTPd的远程命令执行。其中命令执行的部分如下,与本次漏洞很像。于是HTTPD就成了我们这次重点关注的对象。

从官网下载NETGEAR R7000的固件并通过如下命令解开固件。

binwalk -eM R7000-V1.0.7.2_1.1.93.chk 
解开固件之后我们寻找到了相关的文件/usr/sbin/httpd,确认与cgi_result有关之后,我们对httpd进行了逆向。

②逆向篇

根据前文的介绍,我们通过搜索字符串,找到了对应的函数sub_36C34,F5看反编译的伪码,出现cgi_result这一字符串的地方如下:

if ( !strcmp((const char *)&v53, "POST") ) 
{
...
}
else if ( !strcmp((const char *)&v53, "OPTIONS") ) 
{
...
}
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("\r\n##########delete /tmp/cgi_result ############\r");
    }
    v33 = (const char *)&unk_F070F;
    v34 = (char *)&v45;
}
sprintf(v34, v33, &v50); 
system((const char *)&v45); 
memset(&v49, 0, 0x40u); 
可以看到else里的逻辑是先判断/tmp/cgi_result这一文件是否存在,存在则删除该文件,v33为unk_F070F的地址值,unk_F070F的值为/www/cgi-bin/%s > /tmp/cgi_result,v34为v45的地址值。继续向下,可以看到v50替换了v33中的 %s 并赋值给了v34。v50具体是什么我们暂时不清楚。然后再使用system() 函数执行v45对应的值,也就是前面v34的值。 。据此推断,此处应该就是命令执行的触发点了。我们开始向上溯源。 首先,我们先看一下sub_36C34函数的几个参数的内容

int __fastcall sub_36C34(const char *a1, int a2, const char *a3, int a4) 
根据其中的代码内容以及写入的文件/tmp/post_data.txt可知,a1为POST数据包的body部分,a3可能为url,a4为一个整数,用于判断是否为POST的数据包 查看xrefs graph to生成的调用图:

我们从main函数开始看起,main函数直接调用了sub_147A0函数

sub_147A0函数中如此调用了sub_100A0函数

sub_100A0(&s1, a105, (int)&a87, dword_F217F8); 
其中s1为http报文内容,a105为s1的地址值。

在sub_100A0函数中,POST数据交给sub_19600函数处理,GET数据交给sub_19B3C函数处理,还有一些其它情况交给sub_1A1C0函数处理,再交给sub_19B3C处理。我们跟了调用sub_19600函数的一些关键过程。

int __fastcall sub_100A0(char *a1, const char *a2, int a3, int a4) 
{
  char *v9; // r4@5
  const char *v10; // r3@6
  int v11; // r7@6
  bool v12; // zf@6
  ...
  s1 = a1;
  v9 = (int)s1;
  ...
  do
  {
    v10 = (unsigned __int8)*v9;
    v11 = v9++;
    v12 = v10 == 0;
    if ( v10 )
      v12 = v10 == 32;
  }
  while ( !v12 );
  //移动到HTTP报文第一个空格的位置
  ...
LABEL_27: 
  if ( *(_BYTE *)(v11 + 1) == 47 )
    ++v9;
    //移动到HTTP报文中/的位置
  ..
  return (int)sub_19600((const char *)v9, v248, v4);
}
可以看到,sub_19600函数将指针移动到HTTP报文中第一个/的位置,也就是网站路径的位置,然后通过调用LABEL_27:就可以获取网站的目录。这样获取到的内容就是最初始的内容被传递到了下一个函数 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("\r\n##########%s(%d)url=%s\r\n", "handle_options", 1293, v4);
    result = (char *)sub_36C34(v3, v5, v4, 2);
  }
  return result;
}
sub_19600函数没有做任何处理,就直接将获取到的路径传递到了sub_36C34。

int __fastcall sub_36C34(const char *a1, int a2, const char *a3, int a4) 
{
    v6 = a3;

    v12 = strstr(v6, "cgi-bin");
    if(v12)
    {
    ...
        memset(&v50, 0, 0x40u);//给V50分配了64字节的空间,故我们可执行命令的最大长度为64字节
    ...
    }
    else
    {
        if ( v24 )
        {
            if ( v22 )
                v25 = 0;
            else
                v25 = v23 & 1;
            if ( v25 )
            strcpy((char *)&v50, v20);
        }
        else
        {
            strncpy((char *)&v50, v20, v22 - 1 - v21);
        }
    }
    ...
    ...
    ...
    if ( !strcmp((const char *)&v53, "POST") )
    {
    ...
    }
    else if ( !strcmp((const char *)&v53, "OPTIONS") )
    {
    ...
    }
    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("\r\n##########delete /tmp/cgi_result ############\r");
        }
        v33 = (const char *)&unk_F070F;
        v34 = (char *)&v45;
    }
    sprintf(v34, v33, &v50);
    system((const char *)&v45);
    memset(&v49, 0, 0x40u);
}
在sub_36C34函数中,会检测url中是否含有cgi-bin,如果含有,则进行一系列分割操作,并将cgi-bin后面的值赋给v50,而参数v50则正如我们之前分析的那样,替换了v33中的 %s 之后赋值给v34并被 system() 函数执行,造成了命令执行漏洞。

之后我们继续跟了sub_19B3C和sub_1A1C0这两个函数,发现最终也跟sub_19600函数殊途同归。不过是因为HTTP请求的不同(POST和OPTIONS)而导致不同的函数去处理罢了。

③固件对比篇

2016年11月13日,NETGEAR在其官网发布了新的beta固件,我们对其进行了更进。
按照上文同样的方法,我们对httpd进行了逆向,xrefs图如下: 整体函数没有太大变化,让我们来看一下具体细节上的变化。 一路跟下来,在sub_14958,sub_100A0和sub_197B8这些函数中都没有看到对url进行处理,在sub_36EB4中我们发现官方对其进行了过滤。

int __fastcall sub_36EB4(const char *a1, int a2, const char *a3, int a4) 
{
    const char *v6; // r4@1
    ...
    v6 = a3;
    if ( !strchr(a3, 59) && !strchr(v6, 96) && !strchr(v6, 36) && !strstr(v6, "..") )
    //ascii对照表:59=>; 96=>`  36=>$
    {
    ...
    }
}
我们可以看到,官方的beta固件过滤了; \ $和..,但是我们依旧可能可以绕过这些过滤执行命令,例如||(未测试)。

至于官方后续的更新,我们会继续更进。 如有错误,欢迎指正:)

三.漏洞影响

下图为ZoomEye网络空间搜素引擎上最早曝光的受影响设备R7000,R8000和R6400的全球分布情况。

事实上,还有很多内网的设备我们无法探测,对于这些内网设备,通过csrf攻击仍然可以威胁到内网的安全。这里提供一个以往的案例供大家参考: http://www.2cto.com/article/201311/254364.html 。由于本次漏洞可以执行任意命令,故威胁远比案例中修改dns要大,希望可以引起大家的重视。

四.修复建议

目前官方仅推出了beta版的补丁,可以根据官网的提示刷新固件 由于新版本beta固件可能还存在一定的安全问题,我们仍然建议关闭路由器远程管理页面。 对比之前受影响的设备,如果不能进入管理界面,也可以通过下列url关闭。

http(s)://ip:port/cgi-bin/;killall$IFS’httpd’ 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容