前言
这周收到外部合作同事推送的一篇文章,【漏洞通告】Apache Dubbo Provider默认反序列化远程代码执行漏洞(CVE-2020-1948)通告。
按照文章披露的漏洞影响范围,可以说是当前所有的 Dubbo 的版本都有这个问题。
无独有偶,这周在 Github 自己的仓库上推送几行改动,不一会就收到 Github 安全提示,警告当前项目存在安全漏洞CVE-2018-10237。
可以看到这两个漏洞都是利用反序列化进行执行恶意代码,可能很多同学跟我当初一样,看到这个一脸懵逼。好端端的反序列化,怎么就能被恶意利用,用来执行的恶意代码?
这篇文章我们就来聊聊反序列化漏洞,了解一下黑客是如何利用这个漏洞进行攻击。
先赞后看,养成习惯!微信搜索『程序通事』,关注就完事了!
反序列化漏洞
在了解反序列化漏洞之前,首先我们学习一下两个基础知识。
Java 运行外部命令
Java 中有一个类 Runtime
,我们可以使用这个类执行执行一些外部命令。
下面例子中我们使用 Runtime
运行打开系统的计算器软件。
// 仅适用macos
Runtime.getRuntime().exec("open -a Calculator ");
有了这个类,恶意代码就可以执行外部命令,比如执行一把 rm /*
。
序列化/反序列化
如果经常使用 Dubbo,Java 序列化与反序列化应该不会陌生。
一个类通过实现 Serializable
接口,我们就可以将其序列化成二进制数据,进而存储在文件中,或者使用网络传输。
其他程序可以通过网络接收,或者读取文件的方式,读取序列化的数据,然后对其进行反序列化,从而反向得到相应的类的实例。
下面的例子我们将 App
的对象进行序列化,然后将数据保存到的文件中。后续再从文件中读取序列化数据,对其进行反序列化得到 App
类的对象实例。
public class App implements Serializable {
private String name;
private static final long serialVersionUID = 7683681352462061434L;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
System.out.println("readObject name is "+name);
Runtime.getRuntime().exec("open -a Calculator");
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
App app = new App();
app.name = "程序通事";
FileOutputStream fos = new FileOutputStream("test.payload");
ObjectOutputStream os = new ObjectOutputStream(fos);
//writeObject()方法将Unsafe对象写入object文件
os.writeObject(app);
os.close();
//从文件中反序列化obj对象
FileInputStream fis = new FileInputStream("test.payload");
ObjectInputStream ois = new ObjectInputStream(fis);
//恢复对象
App objectFromDisk = (App)ois.readObject();
System.out.println("main name is "+objectFromDisk.name);
ois.close();
}
执行结果:
readObject name is 程序通事
main name is 程序通事
并且成功打开了计算器程序。
当我们调用 ObjectInputStream#readObject
读取反序列化的数据,如果对象内实现了 readObject
方法,这个方法将会被调用。
源码如下:
反序列化漏洞执行条件
上面的例子中,我们在 readObject
方法内主动使用Runtime
执行外部命令。但是正常的情况下,我们肯定不会在 readObject
写上述代码,除非是内鬼 ̄□ ̄||
如果可以找到一个对象,他的readObject
方法可以执行任意代码,那么在反序列过程也会执行对应的代码。我们只要将满足上述条件的对象序列化之后发送给先相应 Java 程序,Java 程序读取之后,进行反序列化,就会执行指定的代码。
为了使反序列化漏洞成功执行需要满足以下条件:
- Java 反序列化应用中需要存在序列化使用的类,不然反序列化时将会抛出
ClassNotFoundException
异常。 - Java 反序列化对象的
readObject
方法可以执行任何代码,没有任何验证或者限制。
引用一段网上的反序列化攻击流程,来源:https://xz.aliyun.com/t/7031
- 客户端构造payload(有效