Skip to content

Latest commit

 

History

History
347 lines (142 loc) · 7.46 KB

S2-002 漏洞分析.md

File metadata and controls

347 lines (142 loc) · 7.46 KB

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

漏洞概述

Struts2-002 是一个 XSS 漏洞, 该漏洞发生在 <s:url> 和 <s:a > 标签中, 未对标签内字符进行转义, 当标签的属性 includeParams=all 时, 即可触发该漏洞。

漏洞影响版本:

Struts 2.0.0 - Struts 2.1.8.1 

更多详情可参考官方通告:

https://cwiki.apache.org/confluence/display/WW/S2-002

环境搭建

直接在上一次 S2-001 的环境上做一些修改

index.jsp 中, 将上次的 form 表单删掉, 添加一个 <s:url> 标签

<%--
Created by IntelliJ IDEA.
User: twosmi1e
Date: 2020/11/19
Time: 7:32 下午
To change this template use File | Settings | File Templates.
--%>
<%@ page language="java" contentType="text/html"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!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>S2-001</title>
</head>
<body>
<h2>S2-001 Demo</h2>
<p>link: <a href="https://cwiki.apache.org/confluence/display/WW/S2-001">https://cwiki.apache.org/confluence/display/WW/S2-001</a></p>


<s:url action="login" includeParams="all"></s:url>




</body>
</html>

LoginAction.java 中添加 url 变量, 将对用户名密码的处理删掉

package com.demo.action;


import com.opensymphony.xwork2.ActionSupport;


public class LoginAction extends ActionSupport {
private String username = null;
private String password = null;
private String url = null;


public String getUsername() {
return this.username;
}


public String getPassword() {
return this.password;
}


public String getUrl(){
return this.url;
}


public void setUsername(String username) {
this.username = username;
}


public void setPassword(String password) {
this.password = password;
}


public void setUrl(String url){
this.url = url;
}






public String execute() throws Exception {


return "error";
}
}

运行复现漏洞

成功触发 XSS

漏洞分析

当在 JSP 文件中遇到 Struts2 标签 <s: 时, 程序会先调用 doStartTag , 并将标签中的属性设置到对应标签对象相应属性中。最后, 在遇到 /> 结束标签的时候调用 doEndTag 方法。

跟进 component.start 函数

可以看到函数会根据标签 includeParams 属性不同做不同处理,all 和 get 的不同只有一个 mergeRequestParameters 方法, 在 includeParams 为 all 的情况下会调用 mergeRequestParameters 将 tomcat 处取来的参数:

这里取到了我们输入的 payload, 并且保存在 parameters 中。

而在 get 时会调用的两个函数中, includeGetParameters 方法则是先获取请求参数字符串, 再进行处理分割:

而调用 this.req.getQueryString 从 tomcat 处获取的请求参数字符串是经过 URL 编码的, 所以无法造成 XSS 漏洞。

includeExtraParameters 则一般为 null。

执行完 doStartTag 后进入 doEndTag, 调用 buildUrl。

然后返回 result 触发 XSS。

漏洞修复

2.0.11.1 版本修复

对 s:a 标签的修复如下:

if (this.href != null) {
this.addParameter("href", this.ensureAttributeSafelyNotEscaped(this.findString(this.href)));
}


加上了一个 ensureAttributeSafelyNotEscaped 方法来过滤双引号:


protected String ensureAttributeSafelyNotEscaped(String val) {
return val != null ? val.replaceAll("\"", """) : "";
}

对 s:url 标签的修复如下:

for(result = link.toString(); result.indexOf("<script>") > 0; result = result.replaceAll("<script>", "script")) {
;
}

在拼接完之后对 script 进行了处理, 但是只是简单的将 script 标签的两个尖括号去掉了, 所以可以轻松绕过:

http://localhost:8080/login.action?<script 1>alert(1)</script>=hello

2.2.1 版本修复

private static String buildParameterSubstring(String name, String value) {
StringBuilder builder = new StringBuilder();
builder.append(translateAndEncode(name));
builder.append('=');
builder.append(translateAndEncode(value));
return builder.toString();
}
public static String translateAndEncode(String input) {
String translatedInput = translateVariable(input);
String encoding = getEncodingFromConfiguration();
try {
return URLEncoder.encode(translatedInput, encoding);
} catch (UnsupportedEncodingException e) {
LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded");
return translatedInput;
}
}

使用 url 编码修复了漏洞。

参考

https://aluvion.gitee.io/2020/07/15/struts2 系列漏洞 - S2-002/

https://mochazz.github.io/2020/06/17/Java 代码审计之 Struts2-002/

end