本文介绍了java开发流行框架struts2以及webwork的一些安全缺陷,并举例说明框架自己以及开发人员使用框架时,所产生的种种安全问题,以及做者挖掘框架安全漏洞的一些心得体会。
推荐如下人群阅读:
了解java开发
了解框架开发
了解web application安全
“网络安全爱好者”
正文:
有了这些framework,让开发人员更加快速的开发出代码,也让代码很是具备可扩展性,那些分层架构的思想,更是深刻人心。这些也大大影响了安全代码审核,曾提出“分层审核代码”的思想,好比在DAO层专门检查sql注入,在view层检查xss等。这些框架都有本身的层级,本次文章主要讲的是struts这个框架的相关安全问题,也会有小部分涉及到struts后面的DAO层。
而struts这个框架更新占有市场份额极大的一个框架,它在各个层级中,位于如图所示位置:
能够看到struts在web应用中,负责处理接收用户数据,调用业务处理,以及展现数据的工做。因此本文把struts的功能分为controller层和view层,controller层来完成接收用户数据,分发用户请求,而view专门用于展现数据。
一个单独的struts,是不合逻辑的,由于架构师一般喜欢多种框架集合,让它们各自负责某一层的处理。研究一个框架的安全问题,不能仅仅站在框架的角度,还应该充分考虑到开发人员是如何使用这些框架的,他们最喜欢写什么样的代码,这样才能还原一个正常的、完整的web应用场景。
从搜索结果看,互联网中,绝大多数教程推荐struts+hibernate+spring这样的黄金组合,那么,我假设有一个应用使用了这个组合,以struts为重点,站在攻击者的角度,层层分析struts的设计缺陷。
Struts2
开发回顾与简单学习
为了让你们回顾或者学习一下struts2,咱们一块儿来创建一个action、jsp页面,作一个接收用户输入,以后处理一下,再展现出来给用户的过程,精通struts2的同窗能够跳过此步。
-------------------------------------struts
回顾start
首先创建action,叫作AaaaAction:
public class AaaaAction extends ActionSupport{javascript
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String execute(){
System.out.println("exe");
return SUCCESS;
}
public String bbb(){
System.out.println("bbbbb");
return SUCCESS;
}
}
请注意execute这个方法,让用户输入action的地址后,默认会访问这个方法。
以后配置struts.xml文件
user/aaa.jspphp
配置这个文件后,当用户输入
http://www.inbreak.net/app/aaaaaaa.action
的时候,struts会负责让AaaaAction中的execute方法处理用户请求。
处理以后,该方法返回“return SUCCESS;”,struts又负责找到result的name是seccuess所指向的jsp页面。把该页面解析后,返回给用户。html
而用户看到的就是aaa.jsp页面的html代码。
struts2
继承了webwork的全部优势,其实等因而webwork的升级,若是开发人员想让用户直接访问action中的某方法,而不是访问默认的execute方法,只要定义一个方法叫作bbb,而且是public的,用户就能够直接输入
http://www.inbreak.net/app/aaaaaaa!bbb.action
直接访问了bbb方法。
那request中的参数若是接收呢?struts2中,这个过程被包装了起来,使用很是方便,只要在action中定义一个属性,叫作public String name;。而后加入getName和setName方法,就能够像正常使用属性同样,接收到用户传递过来的变量。不管是get请求仍是post请求,均可以使用这种方式接收用户输入。
整个过程就如此简单,如今你们对流程有了了解,咱们就开始讨论正文,若是仍是想了解更多,请自行google。
----------------------------------struts
回顾end
Struts2
安全缺陷
能够看到struts2在数据流向方面,有两个重点,一个是进入(in),一个是输出(out)。而我在作漏洞挖掘的思路,也是跟着这个数据的流程,开始分析的,下面咱们就开始让数据进入。
Action
属性默认值能够被覆盖缺陷:
在平常的java项目中,咱们常常会遇到保存一个新的对象(好比注册一个用户),而后给这个对象赋予一些用户提交上来的属性值,在这里,只须要定义一个对象类:
public class User {java
private Long id=0l;
private String name;
private String pass;
private Integer type=1;
。。。下面的
get和set方法代码略
}
定义后,在action中,添加一个属性
User reguser;
用户注册的页面代码以下:
当用户提交这个form到action中后,struts2会负责自动映射reguser.name的值到reguser的相关属性(name)中,因此在execute这个方法中,就可使用reguser.getName()拿到用户提交的reguser.name的值。因此咱们下面的代码就很简单了:
public String execute(){
add(user);
add
方法,更简单了,由于咱们项目中集成了hibernate,这个框架自动映射user类中的各个属性,自动组成insert语句。咱们只要在add中调用session.save(user);就能够保存用户到数据库中。
前文提到那么多“简单”两个字,难道这些过程都是安全的而他给咱们仅仅带来了方便么?
struts2
只负责映射全部对象,他提供了form验证,也只能验证form中属性值的内容,好比email格式等,并不能约束用户提交其余属性上来,因而这就变成了十分危险的功能。
当User中有个属性type,表明User是否管理员时(1为普通用户,2为管理员),麻烦来了,攻击者在原来的注册表单中,新加入一个input,叫作
而后输入值是2,把这个值一块儿交给action。在这个流程中,这个值,固然也会被自动带到数据库中,向下处理的逻辑中,这个用户,就已经变成管理员了。
当你看到了一个struts2或者webwork的应用,能够尝试使用属性攻击,修改当前表单,里面有全部你猜想到的属性,一并提交上来,就可能会影响整个逻辑,达到攻击目的。文中仅仅是一个例子,事实上,在数据传递的过程当中,能够任意覆盖数据的默认值,原本就是一个危险的缺陷,而struts2和webwork这两个框架仅仅看到了它带来的好处,忽略了这方面基于安全性的考虑,仅仅关注了用户提交数据的正确性。对比在没有struts2这个功能的时候,咱们却须要在action中一个一个的把须要的变量,从用户提交的request中解出来,一个一个处理,不可能出现这种安全问题。如今它包装了这个过程,自觉得很方面,却出了严重问题。
Action
中的方法被暴力猜解缺陷:
前文提到,有一种方法可让用户访问action时,不访问默认的execute方法,而是直接访问其余action中的方法,条件是在action中,写一个public的方法。开发人员若是须要作一个登录后,展现全部用户列表的功能,而他的一个“解耦合”的开发习惯,将在这里致使安全缺陷。
定义一个以下的action
public class Userlogin extends ActionSupport{web
private String uname="";
private String upwd;
private List list;
//getter and setter 方法略
public String login(){
if(uname!=null&&upwd!=null&&uname.equals("kxlzx")&&upwd.equals("pass"))
{//if login success
return list();
}
return false;
}
public String list(){
list.add("kxlzx");list.add("kxlzx1");list.add("kxlzx2");list.add("kxlzx3");
return "list";
}
}
Userlogin
中,由于list这个功能(显示全部用户列表),实际上是一个通用的功能,很容易被其余地方调用,因此开发人员把它单独写成了一个方法。
当用户登录的时候,打开
http://www.inbreak.net/app/userlogin!login.action
来到了用户的登录页面,能够看到,只有用户输入正确的用户名和密码,才能最终调用list()方法,显示结果。
可是struts2把全部public的方法都暴露了出去,致使如今用户输入了
http://www.inbreak.net/app/userlogin!list.action
用户访问这个连接后,struts2调用list方法,而后返回结果给用户,因此没有登录,就显示了全部用户信息,直接绕过了login中的登录验证。
在没有struts2的时候,咱们要在servlet的doget或者dopost方法中,写if判断等代码,才能让用户调用其余servlet中的方法,如今看来其实这也是一种保护措施。而如今struts2为了方便开发,把全部的public方法统一映射了出去,致使开发把一个常用的功能,习惯写成一个public的方法,如今竟然成了严重漏洞。
struts2
的action属性设计缺陷:
再回头看看咱们在action中的属性定义,你会发现,如今他们都成了漏洞,由于struts2规定属性的get和set方法,都必须是public的。
那么咱们定义了
private String name;ajax
public String getName() {spring
return name;
}
public void setName(String name) {sql
this.name = name;
}
这段代码的时候,实际上,是写了两个public的方法。
那这两个表面上没有任何实质含义的方法,会有什么安全隐患呢?
这须要和前文联系起来,前文提到,咱们在struts.xml文件中,定义以下:
user/userlist.jsp数据库
user/addUser.jspjson
user/added.jsp
user/false.jsp
这段代码含义是,UserAction中,任何一个方法执行后,若是返回的是success这个字符串, 就会把user/userlist.jsp返回给用户。
若是返回是addUser,就会把user/addUser.jsp返回给用户。
如今UserAction是管理用户的页面,在咱们的系统中,有普通管理员和超级管理员,他们的区别是普通管理员能够查看用户,可是不能添加一个用户。
因此,咱们在UserAction中,写了
public String addUser(){
if(true){ //事实上这里是个超级管理员的判断,我偷懒了。
return "false";
}
return "addUser";
}
这个方法的代码判断了不容许普通管理员访问,可是user/addUser.jsp这个jsp页面中并无这个判断逻辑。由于开发认为只有返回addUser的时候,才会来到这个页面,而要返回addUser,则必须经过超级管理员的验证。
那咱们能让一个方法返回addUser么?固然能够!
http://www.inbreak.net/app/user!getUsername.action?username=addUser
这个连接,struts2会怎么处理呢?
他会找struts.xml中,对应段路径user,因而找到了对应的处理Action(net.inbreak.UserAction),因为路径中有了“!getUsername”,因而就去找这个Action中的getUsername这个方法,很明显,这个方法实际上是username这个属性的get方法,若是你要让Action接收用户提交的username,你就必需要定义这个方法。
那这个方法会返回什么呢?会返回action的字段username的值!哈哈!username用户已经提交给action了,连接后面写着“?username=addUser”,struts2把这个值赋予了action中的username属性。那这里返回的固然就是“addUser”!
一系列巧合后,致使如今给用户返回了user/addUser.jsp页面,这是一个添加用户的表单页面,而且用户没有去走验证是否为超级管理员这一步。
如今用户看到了一个添加用户的页面,他有两种攻击思路:
1
,直接提交,若是处理用户提交的那个action没有再次判断用户身份,那就提交成功了。
2
,若是他判断了用户身份,咱们还能够csrf他,由于咱们知道了这个action的地址,和它须要的参数!
因为struts2的action和jsp文件分离,致使开发人员每每会在action的方法中,执行权限判断,而jsp页面中并无再次执行这个判断,他觉得action判断就够了。而恰恰action的属性,给咱们带来了一个可自定义返回result的方法,致使咱们能够绕过action访问jsp页面。
Struts2
的那些result类型缺陷(redirect):
刚才咱们领教了struts2给咱们带来那些属性的好处,如今咱们再日后走一步,研究Action方法的返回结果。
其实并非只由String类型的返回结果,struts2还有其余类型的返回,好比“redirect”类型。
user/false.jsp
${redirecturl}
这段代码,你们惟一可能看不懂的,就是type="redirect"了。
这是一个url redirect的方式,struts2为了方便你们开发,把“自定义302跳转到其余url”这种方式给包装了起来。只要如上定义,咱们就能够在action中写方法:
public String redirect() {
return "redir";
}
而后定义属性
private String redirecturl;
当用户打开
http://www.inbreak.net/app/test!redirect.action?redirecturl=/a.jsp
的时候,就会302跳转到
http://www.inbreak.net/app/a.jsp
这是很常见的url跳转应用,在struts2中,如上配置一下,就能够实现。
相信明眼人都看出来了,很明显这里存在url跳转漏洞,若是用户输入了
http://www.inbreak.net/app/test!redirect.action?redirecturl=http://www.ph4nt0m.org
就会跳转到http://www.ph4nt0m.org这个钓鱼网站(-_-!)。那么如何防护呢?
文章有点长,不知道你们被我绕晕了没有,请先去休息下,思考前文内容是否看懂了。
书接上文,要防护url跳转到钓鱼网站,咱们确定须要一个白名单机制,或者根本就让他跳转到本站下。因而有了以下判断:
public String redirect() {
if(redirecturl.startsWith("/"))
{
return "redir";
}
return "false";
}
可能你看出来了,仅仅判断"/"开头,实际上是不能杜绝url跳转漏洞的,由于
http://www.inbreak.net/app/test!redirect.action?redirecturl=//www.ph4nt0m.org
同样会跳转。而在这里却足够了,由于struts2已经接管了这个过程,只要以“/”开头,通通先给你自动加上本地域名,抓包后,你会看到
location: http://www.inbreak.net/app//www.ph4nt0m.org
其实是不会有问题的。
struts2
也认为这样判断不会有问题了,然而用户输入
http://www.inbreak.net/app/test!getStr.action?str=redir&redirecturl=http://www.ph4nt0m.org
其实前篇已经分析过了,这样就利用action中的str属性,绕过了必须以“/”开头的判断,直接跳转了。
test
里有个str属性,可自定义返回,这里自定义了“redir”,因此来到了
${redirecturl}
而redirecturl的值,也提交给了action,因此跳转了。
Struts2
的那些result类型缺陷(Ajax):
在struts2中使用ajax,也是被struts2支持的,它提供了一种返回类型,叫作“stream”。在研究这个result的使用时,做者看到一本书,叫作《 Struts 2权威指南:基于WebWork核心的MVC开发 》。这本书很是出名,几乎全部的struts2使用者都推荐使用。
http://book.csdn.net/bookfiles/479/index.html
书上介绍ajax能够这么使用:
配置struts.xml
以后写TestajaxAction:
public InputStream input;
public String execute() throws Exception{
input = new StringBufferInputStream("aaaa
aa");
return SUCCESS;
}
其实你们都看出来个人意思了,返回了contentType为“text/html”的页面,内容为
结果浏览器解析的时候,出现了XSS漏洞。
原本默认的contentType是text/plain,不须要配置,若是用户直接打开,只会看到一个Stream,不会解析其中的html和js。如今书上介绍说要写成这样,不知道做者是否知道这个教程对你们的影响,结果已经误导了大批的开发人员。
事实上,这不是struts的问题,是struts“权威”教程的问题。权威的教程,一旦出现安全漏洞,每每会误导大批的开发人员,不知道你们在挖漏洞的时候,是否注意到了这点,特别是当官方的DEMO出现漏洞,那绝对是惊天地泣鬼神的悲剧。
Struts2
的那些result类型缺陷(自定的页面):
有时候,开发人员为了方便,喜欢配置struts.xml以下:
user/test.jsp user/testproperty.jsp ${redir} ${testloadfilepath} user/redirfalse.jsp user/input.jsp
请注意,其中一条result,名称是”testloadfilepath”,${testloadfilepath}的
做用是自定义的jsp页面地址,接收session或request中传过来的这个变量的值。那么用户提交
http://www.inbreak.net/app/test.action?testloadfilepath=user/test.jsp
固然就会返回user/test.jsp页面,很是的灵活。虽然并非全部的开发都会这么作,可是一旦出现这种状况,会产生什么问题呢?
http://www.inbreak.net/app/test!getRedir.action?redir=testloadfilepath&testloadfilepath=WEB-INF/classes/hibernate.cfg.xml
不知道你们看懂这段url的含义没有,先调用getRedir,能够自定义返回到testloadfilepath,而testloadfilepath已经指定了WEB-INF/classes/hibernate.cfg.xml。WEB-INF目录下,都是受web容器保护的东西,默认不容许直接request相对地址来访问。该目录里面有程序编译后的class文件(能够被直接反编译为java源码),有数据库配置文件等敏感文件,如今打开如上url,直接被下载了hibernate.cfg.xml,这里放着数据库用户名和密码。
这样,攻击者就能够下载你的全部源代码,全部服务器上的文件。struts在提供给咱们这种方式的时候,并无任何官方说明这里有危险,这就是一个不定时炸|弹。
struts2
的taglib设计缺陷
通过几个例子下来,不知道你们注意到没有,从用户输入走到这里,已经走到了输出这一步了。struts2的那些result的type,其实就是几种输出方式,有jsp、ajax、redirect,通过jsonplugin等插件配置,还能够支持其余输出方式。甚至支持一些标签库,好比freemarker等标签库。不过咱们只谈struts2自带的标签库,在一个jsp页面的最上方,写上一段代码,就可使用struts2提供的数据输出和页面数据操做的标签了。比以往咱们在jsp输出“<%=name%>”要方便的多,下面给个例子:
test.jsp
代码
<%@ taglib prefix="s" uri="/struts-tags" %> 第一行是告诉struts这里要使用struts的标签库,第二行就是一个标签的使用,含义是输出username的 值,username会从session、request、page等地方取,这里不关注取数据的次序。
struts2
的taglib设计缺陷(struts2.0不支持escapeJavaScript)
说到输出,你们都能想到XSS漏洞,那么做为一个流行框架,struts2在这里作了什么控制呢?
struts2.0
对部分标签作了默认的htmlescape:
刚才那个标签实际上效果等于
别觉得作了htmlescape就够了,输出在javascript中的时候,还会出现xss漏洞。因此struts在2.1.6这个版本也支持了javascriptescape:
struts2.1.6
:
默认开启如上所示,当你要输出到js中的时候,可使用escapeJavaScript进行转义。
也就是说,一旦你肯定这个struts是2.0的,只要开发人员把变量输出到js中,十有八九会出xss问题。
struts2
的taglib设计缺陷(没有富文本安全输出标签)
而包括最高版本2.1.8在内,仍然没有支持富文本安全输出,这是一件悲剧的事情,若是用struts开发一个大众blog的应用,又支持富文本的文章,开发人员只能把htmlescape和jsescape都去掉,才能保证业务正常运行,因此致使了XSS漏洞。
已有 0 人发表留言,猛击->>这里<<-参与讨论
JavaEye推荐
|