Struts2漏洞调试笔记[S2-001]

配置文件

Struts.xml:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="struts2" extends="struts-default">
<action name="login" class="meizj.test">
<result name="success">success.jsp</result>
<result name="error">index.jsp</result>
</action>
</package>
</struts>

web.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Struts2-001 Example</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

index.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Struts2 001 Demo</title>
</head>
<body>
<h1>Struts2 001 Demo</h1>
<s:form action="login">
<s:textfield name="username" label="username"></s:textfield>
<s:textfield name="password" label="password"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
</html>

success.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8" 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>
<p>Hello <s:property value="username"></s:property></p>
</body>
</html>

漏洞调试

先简单了解下什么是拦截器:

拦截器可以在Action的业务逻辑执行前后进行拦截调用,从而实现特定功能。

而在Struts2 001这个洞中,触发点在于表单字段位置,因此先来看看拦截器的加载情况。

Struts2中规定了一些默认加载的拦截器,在Struts-default.xml中可以找到,我们直接看到对应的Param:

调用的类为com.opensymphony.xwork2.interceptor.ParametersInterceptor,跟进ParametersInterceptor,可以看到:

在拦截器作用时,会进行值栈操作,我这里把getValueStacksetParameters以及return invocation.invoke()都打了断点。

接下来可以step over,接下来会走到return invocation.invoke(),此时开始换用step into,接着经过几次步入,会抵达

步入executeResult:

不断步入,直到到达doFilter方法后步入开始不显示源码,继续步入即可解决:

直到开始解析jsp文件,对于前面的标签可以直接step over,着重关注password字段的处理即可。

步入到org.apache.struts2.views.jsp.ComponentTagSupport,执行doStartTag:

解析结束标签doEndTag:

步入到org.apache.struts2.components.UIBeanend():

步入evaluteParams():

执行this.altSyntax()后,由于默认是支持动态执行OGNL的因此会返回True,从而完成对expr的赋值.

而对expr中的值进行计算的过程则在这一步进行:

步入后会进入findValue()

继续步入,接着进入translateVariables(),在translateVariables中,有一步是十分关键的:

即,循环解析{符号,我们可以通过插入%{1+1}这样的poc使得执行OGNL表达式.

上面两张图便是循环两次分别的变量值,可以看到在第一次解析后,对password中的OGNL表达式循环执行了,在findValue()中完成了对OGNL的执行。

EXP构造

作为Struts2系列漏洞的"鼻祖",此时还是没有任何安全防范措施的,因此直接执行exec: