初探java安全之反射(1)
前言
上次和亮去接了个渗透的比赛,结果我还是啥都不会,当时意识到现在大多数的网站的后端都基本上是 java 和 go了,想 php 的基本上比较少了,php 在以后肯定会没落的,java不想 php 那样 简单易用且灵活,所以很有必要系统性的学习一下java安全。
初探java反射
Java安全可以从反序列化漏洞开始说起,反序列化漏洞⼜可以从反射开始说起。 反射是⼤多数语⾔⾥都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有 ⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将Java这种静态语⾔附加上动态 特性。
java反射这块学校的教科书上并没有,我是参考这篇学习的 https://www.cainiaojc.com/java/java-reflection.html
什么是java反射
Java中有一个名为Class的类,该类在运行时保留有关对象和类的所有信息。
Class对象描述了特定类的属性。该对象用于执行反射。
反射机制允许程序在执行期借助于 ReflectionAPI
取得任何类的内部信息(比如成员变量、构造器、成员方法等等),并能操作对象的属性及方法。 加载完类后,在堆中就产⽣了⼀个Class类型的对象(⼀个类只有⼀个Class对象),这个对象包含了类的完整结构 信息。通过这个对象得到类的结构。
利用反射机制可以使 java 这样的编译型语言 更加灵活起来
Java反射机制可以完成:
- 在运⾏时判断任意⼀个对象所属的类
- 在运⾏时构造任意⼀个类的对象
- 在运⾏时得到任意⼀个类所具有的成员变量和⽅法
- 在运⾏时调⽤任意⼀个对象的成员变量和⽅法
- ⽣成动态代理
反射相关的主要类如下:
1、Java.lang.Class:代表⼀个类,Class对象表示某个类加载后在堆中的对象
2、Java.lang.reflect.Method:代表类的⽅法
3、Java.lang.reflect.Field:代表类的成员变量
4、Java.lang.reflect.Constructor:代表类的构造⽅法
反射要用到的一些方法
参考 https://www.cainiaojc.com/java/java-reflection.html
获得名为 Class 的类的对象
使用
forName()
方法forName()
接受字符串参数(类的名称)并返回Class对象。返回的对象引用字符串指定的类。例如,Class Dog { } Class c1 = Class.forName("Dog"); //获得名为Dog的Class对象
使用getClass()方法
getClass()
方法使用特定类的对象来创建新的对象Class。例如,Dog d1 = new Dog(); Class c1 = d1.getClass();
使用.class
java 的每个类中默认会创建一个名为
class
的属性,该属性就是该类的Class
对象我们还可以使用 .class 扩展名创建Class对象。例如,
Class c1 = Dog.class; //使用 类名.class 获得Class对象
创建Class对象后,我们可以使用这些对象执行反射。
获取接口
若一个类实现了某个接口,可以使用 Class 的 getInterfaces()
方法来获取。此方法返回一个接口数组。
Class obj = d1.getClass();//获得class对象
Class[] objInterface = obj.getInterfaces();//获得接口
获取父类和访问修饰符
类 Class 的方法 getSuperclass()
可用于获取有关特定类的父类的信息。
而且,Class提供了一种 getModifier()
方法,该方法以整数形式返回class的修饰符。
Class obj = d1.getClass();
//以整数形式获取Dog的访问修饰符
int modifier = obj.getModifiers();
//获取Dog的父类
Class superClass = obj.getSuperclass();
获取字段(属性)
我们可以使用 Field
类提供的各种方法检查和修改类的不同字段。
- getFields() -> Field 返回该类及其父类的所有公共字段
- getDeclaredFields() -> Field[] 返回类的所有字段
- getModifier() -> int 以整数形式返回字段的修饰符
- set(Object, value) -> void 使用指定的值设置字段的值(注意是 Object,是个对象实列)
- get(Object) -> Object 获取字段的值 (注意是 Object)
- setAccessible(boolean) -> void 使私有字段可访问
注意:如果我们知道字段名称,则可以使用
- getField(“fieldName”) -> Field 从类返回名称为 fieldName 的公共字段。
- getDeclaredField(“fieldName”) -> Field 从类返回名称为 fieldName 的字段。(若获得的是私有字段,则要在使用之前设置
setAccessible(true)
)
例如有
class Dog {
public String type;
}
访问公共字段
Dog d1 = new Dog();
//创建Class对象
Class obj = d1.getClass();
//操纵Dog类的公共字段type
Field field1 = obj.getField("type");
//设置字段的值,第一个参数是对象实例
field1.set(d1, "labrador");
//通过转换成字符串来获取字段的值,第一个参数是对象实例
String typeValue = (String)field1.get(d1);
//获取类型的访问修饰符
int mod1 = field1.getModifiers();
String modifier1 = Modifier.toString(mod1);
访问私有字段
设有
class Dog {
private String color;
}
Dog d1 = new Dog();
//创建类Class对象
Class obj = d1.getClass();
//访问私有字段
Field field2 = obj.getDeclaredField("color");
//使私有字段可访问
field2.setAccessible(true);
//设置color值(注意第一个参数为对象实例)
field2.set(d1, "brown");
// get the value of type converting in String
String colorValue = (String)field2.get(d1);
//获取color的访问修饰符
int mod2 = field2.getModifiers();
String modifier2 = Modifier.toString(mod2);
获取方法
像字段一样,我们可以使用 Method
类提供的各种方法来检查类的不同方法。
- getMethods() -> Method[] 返回该类及其超类的所有公共方法
- getDeclaredMethod() -> Method[] 返回该类的所有方法
- getName() -> String 返回方法的名称
- getModifiers() -> int 以整数形式返回方法的访问修饰符
- getReturnType() -> Class<?> 返回方法的返回类型
class Dog {
public void display() {
System.out.println("I am a dog.");
}
protected void eat() {
System.out.println("I eat dog food.");
}
private void makeSound() {
System.out.println("Bark Bark");
}
}
class ReflectionDemo {
public static void main(String[] args) {
try {
Dog d1 = new Dog();
//创建一个Class对象
Class obj = d1.getClass();
//使用getDeclaredMethod()获取所有方法
Method[] methods = obj.getDeclaredMethods();
//获取方法的名称
for(Method m : methods) {
System.out.println("方法名称: " + m.getName());
//获取方法的访问修饰符
int modifier = m.getModifiers();
System.out.println("修饰符: " + Modifier.toString(modifier));
//获取方法的返回类型
System.out.println("Return Types: " + m.getReturnType());
System.out.println(" ");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
获取构造方法
可以使用 Constructor
类提供的各种方法检查类的不同构造函数。
- getConstructors() - 返回该类的所有公共构造函数以及该类的父类
- getDeclaredConstructor() -返回所有构造函数
- getName() - 返回构造函数的名称
- getModifiers() - 以整数形式返回构造函数的访问修饰符
- getParameterCount() - 返回构造函数的参数数量
import java.lang.Class;
import java.lang.reflect.*;
class Dog {
public Dog() {
}
public Dog(int age) {
}
private Dog(String sound, String type) {
}
}
class ReflectionDemo {
public static void main(String[] args) {
try {
Dog d1 = new Dog();
Class obj = d1.getClass();
//使用getDeclaredConstructor()获取一个类中的所有构造函数
Constructor[] constructors = obj.getDeclaredConstructors();
for(Constructor c : constructors) {
//获取构造函数的名称
System.out.println("构造函数名称: " + c.getName());
//获取构造函数的访问修饰符
int modifier = c.getModifiers();
System.out.println("修饰符: " + Modifier.toString(modifier));
//获取构造函数中的参数数量
System.out.println("参数个数: " + c.getParameterCount());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
实例化对象与执行函数
- newInstance() 实例化类对象的方法 ,位于 Class 类中 (只能无参构造)
Class clsd = Class.forName("Dog");
Dog dog = (Dog) clsd.newInstance();//通过反射实例化一个对象
dog.display();
- invoke 执行函数的方法,位于 Method 类中
//执行Dog中的display方法
public class Invo {
public static void main(String[] args) throws Exception {
execute("Dog", "display");
}
public static void execute(String className, String methodName) throws Exception {
Class clazz = Class.forName(className);
clazz.getMethod(methodName).invoke(clazz.newInstance());
}
}
class Dog {
public void display() {
System.out.println("I am a dog.");
}
}
重点方法
- 获取类的方法
foName
- 实例化类对象的方法
newInstance
- 获取函数的方法
getMethod
- 执行函数的方法
invoke
基本上,这几个方包揽了Java安全里各种和反射有关的Payload。
public void execute(String className, String methodName) throws Exception {
Class clazz = Class.forName(className);
clazz.getMethod(methodName).invoke(clazz.newInstance());
}