某单位网站一个逻辑验证漏洞引发的战争 - APT防御产品

某单位网站一个逻辑验证漏洞引发的战争

前因

某某客户在试用东巽科技的“铁穹高级持续性威胁预警系统”过程我们发现有明显的木马通讯特征并通知客户,但客户始终不肯承认自己的环境被入侵了甚至被植入木马事实,迫于无奈我们只好又一次的免费为这些钢牙客户做免费的渗透测试来证明他们的网络环境是有多么的脆弱。

 

0x00【备战】

这个有趣逻辑验证漏洞是在帮某单位做网站的安全风险评估时候遇到的,当时正值年前。在开年会的当天中午接到的通知,明天上午要到某单位做一次现场的渗透测试。伟大领袖毛主席曾经说过 “不打无准备之仗,不打无把握之仗,每战都应力求有准备,力求在敌我条件对比下有胜利的把握”。根据可靠来源的情报称,这个单位的网站是有一家当地专门做信息安全的公司在做日常维护。可是只剩下半天时间的时间让我去准备了,这可怎么办?没时间多想了,只能立刻搞起。

 

0x01【开战】

开战的第一步自然是要打开我们的Kali了。前期的信息收集我喜欢用市面上较为流行也较为简单的Maltego还有Theharvester来作为前期的收集信息工具,可是两个工具跑了半天基本没有什么有用的信息

于是就用了dnsmap还有fierce来收集下域名的信息,结果搞了半天只有一个www域名。

图片1.png

图片2.png


只好找出Nmap来扫一下到底还开了什么其他的端口没有。这时候突然想起来手上正好有一台东巽公司的云狐自动化渗透测试系统,干脆就用他来扫吧。由于网络扫描都比较耗时,加上我设置的超时延迟事件也比较大,所以在扫描期间我就只好手工测试下,看有没有什么问题。打开网站第一眼看过去就知道是Apache Tomcat的服务器,用了Struts的框架,心中一阵窃喜。因为Struts的漏洞还比较多,就赶紧拿出一系列的Struts2工具测试了一圈,结果发现没有一个能用,看来有专业的维护团队就是不一样,没那么好对付,毕竟有同行在,那么容易就做下来了那别人的面子往哪搁呀!看来Struts2这条路是走不通了,只能换一个办法。这时候就想到了找robots.txt看看会不会有一些有价值的信息。结果返回了404 看来也是没有什么可以利用的东西了。拿出whatweb扫一扫看是什么CMS,结果啥都没发现,又是一阵悲剧。于是就找了两个连接,看看是不是有SQL注入可利用。尝试了简单的手工注入以后发现没简单,一出现有非法字符就跳转回首页,判断应该是存在WAFWeb Application Firewall)。有WAF也没关系,因为部分WAF是可以绕过的。尝试了一些常用的绕过WAF手法之后也没成功于是就放弃了。其实在乌云上有一篇“深入了解SQL注入绕过waf和过滤机制”的文章,大家有兴趣的可以参考一下,文章里面提出了以下几种绕过WAF的方法。

1、 大小写混合

2、 关键字替换

3、 使用编码

4、 使用注释

5、 使用等价函数与命令

6、 使用特殊符号

7、 HTTP参数控制

8、 缓冲区溢出

9、 整合绕过

到这里就想到偷懒了,拿出注入神器SQLMap里面就带有一个专门用来绕过WAF的参数tamper。这个参数后面跟着的是绕过waf的脚本,一般是放在/usr/share/sqlmap/tamper这个目录下面。使用方法是:

sqlmap -u "http://www.site.com/abc.do?n_id=999" --tamper="space2comment.py"

因为有WAF所以SQL注入也陷入了僵局,这个时候Nmap扫完了,知道结果的我眼泪流下来啊。只开了80还有8080端口。

图片3.png



这个时候云狐也扫完了,在WEB上到是发现了三个高危漏洞。一个是CVE-2011-3190,另一个是XSS,还有一个HPPHTTP Parameter Pollution)。CVE-2011-3190这个漏洞在网上搜素了一下,没发现有EXP。于是看看XSS,看了一下因为有WAF在的原因好多参数也都过滤了,只剩下一个prompt可以触发,而且只能在火狐下触发,那么意义不大了。

图片4.png

剩下一个HPP了,HPP可以利用来绕过WAF,于是手工尝试了一遍没有成功,又丢到了SQLMap里面,也没成功,感觉这次也找不到什么大问题了。

图片5.png


毕竟有专门的安全公司做维护,设备也上得很齐全,再加上只有半天的时间,估计一时半会能找到的问题也就那么多了。

 

0x02【纵深打击】

原本打算就这样出一份初步的报告了,可是想到文章开头提到的毛主席语录的经典名言觉得不应该就此放弃。又返回去仔细看了一遍云狐自动化渗透测试系统的报告。发现里面有一个登录的页面,就上去看了看。输入完帐号密码还有验证码以后点登录立马就弹出了帐号不存在。看了一下,页面没有跳转,应该是用了Ajax来做的页面。这时候心想有验证码也不好跑弱口令,但是出于职业病的原因,还没想完这个问题就已经打开了查看源代码的页面。反正打开就打开了吧,顺便就看看。结果这一看把我乐坏了,心里又想,还好手快看了下源代码,幸好没关掉,要是没看这损失有多大啊。在登录页面里找到了那么一段登录的源码。

 

function login(){

    var userName = document.getElementById("username").value;

   var password = document.getElementById("password").value;

   if(userName==""||password==""){

   alert("用户名或密码不能为空");

   return false;

   }

  $.ajax({

  url:"/user/Login_Check.do?userName="+userName + '&password='+ password,

  success:function(value) {

 if(value=="true"){

document.getElementById('form1').submit();

  } else{

alert(value.split(",")[1]);

return false;

  }

  },

dataType:'text'

  });

}

 

原来可以跑密码,这个验证码是javascript的,不需要验证码验证。于是拿出hydra跑了一圈,发现还是没跑出来用户名,又心灰意冷了。看了一下发现另一个URL,这个才是真的后台呀,而且没有验证码,随便输入了一个admin就弹出密码错误,看样子应该也用的Ajax来实现的。于是也查看了一下源代码,这一看突然发现前方的道路是光明的。

 

var xmlhttp;

function login(){

 

   if (window.XMLHttpRequest){

   xmlhttp = new XMLHttpRequest();

    if (xmlhttp.overrideMimeType){

      xmlhttp.overrideMimeType('text/xml');

    }

   }else{ 

    if(window.ActiveXObject){ 

     try{   

      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");   

     }catch(e){   

      try{   

       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");   

        }catch(e){}   

     }

    }

   }

  var userName = document.getElementById("userName").value;

  var password = document.getElementById("password").value;  

  xmlhttp.open("POST","/@dmin/CheckLoginForAdmin.do?userName="+userName,true);

  xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

  xmlhttp.send(null);

  xmlhttp.onreadystatechange=function(){

  if(xmlhttp.readyState==4)

  {if(xmlhttp.status==200){

  var value=xmlhttp.responseText;

  if(value=="false"){

   alert("用户名不存在,请重新输入!"); 

   return false;

  }else{

if(value.split(",")[1]!=password){

alert("密码不正确,请重新输入!");

   return false;

}else{

document.form1.submit();

}  

}

      }

   }

 }

}

 

问题就出在代码的第三十三行处,登录的时候Ajax返回的数据里面包含一个正确的密码,然后与密码框里面的密码对比看是否匹配,所有操作都是在客户端用Javascript来判断的,这个可就是很严重的问题了,而且刚才访问的时候用户名是存在的。那说明我们可以拿到真的密码了,于是直接访问

http://www.xxx.com/@dmin/CheckLoginForAdmin.do?userName=admin


图片6.png


这样密码就赤果果的出现在我们面前了,赶紧拿着它登录后台,发现一切正常。到这为止才算是完成了这次任务,对客户有所交代了。

 

0x04【战后总结】

从开始检测到成功登录后台用了不到两个小时时间,这个时间如此紧迫,过程如此曲折的任务总算是完成了,其实归根到底还是两个字“运气”。但是“龟仙人”也曾经说过,运气也是实力的一部分,总之是对客户有所交代了。其实最主要的问题应该是在开发者人员身上,毕竟对于密码验证这块第一首先是直接在客户端匹配密码,虽然即使密码正确,也会在服务器上再比对一次,但是直接在客户端上匹配这似乎不像是一个老程序员所写的东西。第二就是密码没有加密,经过几次互联网的脱裤事件之后应该对密码包括存储都应该进行加密才是。第三点就是验证码的问题,也是用JavaScript来实现的,这个验证码对稍微懂得一些技术的人来说就等于是形同虚设。

在解决方案上其实也很简单,毕竟服务器前面有WAF,这能阻挡了大部分的攻击,剩下要做的就是把之前提到的密码验证以及验证码验证这块放到服务器端来进行。对于XSS还有HPP来说过滤好传入的参数就可以了。




转载请注明出处 APT防御产品 » 某单位网站一个逻辑验证漏洞引发的战争

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址