Java代码审计系列第一课 反射机制
简述
这次结尾有彩蛋哦,请记得查看!
自08年Web2.0时代开始之后,国内Web开发的主要语言从PHP逐渐转移到了Java,截止到今天,以Java为主要开发语言的公司越来越多,是现在大中型公司开发的主流编程语言。但是最近几年,Java的安全事件也是频频发生,Struts2,Weblogic,JBoss,Jenkins等等框架的反序列化漏洞层出不穷,国内对于Java安全也是越来越重视,而目前国内研究Java安全系统化的教程还比较少见,那么今天我就来分享一些自己的拙见,如果写的不好,或者有错误,也可以在后台给我留言,我也会及时改正。
反射
说道Java安全,就不得不说反序列化漏洞,而说道反序列化漏洞,就不得不说Java中的反射机制。
反射的概论
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
我总结一句话:反射就是把java类中的各种成分映射成一个个的Java对象
举个简单的例子:
//Person类
A a = (A)Class.forName("pacage.A").newInstance();
这和你 A a = new A(); 是一样的效果。
细说反射
package com.evalshll.User;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(String Name, int age){
System.out.println("我叫"+Name+",今年"+age+"岁");
}
}
1.获得Person对象的Class类实例
有三种方式获得Class类实例:
1.调用Class类的静态方法forName获取某个类的Class类实例:
Class clz = Class.forName("com.evalshll.User.Person");
2.访问某个类的class属性,这个属性就存储着这个类对应的Class类的实例:
Class clz = com.evalshll.User.Person.class;
3.调用某个对象的getClass()方法:
Class clz = (new Person()).getClass();
2.获取setName方法
单独获取某一个方法是通过Class类的以下方法获得的:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的
public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的
我们之前已经提到了Method这个类,java中所有的方法都是Method类型,所以我们通过反射机制获取到某个对象的方法也是Method类型的。通过Class对象获取某个方法:
clz.getMethod(方法名,这个方法的参数类型)
例:
Method method = clz.getMethod("setName", String.class);
3.调用setName方法
Method类中有一个invoke方法,就是用来调用特定方法的,用法如下:
第一个参数是调用该方法的对象,第二个参数是一个可变长参数,是这个方法的需要传入的参数,描述的不够直观,直接看代码:
package com.evalshell.Reflect;
import com.evalshell.User.Person;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
try{
Class c = Class.forName("com.yishishuo.User.Person");
Object o = c.newInstance();
Method method = c.getMethod("show", String.class, int.class);
method.invoke(o, "fengxuan", 20);
}catch (Exception exception){
exception.printStackTrace();
}
}
}
接着上文代码:
method.invoke((new Person()), "fengxuan");
或者
User user = new User();
method.invoke(user, "fengxuan");
动态特性
“动态特性”这个词语是直接引用phith0n
在Kcon上的演讲说到的一个术语,我用自己的话来解释一下,熟悉PHP代码审计的同学,可能知道PHP是一门弱类型的编程语言,比如说以下代码:
<?php
$bar= "a";
$Foo="Bar";
$World="Foo";
$Hello="world";
$a="Hello";
echo $a; //hello
echo $$a; //world
echo $$$a; //foo
echo $$$$$a; //Bar
echo $$$$$$a; //a
echo $$$$$$$a; //hello
echo $$$$$$$$a; //world
PHP动态变量是指一个变量名的变量名可以动态的设置和使用,一个变量获取另一个变量的值作为这个变量的变量名。
虽然Java没有PHP这些动态变量的特性,但是其反射机制也给我们带来一些类似上面的动态特性。比如下面这段代码
protected void Run(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
Class class1 = Class.forName(req.getParameter("className"));
class1.getMethod(req.getParameter("methodName")).invoke(class1.newInstance());
}catch (Exception exception){
exception.printStackTrace();
}
}
在这个例子中,有几个比较重要的方法需要说一下:
Class.forName()
动态的加载类newInstance()
实例化一个对象getMethod()
获取类中的方法invoke()
获取类的方法后,执行这个方法
这几个方法基本贯穿Java POP链中各种于反射有关的payload,java反射机制给漏洞利用提供了很多便利,我们可以在很多java漏洞的exp中看到它的影子,所以,学习java安全是绕不开它的。
反射的好处在于,如果说,在漏洞挖掘的过程中,发现上下文中没有String类型的变量,可以这么做
Person person = new Person();
person.getClass().forName("java.lang.Runtime")
.getMethod("exec", String.class)
.invoke(person.getClass()
.forName("java.lang.Runtime")
.newInstance(), "whoami");
默认情况下, forName 的第⼀一个参数是类名;第⼆二个参数表示是否初始化;第三个参数就 是 ClassLoader 。
什么是ClassLoader
顾名思义,它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。
可以直接使用ClassLoader来加载完整的类名路径,比如说上面的com.evalshell.User.Person
从而实例化这个类。
在我们构造Payload的时候,就会使用到ClassLoader中的一些利用手法,这个我们下节课再继续讲解。
总结
说点别的…
安全近乎全栈,知识多且碎片化
所以我做了知识库,且一直在改进,更新。
我想请问各位小伙伴们你们觉得知识库如何改进才能更好的解决安全知识碎片化这个问题?
有想法的小伙伴们可以一起交流交流,😁
希望小伙伴们留言给我一点建议,非常感谢!
还有一件事,我也开始建群啦,哎呀,被人吐槽只会写文章不会推广,嗨,不就是群嘛,小伙伴们,冲了!
如果二维码失效,请在公众号里回复 【加群】获取最新二维码
最后发一张总结了Java 反射机制的思维脑图,大家有时间可以看看。
参考链接
https://github.com/phith0n