关于跨站脚本攻击
  跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页面时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
审计思路
XSS一般分为反射型、存储型、DOM型。DOM型比较特殊。下面主要是围绕存储型和反射型进行整理。XSS的产生主要是因为前端传递的数据未经检验就被作为动态内容提交给用户浏览器执行。那么也就是说,核心的要点有:
* 参数用户前端传递可控
* 在view层会对用户的输入进行输出
而且XSS如果一处接口存在,那么很大可能全部接口都是存在类似的安全问题的。
所以要点首先肯定是挖掘一条完整的攻击链,不是简单的例如没有过滤器,没发现输出编码的相关函数就可以断定存在xss缺陷了,例如一些webservice很多都是没有view层渲染的,主要是进行数据的封装和传递。
反射型跟存储型的区别主要是存储型会经过DAO层进行类似数据库的更新或者写入操作,最终在view层进行展示。
所以整个审计过程要点还是定位用户的输入输出,也就是梳理数据交互以及前端展示的过程。找到一条完整的利用链之后,就是结合现有的安全措施(输出编码、过滤器等)进行判断,例如是否存在绕过的可能,或者是没有任何安全防护可直接造成攻击。
快速定位输入输出
所谓输入输出,主要是前端渲染还有后端处理,例如前端EL表达式接受数据,展示内容,后端接受用户输入,写入数据库完成注册业务等。
首先是前端渲染,主要是看前端渲染的内容是什么,可以简单的定位前端输出的常见标识,然后结合后端逻辑进行判断。例如发现前端进行了用户个人信息的展示,那么我们就可以关注后端注册接口,是否对用户名、地址等信息进行xss的相关防护。
常见的view层展示数据方式如下:
* JSP表达式
<%=%>,用于将已经声明的变量或者表达式输出到网页上面。
* 相关案例:
获取前端输入的id,然后展示在JSP页面中:
java
<%
String id = request.getParameter("id");
%>
<%=id%>
* EL表达式
EL(Express Lanuage)表达式可以嵌入在jsp页面内部,减少jsp脚本的编写,EL出现的目的是要替代jsp页面中脚本的编写。
${expression},用于指定要输出的内容,可以是字符串,也可以是由EL运算符组成的表达式。
* 相关案例:
例如在request域中存储了一个名为"user"的对象,我们不需要通过${requestScope.user}取出相应的对象,只需要${user}即可取出相应的对象,获取对应的属性的话只需要调用对应的get方法即可。
java
<li>${user.getUsername()}</li>
<li>${user.getRole()}</li>
<li>${user.getDepartment()}</li>
从pageContext域,request域,session域,application域中获取属性,在某个域中获取后将不在向后寻找。
* JSTL标签
JSTL(JSP Standard Tag Library),JSP标准标签库,可以嵌入在jsp页面中使用标签的形式完成业务逻辑等功能。jstl出现的目的同el一样也是要代替jsp页面中的脚本代码。
常见标签如下:
(1)
用于将表达式的值输出到JSP页面中,类似于JSP表达式<%=表达式%>,或EL表达式${expression}。
例如:结合EL表达式,登陆后从session里获取当前用户的用户名,显示当前登陆的用户:
<c:out value="${user.getUsername()}"/>
(2)
条件判断标签:根据不同的条件处理不同的业务。
例如用标签根据是否登陆显示不同的内容,若相关session中的user不为空,则说明登陆成功,则显示user对象中的用户名和注销按钮:
<c:if test="${!empty user}">
<li>${user.name}</li>
<li><a href="./logout.action">注销</a></li>
</c:if>
(3)
循环标签:可以根据循环条件,遍历数组和集合类中的所有或部分数据。
例如,使用标签遍历商品菜单:
<c:forEach var="pt" items="${requestScope.products}" varStatus="status">
<tr>
<!-- 商品名称 -->
<td>
<!-- 输出商品名称 -->
${pt.name }
</td>
<td>
<!-- 输出商品地址 -->
${pt.area }
</td>
<td>
<!-- 输出商品价格 -->
${pt.price }
</td>
</tr>
</c:forEach>
通过上面的例子可以看到,利用EL表达式结合JSTL标签,可以完成对应的前端渲染工作,那么对应实际的跨站脚本的挖掘,也可以简单的通过这些标签进行定位,例如标签经常用户循环的列表输出,如果目标应用是一个管理后台,那么很可能是用于用户展示,那么很可能存在存储型的XSS,可以定位对应的前端页面,联动整个后端处理过程,进行对应的审计。
然后就是后端处理,实际业务中一般在前端与后台之间互相传递参数,一般主要是数据封装、入库等,通过查看后端处理可以快速的定位我们前端传入的内容是否写入了数据库(存储XSS),亦或是存在什么安全措施等。
所以关注点主要在如何快速定位后端如何封装数据,封装什么数据。什么参数是前端传输且可控的,以常见的Spring MVC和Spring Boot为例,常见的有:
(1)ModelAndView
例如这里会通过id进行用户信息的查询,然后封装后跳转到stulist.jsp,那么我们可以进一步在stulist.jsp视图层查看展示的相关用户信息是否可控以及,例如用户名、地址等。然后这里通过service层与数据库进行了交互,如果可控的话那么很大可能会存在存储型XSS。
java
/*
根据id查询用户
*/
@RequestMapping(value = "/sid/{sid}",method=RequestMethod.GET)
public ModelAndView getUserById(@PathVariable String sid){ //sid参数对应方法上的路径参数{sid}
ModelAndView mv = new ModelAndView("stulist"); //跳转到stulist.jsp
User user = userService.getUserById(sid);
mv.addObject("user",user);
return mv;
}
(2)ModelMap
java
@RequestMapping(value = "/sid/{sid}",method=RequestMethod.GET)
public String getUserById(ModelMap modelMap,String sid) {
User user = userService.getUserById(sid);
modelMap.addAttribute("userInfo", user);
return "stulist";
}
(3)Model
java
@RequestMapping("/testModel")
public String testModel(Model model,String email) {
model.addAttribute("email",email);
return "success";
}
通过搜索ModelAndView、ModelMap、Model等关键字,我们可以快速定位哪些接口映射是与前端展示相关的,通过这些接口定位对应的jsp、html,定位对应的输出内容。
当然了,类似request对象的各种方法,同样也是关注点,例如request.getParameter()。
综上,可以总结出一些常用的关键字:
<%=
${
<c:out
<c:if
<c:forEach
ModelAndView
ModelMap
Model
request.getParameter
request.setAttribute
结合这些关键字,可以快速定位整个前后端交互,处理和渲染的过程。举个例子:
访问auditView接口,传递auditId参数,并且将其值封装在Model里,然后返回相关jsp页面view:
```java
@RequestMapping("/auditView")
public String auditView(Model model,HttpSession session,
@RequestParam("auditId") String auditId) {
try{
MpDataAuditChangeDto mpDataAuditChangeDto = mpDataAuditChangeService.selectByPrimaryKey(auditId);
if(UserModelContext.get().getUserName().equals(mpDataAuditChangeDto.getCreateUser())){
PageMessageUtil.saveErrorMessage(session, "用户不能审核自己创建的信息");
return "redirect:" + REQUEST_BASE_PATH + "auditList";
}
BdsCurrencyRelationshipDto BdsCurrencyRelationshipDto = bdsCurrencyRelationshipDtoService.selectByPrimaryKey(mpDataAuditChangeDto.getRefId());
model.addAttribute("BdsCurrencyRelationshipDto", BdsCurrencyRelationshipDto);
model.addAttribute("changeType",mpDataAuditChangeDto.getChangeType());
model.addAttribute("auditId",auditId);
}catch(Exception e){
}
return "view";
}
  在对应的jsp页面view.jsp直接把auditId的值直接写入到jsp页面中通过EL表达式进行展示:
java
评论