首页 > 暗组技术 > J2EE_WEB安全测试系列文章
2019
07-05

J2EE_WEB安全测试系列文章

文档参考:
1、JAVAWEB源码审计
http://www.ibm.com/developerworks/cn/views/java/libraryview.jsp?sort_by=&show_abstract=true&show_all=&search_flag=&contentarea_by=Java+technology&search_by=%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1&topic_by=-1&type_by=%E6%89%80%E6%9C%89%E7%B1%BB%E5%88%AB&ibm-search=%E6%90%9C%E7%B4%A2
2、JSTL过滤XSS
http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html
http://xiemingmei.iteye.com/blog/2105826
http://zone.wooyun.org/content/2448
3、EL-injection
http://danamodio.com/appsec/research/spring-remote-code-with-expression-language-injection/
4、XML漏洞
http://resources.infosecinstitute.com/soap-attack-2/
5、flash漏洞
1)http://blog.detectify.com/post/86298380233/the-pitfalls-of-allowing-file-uploads-on-your-website
2)http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLLoader.html

一、认证测试
1.1、用户名枚举漏洞测试
1.1.1、漏洞产生原因分析
目前很多注册页面在注册前采用ajax、jquery无刷新先判断用户名是否已经被注册,该机制方便提醒用户是否账户已被注册,同时引入登录账户名可枚举安全问题。
如下代码:
1、客户端使用ajax技术实现无刷新获取表单提交数据至后端验证(ajax代码仅供参考):
function validatorloginName(){
                 var loginName=document.getElementById("uname").value;
                 if(loginName == "")
                 {
                         alert("用户名不能为空!");
                         return;
                 }
                 $.ajax({
                                 type: "POST", //提交方式  
                         url: "ValidateName", //后端业务处理页面   
                          data: "loginName="+loginName, //提交post数据
                         success: function(data)//回调处理方法{
                            if(data=="true"){   
                             alert("恭喜您!用户名没有被使用!");  
                            }else{   
                             alert("抱歉!用户名已存在!");   
                            } 
                                  }          
                        });   
                }                
2、后端servlet业务处理参考代码:

1.1.2、安全测试方法
1、        使用burpsuite拦截ajax报文发送至Send to Intruder

2、针对用户名字段添加变量符$,添加payload进行账户遍历猜解。

1.1.3、修复建议
添加验证码、一次性token

1.2、用户名/密码弱口令
1.2.1、漏洞产生原因分析
业务系统发布或调试阶段使用弱口令,常见弱口令有admin/admin、admin/admin888、system/system888等等。

1.2.1、安全测试方法
测试人员可根据常见社工库做筛选排序,选出最常用的200个、6000个密码作为个人字典,在不同类型网站中进行使用。
1.2.2、修复建议
使用字母、数字、特殊字符长度超过8位字符位元素设置强壮口令,并定期修改。

1.3、登录认证模式绕过
υ        漏洞产生原因:
很多网站登录设计采用表单验证模式,登录设计如果存在sql注入漏洞,则可以通过sql注入语句进行表单绕过。
漏洞代码参考:

υ        安全测试方法
用户名/密码:'or'='or'/ 'or'='or',admin' or '1'='1/ admin' or '1'='1, ' or '1'='1/ ' or '1'='1,admin' or '1'='1—/11
υ        修复建议:

使用PrepareStatement预编译对象进行参数化查询。

1.3.2、非授权访问
υ        漏洞产生原因:
一些需要登录才能访问页面,缺乏对用户登录会话、token进行验证有效性判断,导致非合法会话、token也可以访问原本需要用户登录态页面。
υ        安全测试方法:
1、借助Dirbuster进行目录猜解、点击测试需授权访问页面能否在非登录状态下打开访问。
2、手工访问常见登录后页面main.jsp、menu.jsp、left.jsp、botton.jsp等。
υ        修复建议:
修复方法可以在某个页面文件包含一个用于验证会话/token是否有效的文件,也可以新建个过滤器Filter,在dofilter()方法中进行用户会话\token有效性判断,并在web.xml配置filter适用url,推荐使用过滤器,效率高如:
<filter>
        <filter-name>login</filter-name>
        <filter-class>com.jingxing.oa.filter.LoginFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>login</filter-name>
        <url-pattern>admin/*</url-pattern>
</filter-mapping>

1.3.3、验证码机制绕过
υ        漏洞产生原因:
验证码设计用于区分机器与人为操作,可用于防治恶意注册、暴力破解等频发操作,如果验证码采用本地cookie生成验证、验证码无法一次/一用(不及时刷新)、或者验证码可以轻易OCR识别则存在设计不当。
υ        安全测试方法:
1、        验证码输出在cookie中

1、        验证码不及时更新漏洞
每次登录验证码老半天不更新、一次多用。
2、        可轻易OCR(噪点或扭曲度不够)
可下载http://zone.wooyun.org/content/19188?&from=androidqq 工具测试验证码是否可以识别。

3、        验证码逻辑设计不当,无非空判断等
可参考:http://www.wooyun.org/bugs/wooyun-2014-083050

1.3.4、后台Js跳转验证绕过
υ        漏洞产生原因:
网站的后台登陆在输入用户名和密码错误以后使用的JS跳转,页面会自动跳转到登陆页面,也就是说如果突破了JS也就可以直接管理后台了(利用条件是登录后的页面未判断session、token)。
υ        安全测试方法:
这类漏洞一般都会访问时,都可以通过肉眼看到部分登录后页面,或者抓包能看到加载了登录后页面。

1、在Firefox地址栏里输入“about:config”。
2、在搜索栏输入“javascript.enabled”查找到首选项。
3、点击鼠标右键选择“切换”,把“javascript.enabled”键值改为“false”
这样就能禁止JavaScript的运行了。
4、再次访问页面,如果能登录成功,则存在安全问题。
υ        修复方案
身份验证检测放服务端检测、参考登录验证章节。
response.sendRedirect("main.jsp");
<jsp:forward page="main.jsp"></jsp:forward>

1.3.5、手机OTP认证绕过
υ        漏洞产生原因分析
网站手机OTP动态口令绕过主要存在以下几种情况:
1、        手机otp动态码失效时间设计有问题,目前多半是2分钟左右时效,如果时效过长,存在一定风险;
2、        手机otp动态码发送后,在页面源代码中本地显示;
3、        手机otp位数低于4位,设计安全性不高;
4、        后台otp验证机制有缺陷,如无非空判断;
5、        手机otp与账密验证逻辑分离,可绕过;
6、        手机otp随机性不强,可预测。
7、        手机otp验证结果前端认证,修改返回状态码,可绕过。
υ        漏洞测试方法
1、        在等待输入手机otp页面,超过2分钟后输入,观测是否失效;
2、        view-source页面源代码,观测是否在本地代码中显示动态号;
3、        上述3-6根据抓包比对分析,难度不大。
υ        修复建议
略。

[公告]7月20日北京国家会议中心,第三届看雪峰会2.5折门票限时抢购中!

上传的附件:
  • 最新回复 (30)


接上个章节讲
2.1、会话固定漏洞(失效的会话管理)
2.1.1、漏洞产生原因
该漏洞主要是因为Web应用程序没有正确的执行会话管理,例如用户登陆前的会话Cookie和登录后的是一样的,另外一个例子是当用户点击退出的时候,Session不会失效。
漏洞代码示例:
<%@page import="java.sql.*"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title> </title>
</head>
<body>
<% 
    String user = request.getParameter("user");
    String pass = request.getParameter("pass");
    Class.forName("com.mysql.jdbc.Driver");
    Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" , "");
    PreparedStatement ps=(PreparedStatement) con.prepareStatement("select * from users where username=? and password=? limit 0,1");
    ps.setString(1,user);
    ps.setString(2,pass);
    ResultSet rs=ps.executeQuery();
    if(rs.next())
    {
        session.setAttribute("useracc", rs.getString("user"));
        out.println("Login success");
    }
    else
    {
        out.println("Login failed");
    }
%>
</body>

2.1.2、安全测试方法

漏洞测试比较简单,可借助Firefox、Burpsuite等工具在观察登录前、登录后、退出状态下cookie、jsessionid是否有异同,如果不变,则存在问题。
2.1.3、修复建议
一般来说,解决固定会话是相当容易的。最基本的建议就是:一旦用户登录成功以后,马上invalidate用户的会话。具体的步骤如下:
➊ 用户输入用户名和密码。
➋ 系统对用户进行验证通过。
➌ 已有的会话信息如果仍然需要,则转移到一个临时变量中去(请参考➌)。
➍ invalidate 当前的会话(请参考➍)。
➎ 创建一个新的会话(请参考➎)。
➏ 把临时变量中保存的会话信息恢复到新创建的会话中去(请参考➏)。
➐ 用户使用这个新的会话登录到系统中并进行操作。
修复代码参考:
<% 
    String user = request.getParameter("user");
    String pass = request.getParameter("pass");
    Class.forName("com.mysql.jdbc.Driver");
    Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/userdb", "root" , "");
    PreparedStatement ps=(PreparedStatement) con.prepareStatement("select * from users where username=? and password=? limit 0,1");
    ps.setString(1,user);
    ps.setString(2,pass);
    ResultSet rs=ps.executeQuery();
    if(rs.next())
    {
        session.invalidate();
        request.getSession(true);
        session.setAttribute("useracc", rs.getString("user"));
        out.println("Login success");
    }
    else
    {
        out.println("Login failed");
    }
%>
以上修复后的代码中,用户在登录的时候,首先会让之前的session失效,然后又获取新的seesion。

2.2、越权漏洞
2.2.1、漏洞产生原因
如果一个Web应用程序不严格检查用户是否被授权访问(鉴权),就有可能产生越权类安全问题。例如帐号A在登录的状态下,遍历访问资源ID就可以查看其它人的相关信息。
漏洞代码示例:
<%@page import="java.util.Enumeration"%>
<%@ page import="java.sql.*" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Account Balance</title>
</head>
<body>
<%
    int flag = 0;
    Enumeration e = session.getAttributeNames();
    while (e.hasMoreElements())
    {
        String name = (String) e.nextElement();
        String value = session.getAttribute(name).toString();
        if(name.equals("useracc") && !(value.isEmpty()))
        {
            flag = 1;
            break;
        }
    }
    if(flag == 1)
    {
        String accno = request.getParameter("accno");
        Class.forName("com.mysql.jdbc.Driver");
        Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");
        PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from account_balance where accno=? limit 0,1");
        ps.setString(1,accno);
        ResultSet rs = ps.executeQuery();
        if(rs.next())
        {
            String s = rs.getString("balance");
            out.println("<h1>Welcome to your account</h1>");
            out.println("<br>Account Number: "+session.getAttribute("useracc"));
            out.println("<br>Your current balance is: "+s);
        }
        else
        {
            out.println("Error: Contact administrator.");
        }
    }
    else
    {
        response.sendRedirect("login.jsp");
    }
%>
</body>
</html>
2.2.2、安全测试方法
抓包修改用于区分用户标识的account_id、memberid字段,查看是否可以越权查看其他人信息。
2.2.3、修复建议
判断了用户的Session,在为True的情况下才能够查看返回的信息,因此当用户遍历accno的值来尝试获取返回结果时,会提示无权访问。
<%
    int flag = 0;
    Enumeration e = session.getAttributeNames();
    while (e.hasMoreElements())
    {
        String name = (String) e.nextElement();
        String value = session.getAttribute(name).toString();
        if(name.equals("useracc") && !(value.isEmpty()))
        {
            flag = 1;
            break;
        }
    }
      
    if(flag == 1)
    {
        String sess_accno = session.getAttribute("useracc").toString();
        String accno = request.getParameter("accno");
//数据库查询时,先判断查询的账户信息是否和现登录态用户一致        
if(sess_accno.equals(accno))
        {
            Class.forName("com.mysql.jdbc.Driver");
            Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");
            PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from account_balance where accno=? limit 0,1");
            ps.setString(1,accno);
            /*
            This line will be better
            ps.setString(1,sess_accno);
            */
            ResultSet rs = ps.executeQuery();
            if(rs.next())
            {
                String s = rs.getString("balance");
                out.println("<h1>Welcome to your account</h1>");
                out.println("<br>Account Number: "+session.getAttribute("useracc"));
                out.println("<br>Your current balance is: "+s);
            }
            else
            {
                out.println("Error: Contact administrator.");
            }
        }
        else
        {
            out.println("Unauthorized Access Detected");
        }
    }
    else
    {
        response.sendRedirect("login.jsp");
    }
%>
上传的附件:
支持,好文章
接上章继续,后续章节有空再慢慢补,最近工作有点变动,有点抽不出时间整理,这些都是之前工作中断断续续整理的一些内容
三、文件I/O类漏洞测试
3.1、下载类漏洞
3.1.1、漏洞产生原因分析
文件下载功能设计不当,可能导致目录穿越漏洞(php中称为文件包含),可以通过构造filename、filepath等下载文件名路径下载任意文件,导致敏感信息泄露。
漏洞代码示例:

3.1.2、安全测试方法
一般存在类似漏洞,都会类似存在一些共性,现在文件名+参数,例如,filename=?或filepath=?或file=?或path=?
一般测试,可在正常文件名前加%0a/../,观测是否可正常下载,如果下载正常,很可能存在问题,更多测试payload网上很多,下面例举部分:
1、        download?filename=../../../../../../../etc/passwd;
2、        download?filename=../../../../boot.ini;
3、        download?filename=/../..//../..//../../etc/passwd%00.jpg;
4、        download?filename=/../..//../../etc/passwd%3f.html
5、        java语言中会把"%c0%ae"解析为"\uC0AE",最后转义为ASCCII字符的"."(点)。
3.1.3、修复建议
对客户端下载文件名uri进行检查,可参考以下代码:
String filename = request.getParameter("filename");  
获取的filename参赛可能为../../或/../../总之有..
if(filename.indexOf("..")>=0){
                PrintWriter pw=response.getWriter();
                pw.print("下载文件非法..");
        }
else{
业务….
}

3.2、文件上传漏洞
ν        漏洞概述
上传漏洞,根据其意主要是由于文件上传过程中安全设计有问题,导致可以非法上传恶意脚本文件,根据漏洞利用方式又可以细分为客户端检测漏洞、服务端MIME类检测漏洞、服务端文件扩展名检测漏洞、服务端文件内容检测漏洞、服务端文件路径检测漏洞、及web容器解析(IIS、apache、nginx)漏洞等,可参考下述链接:
http://www.owasp.org.cn/OWASP_Training/Upload_Attack_Framework.pdf
本节以客户端检测、文件扩展名检测进行举例说明:
3.2.1、客户端上传检测漏洞
υ        漏洞产生原因
客户端上传文件至Web服务器过程采用客户端javascript 、ajax进行文件合法性检测检测,漏洞代码参考:
Javascript检查文件扩展名
function fileFormatCheck(s)
    {
       var i = s.lastIndexOf(".");//获取文件名.字符串位置
         if(i < 0)
           {
           alert("<%translate("Certificate file error"); %>."); 
           return false;
       }

     var  var1 = s.substring(i+1) ;//获取文件名. 后面后缀
        if(var1 == "jpg" || var1 == "gif" || var1 == "jpeg")
         {
           return true;
         }
      else
         {
        alert("<%translate("Certificate file error"); %>."); 
        return false;
     }

υ        安全测试方法
1、借助firebug、浏览器自带开发者工具、鼠标定位到提交按钮,查看上传动作,定位到代码段,如果有类似onsubmit、onclick等响应函数,则可能采用本地检测;
2、采用本地安全检测有个明显特点,选择待上传文件后,如果文件不合法,则在点提交动作前,提前触发;
3、将.jsp文件上传前改为.jpg等合法文件,并借助burpsuite工具拦截修改上传报文。
υ        修复建议
见本章节文件上传漏洞修复方案。

3.2.2、服务端文件扩展名检测漏洞
υ        漏洞产生原因
服务端对上传文件扩展名检测机制存在设计问题,如使用黑名单机制文件扩展名类型有遗漏、或匹配扩展名代码有缺陷。
漏洞代码示例:

υ        安全测试方法
更多参考:http://www.owasp.org.cn/OWASP_Training/Upload_Attack_Framework.pdf

3.2.3、其他参考pdf介绍
补充一点,在servlet 3.0之前,javaweb上传文件比较麻烦,往往需要借助第三方sdk,如apache commons-fileuplaod-1.2.1.jar、commons-io-1.4.jar,往往在实现时,程序员容易忽视上传安全机制。

    HashMap<String, String> extMap = new HashMap<String, String>(); 
    extMap.put("image", "gif,jpg,jpeg,png,bmp"); 
    extMap.put("flash", "swf,flv"); 
    extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb"); 
    extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2"); 
// 检查扩展名 
        String fileExt = fileName.substring( 
            fileName.lastIndexOf(".") + 1).toLowerCase(); 
        if (!Arrays.<String> asList(extMap.get(dirName).split(",")) 
            .contains(fileExt)) { 
          out.println(getError("上传文件扩展名是不允许的扩展名。\n只允许" 
              + extMap.get(dirName) + "格式。")); 
          return; 
        }


本文》有 0 条评论

留下一个回复