1. Overview
1.概述
In this tutorial, we’ll explore how an attacker can use deserialization in Java code to exploit a system.
在本教程中,我们将探讨攻击者如何利用Java代码中的反序列化来利用系统。
We’ll start by looking at some different approaches an attacker might use to exploit a system. Then, we will look at the implications of a successful attack. Finally, we will look at some best practices to help avoid these types of attacks.
我们将首先看一下攻击者可能使用的一些不同方法来利用一个系统。然后,我们将看看一个成功的攻击所带来的影响。最后,我们将看看一些最佳做法,以帮助避免这些类型的攻击。
2. Deserialization Vulnerabilities
2.反序列化漏洞
Java uses deserialization widely to create objects from input sources.
Java广泛使用反序列化来从输入源创建对象。
These input sources are byte-streams and come in a variety of formats (some standard forms include JSON and XML). Legitimate system functionality or communication with trusted sources across networks use deserialization. However, untrusted or malicious byte-streams can exploit vulnerable deserialization code.
这些输入源是字节流,有各种不同的格式(一些标准形式包括JSON和XML)。L合法的系统功能或与网络中受信任的来源的通信都使用反序列化。然而,不受信任的或恶意的字节流可以利用脆弱的反序列化代码。
Our previous article on Java Serialization covers how serialization and deserialization work in greater depth.
我们之前关于Java 序列化的文章更深入地介绍了序列化和反序列化的工作方法。
2.1. Attack Vector
2.1.攻击矢量
Let’s discuss how an attacker might use deserialization to exploit a system.
让我们来讨论一下攻击者如何使用反序列化来利用一个系统。
For a class to be serializable, it must conform to the Serializable interface. Classes that implement Serializable use the methods readObject and writeObject. These methods deserialize and serialize object instances of the class, respectively.
要使一个类可被序列化,它必须符合可序列化接口。实现Serializable的类使用readObject和writeObject.方法,这些方法分别对该类的对象实例进行反序列化和序列化。
A typical implementation of this may look like this:
这方面的典型实现可能是这样的。
public class Thing implements Serializable {
private static final long serialVersionUID = 0L;
// Class fields
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// Custom attribute setting
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// Custom attribute getting
}
}
Classes become vulnerable when they have generic or loosely defined fields and use reflection to set attributes on these fields:
当类拥有通用或松散定义的字段,并使用反射来设置这些字段的属性时,就会变得很脆弱。
public class BadThing implements Serializable {
private static final long serialVersionUID = 0L;
Object looselyDefinedThing;
String methodName;
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
try {
Method method = looselyDefinedThing.getClass().getMethod(methodName);
method.invoke(looselyDefinedThing);
} catch (Exception e) {
// handle error...
}
}
// ...
}
Let’s break down the above to see what is happening.
让我们对上述情况进行分解,看看发生了什么。
Firstly, our class BadThing has a field looselyDefinedThing which is of type Object. This is vague and allows for an attacker to make this field any type that is available on the classpath.
首先,我们的类BadThing有一个字段looselyDefinedThing,它的类型是Object。 这是模糊的,允许攻击者使这个字段成为classpath上的任何类型。
Next, what makes this class vulnerable is that the readObject method contains custom code that invokes a method on looselyDefinedThing. The method we want to invoke uses the field methodName (which can also be controlled by the attacker) via reflection.
接下来,使这个类变得脆弱的是,readObject方法包含了调用looselyDefinedThing上一个方法的自定义代码。我们想要调用的方法通过反射使用字段methodName(也可以由攻击者控制)。。
The above code is equivalent to the following in execution if the class MyCustomAttackObject is on the system’s classpath:
如果MyCustomAttackObject类在系统的classpath上,则上述代码在执行时等同于以下内容。
BadThing badThing = new BadThing();
badThing.looselyDefinedThing = new MyCustomAttackObject();
badThing.methodName = "methodThatTriggersAttack";
Method method = looselyDefinedThing.getClass().getMethod(methodName);
method.invoke(methodName);
public class MyCustomAttackObject implements Serializable {
public static void methodThatTriggersAttack() {
try {
Runtime.getRuntime().exec("echo \"Oh, no! I've been hacked\"");
} catch (IOException e) {
// handle error...
}
}
}
By using the MyCustomAttackObject class, the attacker has been able to execute a command on the host machine.
通过使用MyCustomAttackObject类,攻击者已经能够在主机上执行一个命令。
This particular command is harmless. However, if this method were able to take custom commands, the possibilities of what an attacker can achieve are limitless.
这个特定的命令是无害的。然而,如果这种方法能够采取自定义命令,那么攻击者能够实现的可能性是无限的。
The question that still stands is, “why would anyone have such a class on their classpath in the first place?”.
仍然存在的问题是,”为什么有人首先在他们的classpath上有这样一个类?”。
Classes that allow an attacker to execute malicious code exist widely throughout open source and third-party libraries that are used by many frameworks and software. They are not often as simple as the above example but involve using multiple classes and reflection to be able to execute commands of similar ilk.
允许攻击者执行恶意代码的类广泛存在于许多框架和软件所使用的开源和第三方库中。它们通常不像上面的例子那样简单,而是涉及使用多个类和反射,以便能够执行类似的命令。
Using multiple classes in this way is often referred to as a gadget chain. The open-source tool ysoserial maintains an active list of gadget chains that can be used in an attack.
以这种方式使用多个类,通常被称为小工具链。开源工具ysoserial维护着一个可用于攻击的小工具链的有效列表。
2.2. Implications
2.2.影响
Now that we know how an attacker might gain access to remote command execution let’s discuss some of the implications of what an attacker may be able to achieve on our system.
现在我们知道了攻击者如何获得远程命令执行的权限,让我们讨论一下攻击者可能在我们的系统上实现的一些影响。
Depending on the level of access that the user running the JVM has, the attacker may already have heightened privileges on the machine, which would allow them to access most files across the system and steal information.
根据运行JVM的用户所拥有的访问级别,攻击者可能已经在机器上拥有较高的权限,这将使他们能够访问整个系统的大多数文件并窃取信息。
Some deserialization exploits allow an attacker to execute custom Java code that could lead to denial of service attacks, stealing of user session or unauthorized access to resources.
一些反序列化的漏洞允许攻击者执行自定义的Java代码,这可能导致拒绝服务攻击、窃取用户会话或未经授权访问资源。
As each deserialization vulnerability is different and each system set up is different, what an attacker can achieve varies widely. For this Reason, vulnerability databases consider deserialization vulnerabilities high risk.
由于每个反序列化漏洞都是不同的,而且每个系统的设置也是不同的,因此攻击者能够实现的目标也大不相同。出于这个原因,漏洞数据库认为反序列化漏洞的风险很高。
3. Best Practices for Prevention
3.预防的最佳做法
Now that we have covered how our system might be exploited, we’ll touch on some best practices that can be followed to help prevent this type of attack and limit the scope of potential exploits.
现在我们已经涵盖了我们的系统可能被利用的方式,我们将谈谈可以遵循的一些最佳做法,以帮助防止这种类型的攻击,并限制潜在的利用范围。
Note, there is no silver bullet in exploit prevention, and this section is not an exhaustive list of all preventative measures:
注意,在预防漏洞方面没有银弹,本节并非所有预防措施的详尽清单:。
- We should keep open source libraries up to date. Prioritize updating to the latest version of libraries when available.
- Actively check vulnerability databases such as the National Vulnerability Database, or CVE Mitre (to name a few) for newly declared vulnerabilities and make sure that we are not exposed
- Verify the source of input byte-stream for deserialization (use secure connections and verify the user, etc.)
- If the input has come from a user input field, make sure to validate these fields and authorize the user before deserializing
- Follow the owasp cheatsheet for deserialization when creating custom deserialization code
- Limit what the JVM can access on the host machine to reduce the scope of what an attacker can do if they are able to exploit our system
4. Conclusion
4.总结
In this article, we’ve covered how an attacker may use deserialization to exploit a vulnerable system. In addition, we have covered some practices to maintain good security hygiene in a Java system.
在这篇文章中,我们已经介绍了攻击者如何利用反序列化来利用一个脆弱的系统。此外,我们还介绍了一些在Java系统中保持良好安全卫生的做法。
As always, the source code is available over on GitHub.
像往常一样,源代码可在GitHub上获得。