**Author:p0wd3r,dawu(知道创宇404安全实验室)**
**Data: 2016-12-13**
**更新于 12/16 :** 修正了原文中的一处错误,感谢 [@k0pwn](http://weibo.com/k0pwn)。
>2016年12月7日,国外网站exploit-db上爆出一个关于NETGEAR R7000路由器的命令注入漏洞。一时间,各路人马开始忙碌起来。厂商忙于声明和修复,有些人忙于利用,而我们则在复现的过程中寻找漏洞的本质。
##一.漏洞简介
###1.漏洞简介
2016年12月7日,NETGEAR R7000路由器在exploit-db上被爆出存在远程命令执行漏洞,随着安全研究人员的不断深入,R8000和R6400这两款路由器也被证实有同样的问题。
2016年12月13日,NETGEAR官网上确认漏洞存在,对部分受影响的设备发出了`beta`版的固件补丁。
2016年12月14日,受影响的设备型号增加至11种。
###2.漏洞影响
NETGEAR R6250*
NETGEAR R6400*
NETGEAR R6700*
NETGEAR R6900*
NETGEAR R7000*
NETGEAR R7100LG*
NETGEAR R7300DST*
NETGEAR R7900*
NETGEAR R8000*
NETGEAR D6220*
NETGEAR D6400*
##二.漏洞复现与分析
###1.漏洞复现
通过ZoomEye网络空间搜素引擎我们可以寻找此次受影响的设备的ip
```bash
curl -v "https://ip:port/cgi-bin/;echo$IFS"testt" --insecure
```
![](https://images.seebug.org/content/images/2016/12/curl--.png)
###2.漏洞分析
####①灵感篇
此次漏洞分析的灵感源于小伙伴检测中发现的一个问题,让我们走了不少弯路,顺利定位到问题所在。
当我们执行`ps`命令时,出现了如下的结果:
![](https://images.seebug.org/content/images/2016/12/ps.png)
很有意思的是,我们请求的url出现在了以`nobody`用户运行的进程里,这让我们可以根据关键词"sh -c"和"/tmp/cgi_result"去定位关键代码的位置。
在漏洞分析的过程中,另一个小伙伴给出了一个[链接](https://www.exploit-db.com/exploits/9209/),DD-WRT HTTPd的远程命令执行。其中命令执行的部分如下,与本次漏洞很像。于是`HTTPD`就成了我们这次重点关注的对象。
![](https://images.seebug.org/content/images/2016/12/DD-WRT-httpd.png)
从官网下载NETGEAR R7000的[固件](http://support.netgear.cn/Upfilepath/R7000-V1.0.7.2_1.1.93.chk)并通过如下命令解开固件。
```bash
binwalk -eM R7000-V1.0.7.2_1.1.93.chk
```
解开固件之后我们寻找到了相关的文件`/usr/sbin/httpd`,确认与`cgi_result`有关之后,我们对`httpd`进行了逆向。
![](https://images.seebug.org/content/images/2016/12/strings-1.png)
####②逆向篇
根据前文的介绍,我们通过搜索字符串,找到了对应的函数`sub_36C34`,F5看反编译的伪码,出现`cgi_result`这一字符串的地方如下:
```c
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函数的几个参数的内容
```c
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`生成的调用图:
![](https://images.seebug.org/content/images/2016/12/-.png)
我们从main函数开始看起,main函数直接调用了sub_147A0函数
sub\_147A0函数中如此调用了sub\_100A0函数
```c
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函数的一些关键过程。
```c
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。
```c
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`。
```c
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`[固件](http://www.downloads.netgear.com/files/GDC/R7000/R7000-V1.0.7.6_1.1.99.chk),我们对其进行了更进。
按照上文同样的方法,我们对`httpd`进行了逆向,`xrefs`图如下:
![](https://images.seebug.org/content/images/2016/12/update.png)
整体函数没有太大变化,让我们来看一下具体细节上的变化。
一路跟下来,在`sub_14958`,`sub_100A0`和`sub_197B8`这些函数中都没有看到对`url`进行处理,在`sub_36EB4`中我们发现官方对其进行了过滤。
```c
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的全球分布情况。
![](http://paper.seebug.org//content/images/2016/12/r7000.png)
![](https://images.seebug.org/content/images/2016/12/r8000.png)
![](https://images.seebug.org/content/images/2016/12/6400.png)
事实上,还有很多内网的设备我们无法探测,对于这些内网设备,通过`ssrf`攻击仍然可以威胁到内网的安全。这里提供一个以往的案例供大家参考: http://www.2cto.com/article/201311/254364.html 。由于本次漏洞可以执行任意命令,故威胁远比案例中修改`dns`要大,希望可以引起大家的重视。
全部评论 (1)