目录
(一)XSS 常见触发位置
1.JSP 表达式
2.EL
(二)反射型 XSS
(三) 存储型 XSS
(四)XSS 漏洞修复
配置过滤器:
(一)XSS 常见触发位置
XSS 漏洞产生后必然会有相关的输入 / 输出,因此我们只需快速找到这些输入 / 输出点,即可快速地进行跟踪发现漏洞。输入在 Java 中通常使用“ request.getParameter(param)”或“${param}”获取用户的输入信息。输出主要表现为 前端的渲染,我们可以通过定位前端中一些常见的标识来找到它们,然后根据后端逻辑来判断漏洞是否存在。
1.JSP 表达式
“ <%= 变量 %> ”是“ <% out.println( 变量 ); %> ”的简写方式,“ <%=%> ”用于将已声明的变量或表达式输出到外网页中。
- 形式一:
<%=msg%>
<% out.println(msg); %>
- 形式二:
<% String msg = request.getParameter('msg');%>
<%=msg%>
通过“request.getParameter”获取 msg 传入的值,然后通过“<%=msg%>”将其输出到网页中
2.EL
EL( Expression Language ,表达式语言)是为了使 JSP 写起来更加简单。 EL 的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,使得 JSP的 代 码 更 加 简 化 。 例 如 :“ <%=request.getParameter("username")%> ”等价于“ ${param.username} ”。 JSP 标准标签库( JSTL )是一个 JSP 标签集合,它封装了 JSP 应用的通用核心功能。
(二)反射型 XSS
我们先updatecert一下,发现红框中的数据直接输出了所以我们直接进行xss即可
直接拼接payload
接下来我们来看一下源码
查看后端代码可以发现,后端没有进行任何过滤直接进行了拼接输出
(三) 存储型 XSS
存储型 XSS 与反射型 XSS 核心原理一致,都是将 JavaScript 通过程序输出到 HTML页面中并交由浏览器引擎解析。相比于反射型 XSS,存储型 XSS 危害更大。反射型 XSS 需构造恶意 URL 来诱导受害者点击,而存储型 XSS 由于有效载荷直接被写入了服务器中,且不需要将有效载荷输入到 URL 中,往往可以伪装成正常页面,迷惑性更强。因此存储型 XSS 漏洞对于普通用户而言很难及时被发现。
若过滤不严,存储型 XSS 极易造成网络蠕虫,导致大量 Cookie 被窃取。当然存储型XSS 也可用作权限维持来窃取网站后台的登录信息,存储型 XSS 攻击流程如图 2-53 所示。
根据 xml 中的调用可以跟踪到调用类,在类中可以发现,程序首先对“ MessageInfo ServiceImpl”类进行了实例化操作,随后通过 setAttribute 方法对 msg 的值进行存储,并通过“getRequestDispatcher ”将其重定向至“ message.jsp ”文件进行输出,如下图 所示。
- 追踪 msg 的值,其来源于“msginfo.MessageInfoShowService()”,跟进该方法可以发现程序最终调用了 MessageInfoShowDao 方法,如图
- 跟进 MessageInfoShowDao 方法,可发现其对数据库进行了连接操作,并从 message数据表中查询出全部数据,赋值给对应的 name、mail、message 并返回给 servlet 层,如下图所示
- 回到 message.jsp,可以看到 message.jsp 文件从 list 中取出之前存入的 name mail messgae,并渲染 HTML 输出至浏览器,如下图所示:
到此漏洞的整个执行流程我们已分析完成,接下来便是找到对应的入口进行触发,而触发该漏洞一个最大的问题是:数据来源于数据库,我们需要对其进行控制。也就是说,想要触发该 XSS 漏洞,首先得将包含 XSS 有效载荷的数据插入数据库中。通过搜索对应的关键字可以在 MessageInfoStoreDao 中找到数据对应的入库操作,如下图所示:
那么接下来的问题便回到了 MessageInfoStoreDao 在哪里被调用,通过调用栈可以发现 MessageInfoStoreService 方法中调用了 MessageInfoStoreDao,如下图所示:
通过调用栈继续跟进 MessageInfoStoreService,可以发现在 StoreXSS 中对其进行了调用,并且 MessageInfoStoreService 中的三个参数全部直接来源于 GET 方法,如下图所示
因为程序没有对输入进行任何校验,所以只需找到添加数据的路由后插入对应的有效载荷
(四)XSS 漏洞修复
- 编写全局过滤器实现拦截,并在 web.xml 进行配置。下面将给出一个网上使用较多的拦截器样例。
配置过滤器:
public class XSSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws
IOException, ServletException {
chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
}
}
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XSSRequestWrapper extends HttpServletRequestWrapper {
public XSSRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = stripXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
return stripXSS(value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return stripXSS(value);
}
private String stripXSS(String value) {
if (value != null) {
// NOTE: It's highly recommended to use the ESAPI library and uncomment the following
line to
// avoid encoded attacks.
// value = ESAPI.encoder().canonicalize(value);
// Avoid null characters
value = value.replaceAll("", "");
// Avoid anything between script tags
Pattern scriptPattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE |
Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE |
Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome tag
scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome tag
scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE |
Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid eval(...) e-xpressions
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE |
Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid e-xpression(...) e-xpressions
scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE |
Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid javascript:... e-xpressions
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid vbscript:... e-xpressions
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid onload= e-xpressions
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE |
Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
}
return value;
}
}
// HTML Context
String html = ESAPI.encoder().encodeForHTML("<script>alert('xss')</script>");
// HTML Attribute Context
String htmlAttr = ESAPI.encoder().encodeForHTMLAttribute("<script>alert('xss')</script>");
// Javascript Attribute Context
String jsAttr = ESAPI.encoder().encodeForJavaScript("<script>alert('xss')</script>");
<%
String Str = "<script>alert('XSS')</script>";
Str = Str.replaceAll("\"",""");
Str = Str.replaceAll("&","&");
Str = Str.replaceAll("\\(","(");
Str = Str.replaceAll("<","<");
Str = Str.replaceAll(">",">");
Str = Str.replaceAll("\'","'");
Str = Str.replaceAll("\\)",")");
out.println(Str);
%>