这款网关的HTTP系统使用的是BOA服务文件,在此基础上进行的二次开发。在这款网关里面存在几个RCE漏洞点和未授权访问点,在厂商修复漏洞过程中发生了一些有趣的事,所以拿出来分享下。按照惯例和当前国情,为了不必要的麻烦,已隐去相关敏感信息,看官权当普通漏洞分析思路文章来看就好。
首先来看REC漏洞,由于设备没有做游客访问控制,所以可以直接结合未授权访问进行远程直接利用,未授权访问没有什么分析价值,我们放到后文。
打开HTTP主程序boa,在我们调用请求web_ping_exe_cgi文件时,会来到这个地方,进入loc_BCAD8函数
loc_BCAD8函数是获取我们提交过来的数据包,用boaGetVar()函数接收处理数据并保存
然后接下来取出了静态变量数据“/usr/bin/diag ping ”,把数据参数传入diag文件里面的ping函数
在进行了一些操作后调用了execv()函数来执行/bin/sh,这里就是RCE漏洞触发点。
目前整个流程就很清晰了,一路走来,我们没有看到任何的参数处理函数来对我们传入的数据进行清洗,所以我们传入的攻击字符串会被拼接传入diag文件。最终执行格式为
/bin/sh -c “/usr/bin/diag ping” +data
在成功执行后会利用boaWrite()函数输出字符串“SUCCESS”。
好了,我们来看下diag文件里面的处理过程。
在diag文件里面,首先会判断我们选择的是哪个命令,这里我们选择的是“ping”命令,所以会直接调用ping()函数,我们跟进看下
.......
case "$diag_command"
in "ping" ) Ping $diag_dst $diag_wan $diag_ipversion $ping_repeat;;
"traceroute" ) Traceroute $diag_dst $diag_wan $diag_ipversion ;;
"tracerouteu" ) Tracerouteu $diag_dst $diag_wan $diag_ipversion ;;
*) show_usage ;;
......
很明显了,在这个文件里面也没有对我们传入的参数进行过滤处理,直接就带入了执行,执行结果保存在了"/tmp/diag-result”文件里面。
因为这个bash脚本里面用的变量拼接,所以我们直接用多语句分割就行了
RESULT_FILE="/tmp/diag-result"
Ping()
{
local destination="${1}" traceroutewan="$2
local ipversion="${3}"
local repeatnum="${4}"
if [ "$ipversion" = "ipv4" ]; then
[ -n "$traceroutewan" ] && {
ping -c ${repeatnum} ${destination} -I ${traceroutewan} > ${RESULT_FILE} 2>&
}
else
[ -n "$traceroutewan" ] && {
ping6 -c ${repeatnum} ${destination} -I ${traceroutewan} > ${RESULT_FILE} 2>&1
}
fi}
本地监听执行结果
在厂家修复漏洞后,复查了一下漏洞的修复结果。
咋看下相关漏洞被修复了,未授权访问不存在,但是在认真看了修复代码后发现,修复结果可以被绕过。
未授权修复不当
在最新的版本中,修复了上一版本的未授权问题,其中存在未授权访问的页面已经无法访问,但是厂家在此基础上修复不当,无意之间引入了一个后门,我们来了解一下。
其中禁止访问的一部分敏感页面如下,添加了访问判断规则
但是,在往下分析时发现,发现厂商为这些页面添加了一个账号密码(这种修复方法闻所未闻,活久见),要访问这些页面需要这个账号密码,游客和管理员访问这些限制页面都会先判断帐号密码,不过访问的帐号密码却被硬编码到固件中,在这种情况下造成了很明显的后门帐号,如图
所以,我们只需要在访问页面时加上帐号密码参数就行了
RCE漏洞修复不当
在RCE漏洞接收参数这个地方,添加了一个strtok_r()函数来处理分隔符“;”问题
但是可以执行多行命令的分隔符却不止这一个,所以还是存在绕过的可能,我们只需要将“;”号换成“||”或“&&”或者其他的可执行的分割语法就行了,这里换成"||"进行尝试,最后成功执行。
未授权修复不当
在厂家进一步修复后,之前的明文后门帐号变成了编码后门,md5加base64的混合加密。
后门还是存在,只不过加密了而已,只要解开了hash值,那么就可以利用明文密码访问敏感页面了
RCE漏洞修复不当
还是之前的那个RCE漏洞,厂家这次不在HTTP主程序中进行数据判断,转向Web源码函数判断。现在在网页源码中添加了一个回调函数来判断命令分隔符,这个已经很不错了,提高了利用难度,不过现实是还是可以绕过.......
我们来看下这个回调函数
在源代码中有两个函数,inValidIPAddr()和isValidUrlName(),这两个函数是过滤IP和域名的,IP我们无法绕过,正则写的很死,但是对于域名过滤,我们却是可以绕过的,由于判断是先判断是否为IP地址再判断是否为域名,那么这两个回调函数结合起来等于没起作用。源码太长,这里取主要函数。下面我们来看下过滤源码
function inValidIPAddr(Address)
{
var address = Address.match("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$");
var digits;
var i;
if(address == null) {
return false;
}
digits = address[0].split(".");
for(i=0; i < 4; i++)
{
if((Number(digits[i]) > 255 ) || (Number(digits[i]) < 0 ) || (Number(digits[0]) > 223) || (digits[i] == null))
{
return false;
}
}
return true;
}
function isValidUrlName(url)
{
var i=0;
var invalidArray = new Array();
invalidArray[i++] = "www";
invalidArray[i++] = "com";
invalidArray[i++] = "org";
invalidArray[i++] = "net";
invalidArray[i++] = "edu";
invalidArray[i++] = "www.";
invalidArray[i++] = ".com";
invalidArray[i++] = ".org";
invalidArray[i++] = ".net";
invalidArray[i++] = ".edu";
if (isValidAscii(url) != ''")
{
return false;
}
for (i = 0; i < url.length; i++)
{
if (url.charAt(i) == '\\')
{
return false;
}
if (url.charAt(i) == '&')
{
return false;
}
if (url.charAt(i) == '|')
{
return false;
}
if (url.charAt(i) == ';')
{
return false;
}
}
if (url == "")
{
return false;
}
if (url.length < 3)
{
return false;
}
for(j=0; j< invalidArray.length; j++)
{
if (url == invalidArray[j])
{
return false;
}
}
return true;
}
这次比之前都要厉害一些,这次过滤了 “&”,“|”,";“这三个很常见的命令分割符。不过在linux系统中,执行命令的分割符有多种情况除了 “&”,“|”,";“这三种外,还有“`"、"$()"等,所以结果是,修复结果又绕过了。如图
经过几次的修复和绕过,我们可以发现,如果一个系统没有考虑到对输入、输出、数据流进行严格检查控制的话,都有可能造成一定的安全风险。厂商由于安全开发知识缺乏,导致修复结果令人哭笑不得。最后,由于攻击的难度已加大,安全性也有所提高,厂商尽力了,那么作者也尽力了。