XPATH注入详解|OWASP Top 10安全风险实践(五)

本文为一些列连载文章之一,不定期更新,计划目录如下:

命令注入

XML外部实体注入

XPATH注入

反射式、DOM及存储XSS

失效的身份认证和会话管理

不安全的直接对象引用

安全配置错误

敏感信息泄露

功能级访问控制缺失

跨站请求伪造

服务端请求伪造

文件上传漏洞

未验证的重定向和转发

不安全的反序列化

使用含有已知漏洞的组件

一、注入

注入攻击漏洞,例如SQL,OS 以及 LDAP注入。这些攻击发生在当不可信的数据作为命令或者查询语句的一部分,被发送给解释器的时候。攻击者发送的恶意数据可以欺骗解释器,以执行计划外的命令或者在未被恰当授权时访问数据。

  1. Xpath注入
  • 漏洞利用演示

页面功能:通过用户名查询对应用户的电话号码。用户名和电话号码存储在XML文件中。XML文件内容如下:

XPATH注入详解|OWASP Top 10安全风险实践(五)

正常查询和恶意利用结果对比如下:

XPATH注入详解|OWASP Top 10安全风险实践(五)

XPATH注入详解|OWASP Top 10安全风险实践(五)

  • 漏洞危害说明

用户输入的字符被用于XPATH的查询表达式中,这些恶意编造的字符会获取XML文件中非授权数据,从而用于执行身份验证、数据使用或者其它操作等;

  • 漏洞代码分析
publicvoiddoPost(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {              File f= new File("D:\Tomcat-7.0.55\webapps\owasp\xml\telphone.xml");              xpath_bad(request,response, f);} privatevoidxpath_bad(HttpServletRequest request, HttpServletResponseresponse, File f) throws ServletException,IOException {           Stringlname = request.getParameter("username1");                   String xpath = "/user_name_phone/user[name='" + lname + "']/phone";           SAXReadersaxReader = new SAXReader();           Documentdocument = null;           try {                document= saxReader.read(f);           } catch(DocumentException e) {                e.printStackTrace();           }           Elementroot = document.getRootElement();           List<?> list =root.selectNodes(xpath);           Iterator<?>iter = list.iterator();           Stringstr = "";            if(!iter.hasNext()) {                str= "用户不存在!";                request.setAttribute("xname",lname.toString());                request.setAttribute("xphone",str.toString());           } else {                while(iter.hasNext()) {                     Elementelement = (Element) iter.next();                     Stringtelno = element.getText();                     Stringtelname = element.getParent().elementText("name");                     str= str + telname.toString() + ":" +telno.toString() + "; ";                }                request.setAttribute("xname",lname.toString());                request.setAttribute("xphone",str.toString());           }           request.getRequestDispatcher("/jsp/xpath_sqldemo.jsp").forward(request,   response);}

用户输入的数据,未进行任何合法性验证,直接代入执行了Xpath查询。如果用户输入了恶意数据,如wah’ or ‘1’=’1,则查询表达式为:/user_name_phone/user[name=’wah’ or’1’=’1′]/phone,表示查询所有用户的电话号码。

  • 漏洞代码修复

  • 用户输入的XPATH查询数据,在服务端正式处理前,进行合法性验证;
  • 对用户输入数据中的单引号和双引号进行转义,避免用户恶意输入的单引号或双引号被当成XPATH查询表达式的分隔符解释;
  • 屏蔽XPATH查询时的出错信息;
  • 参数化XPath查询:将需要构建的XPath查询表达式,以变量的形式表示,变量不是可以执行的脚本。

防止Xpath注入可使用:

a.使用ESAPI提供的方法对输入进行验证

publicvoiddoPost(HttpServletRequest request, HttpServletResponse response)   throwsServletException, IOException {     File f = new File("D:\Tomcat-7.0.55\webapps\owasp\xml\telphone.xml");     xpath_good_esapi(request,response, f);}
privatevoidxpath_good_esapi(HttpServletRequest request,HttpServletResponse response, Filef) throws ServletException, IOException { Stringlname = request.getParameter("username1"); lname = ESAPI.encoder().encodeForXPath(lname); Stringxpath = "/user_name_phone/user[name='" + lname + "']/phone"; SAXReadersaxReader = new SAXReader(); Documentdocument = null; try { document= saxReader.read(f); } catch(DocumentException e) { e.printStackTrace(); } Elementroot = document.getRootElement(); List<?>list = root.selectNodes(xpath); Iterator<?>iter = list.iterator(); Stringstr = ""; if(!iter.hasNext()) { …… } else { …… } request.getRequestDispatcher("/jsp/xpath_sqldemo.jsp").forward(request, response); }

b.自定义校验方法过滤特殊字符

privatevoidxpath_good(HttpServletRequest request,HttpServletResponse response, File f) throwsServletException,IOException {           Stringlname = request.getParameter("username1");           Stringxpath = "/user_name_phone/user[name='" + lname + "']/phone";           try {                if (checkValueForXpathInjection(lname)) {                     ……                                  }else{                     ……                }           } catch (Exceptione) {                e.printStackTrace();           }     } publicboolean checkValueForXpathInjection(String value) throws Exception {     boolean isValid = true;     if ((value != null) &&!"".equals(value)) {           StringxpathCharList = "()='[]:,*/ ";           StringdecodedValue = URLDecoder.decode(value, Charset.defaultCharset().name());           for (char c :decodedValue.toCharArray()) {                if(xpathCharList.indexOf(c) != -1) {                     isValid= false;                     break;                }           }     }