0x01 前言
这个漏洞给我的感觉和Groovy有点像,都是在 Java 上解析和执行命令,也属于Lisp编程语言,本文着重点在于分析如何触发漏洞,具体的解析流程可能不会过于深入,分析不到位的地方,大佬们多多包涵。
0x02 漏洞分析
01 命令执行浅析
我们先通过一段代码来了解一下Clojure是如何执行命令的,使用如下代码
import clojure.main$eval_opt;
public class Demo1 {
public static void main(String[] args) {
domain();
}
private static void domain() {
String clojurePayload = "(use '[clojure.java.shell :only [sh]]) (sh "calc")";
Object invoke = new main$eval_opt().invoke(clojurePayload);
}
}
在invoke方法打上断点,调试看看
可以看到将构造的payload传入了invokeStatic方法,跟入
这里会先将payload传入StringReader然后转化为LineNumberReader对象,之后将reader对象传入 main$eval_opt$fn__7422 构造方法,如图
构造方法进行了赋值,之后调用 main$eval_opt$fn__7422 的 invoke 方法
先是将reader赋值给了 var10000,然后调用 reader 对象的 invokeStatic 方法,由于有些方法进不去,就不一一展示了,之后会回到 clojure.main$eval_opt#invokeStatic 方法,调用 clojure.core$eval.invokeStatic(var10000),跟入
主要就是在 Compiler.eval 中解析 payload 然后触发命令执行的,我们可以看下核心代码,clojure.java.shell$sh#invokeStatic
可以看到最后调用的就是Runtime的exec方法执行的命令弹出计算器
02 如何通过反序列化触发漏洞
通过前面的调试我们可以知道,如果能够调用 main$eval_opt().invoke 方法,且传入的内容为我们构造的payload,即可触发命令执行,作者是在 clojure.core$comp$fn__4727#invoke(java.lang.Object) 方法找到的命令执行点,如图
其实看到这个就很明显了,只要 this.f 和 this.g 可控,那么就可以用来命令执行了
我们看一下这两个属性是如何赋值的
实例化的时候进行赋值,那么如何控制返回值为我们的payload呢,其实就是找什么类执行invoke之后能返回我们要的payload,这里作者找到的是 core$constantly 对象,我们可以看一下
将payload传入了invokeStatic方法,然后传入了构造方法 core$constantly$fn__4614 ,跟入
这里将payload传给了this.x,当调用invoke方法时会返回this.x的值,如图
有点类似cc链的那种感觉,存一个对象再返回这个对象
到这里,我们大致了解了命令执行点在什么位置,以及如何构造,接下来就是如何触发的问题了
我们通过调试来讲解,运行debug程序
先是构造了payload,然后创建了三个对象,AbstractTableModel$ff19274a 这个是关键类,其他的都是用来触发的,将model作为key放置到targetMap集合中,继续往下
这里可以看到将payload传入了 clojure.core$constantly().invoke 方法,也就是前面分析的赋值给了this.x然后invoke返回payload的值,然后实例化了 clojure.main$eval_opt 对象,将这两个对象传入 clojure.core$comp().invoke 方法,跟入看下
将 clojure.main$eval_opt 对象赋值给了var10000,clojure.core$constantly 对象赋值给了var10001,传入invokeStatic,跟入
这里也是赋值,然后传入构造方法实例化 core$comp$fn__4727 对象
this.g 是 clojure.core$constantly 对象,this.f 是 clojure.main$eval_opt对象
返回的对象作为 value 赋值给了 fnMap 对象的 hashCode 键
后面调用 model.__initClojureFnMappings(PersistentArrayMap.create(fnMap)),我们跟入看下
就是将fnMap对象转化后赋值给了 __clojurefnMap,后面我们会用到
接下来就开始反序列化,反序列化的是HashMap对象,跟入readObject
这里key是 AbstractTableModel$ff19274a 对象,传入hash方法,跟入
调用key的hashCode方法,也就是 clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a#hashCode 方法,跟入
这里先是从 this.__clojureFnMap 对象中取出hashCode键对应的值,也就是前面的 core$comp$fn__4727 对象,取出来之后调用其invoke方法,跟入
这里就是我们前面分析的触发点了,有些代码动态调试进不去,所以这里一部分是静态分析,到了这一步后面的触发过程就和前面分析的一样了,最后通过Runtime对象触发命令执行
0x03 总结
本次漏洞分析其实没有太多新的东西,也是典型的对象可控导致的反序列化命令执行,主要是找到漏洞触发点,对clojure这个组件也不是那么了解,所以可能有分析不到位的地方,当作学习笔记了。
0x04 参考文章
java反序列化漏洞(https://su18.org/post/ysoserial-su18-5/#clojure)
原文始发于微信公众号(伟盾网络安全):ysoserial分析之Clojure利用链
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论