返回介绍

Mechanisms

发布于 2024-10-11 20:34:01 字数 43790 浏览 0 评论 0 收藏 0

Serialization is the process by which some bit of data in a programming language gets converted into a format that allows it to be saved in a database or transferred over a network. Deserialization refers to the opposite process, whereby the program reads the serialized object from a file or the network and converts it back into an object.

序列化是将编程语言中的某些数据转换为一种格式的过程,使其可以保存在数据库中或通过网络传输。反序列化指的是相反的过程,即程序从文件或网络中读取序列化对象并将其转换回对象。

This is useful because some objects in programming languages are difficult to transfer through a network or to store in a database without corruption. Serialization and deserialization allow programming languages to reconstruct identical program objects in different computing environments. Many programming languages support the serialization and deserialization of objects, including Java, PHP, Python, and Ruby.

这是有用的,因为一些编程语言中的对象很难在网络中传输或存储在数据库中而不破坏。序列化和反序列化允许编程语言在不同的计算环境中重构相同的程序对象。许多编程语言支持对象的序列化和反序列化,包括 Java,PHP,Python 和 Ruby。

Developers often trust user-supplied serialized data because it is difficult to read or unreadable to users. This trust assumption is what attackers can abuse. Insecure deserialization is a type of vulnerability that arises when an attacker can manipulate the serialized object to cause unintended consequences in the program. This can lead to authentication bypasses or even RCE. For example, if an application takes a serialized object from the user and uses the data contained in it to determine who is logged in, a malicious user might be able to tamper with that object and authenticate as someone else. If the application uses an unsafe deserialization operation, the malicious user might even be able to embed code snippets in the object and get it executed during deserialization.

开发者经常信任用户提供的序列化数据,因为对于用户来说很难读取或者不可读。然而这种信任假设恰恰是攻击者可以利用的弱点。不安全的反序列化就是一种类型的漏洞,攻击者可以通过操纵序列化对象来在程序中引起意料之外的后果。这可能导致绕过认证甚至 RCE。例如,如果一个应用程序接受用户提供的序列化对象并使用其中的数据来确定谁已登录,那么恶意用户可能能够篡改该对象并作为其他人进行认证。如果应用程序使用不安全的反序列化操作,那么恶意用户甚至可能能够在对象中嵌入代码片段并在反序列化期间执行它。

The best way to understand insecure deserialization is to learn how different programming languages implement serialization and deserialization. Since these processes look different in every language, we’ll explore how this vulnerability presents itself in PHP and Java. Before we continue, you’ll need to install PHP and Java if you want to test out the example code in this chapter.

理解不安全反序列化的最佳方法是学习不同编程语言如何实现序列化和反序列化。由于这些过程在每种语言中看起来不同,因此我们将探讨此漏洞在 PHP 和 Java 中的表现方式。在继续之前,如果您想测试本章节中的示例代码,您需要安装 PHP 和 Java。

You can install PHP by following the instructions for your system on the PHP manual page ( https://www.php.net/manual/en/install.php ). You can then run PHP scripts by running php YOUR_PHP_SCRIPT.php using the command line. Alternatively, you can use an online PHP tester like ExtendsClass ( https://extendsclass.com/php.html ) to test the example scripts. Search online PHP tester for more options. Note that not all online PHP testers support serialization and deserialization, so make sure to choose one that does.

您可以按照 PHP 手册页面上针对您所用系统的说明进行安装(https://www.php.net/manual/zh/install.php)。您随后可以使用命令行通过运行“php YOUR_PHP_SCRIPT.php”来运行 PHP 脚本。或者,您可以使用在线的 PHP 测试器(例如 ExtendsClass(https://extendsclass.com/php.html))来测试示例脚本。搜索在线 PHP 测试器以了解更多选项。请注意,并非所有在线 PHP 测试器都支持序列化和反序列化,请确保选择支持此功能的测试器。

Most computers should already have Java installed. If you run java -version at the command line and see a Java version number returned, you don’t have to install Java again. Otherwise, you can find the instructions to install Java at https://java.com/en/download/help/download_options.html . You can also use an online Java compiler to test your code; Tutorials Point has one at https://www.tutorialspoint.com/compile_java_online.php .

大多数计算机应该已经安装了 Java。如果您在命令行上运行 java -version 并看到返回的 Java 版本号,则无需再次安装 Java。否则,您可以在 https://java.com/en/download/help/download_options.html 找到安装 Java 的说明。您也可以使用在线 Java 编译器测试您的代码;Tutorials Point 在 https://www.tutorialspoint.com/compile_java_online.php 上有一个。

PHP

Although most deserialization bugs in the wild are caused by insecure deserialization in Java, I’ve also found PHP deserialization vulnerabilities to be extremely common. In my research project that studied publicly disclosed deserialization vulnerabilities on HackerOne, I discovered that half of all disclosed deserialization vulnerabilities were caused by insecure deserialization in PHP. I also found that most deserialization vulnerabilities are resolved as high-impact or critical-impact vulnerabilities; incredibly, most can be used to cause the execution of arbitrary code on the target server.

尽管在实际环境中,大多数反序列化错误是由 Java 中的不安全反序列化引起的,我发现 PHP 反序列化漏洞也非常普遍。在我研究公开披露的 HackerOne 反序列化漏洞的研究项目中,我发现一半以上的披露的反序列化漏洞是由 PHP 不安全反序列化引起的。我还发现,大多数反序列化漏洞都被解决为高影响力或危急影响力漏洞;令人惊讶的是,这些漏洞大多可用于在目标服务器上执行任意代码。

When insecure deserialization vulnerabilities occur in PHP, we sometimes call them PHP object injection vulnerabilities . To understand PHP object injections, you first need to understand how PHP serializes and deserializes objects.

当 PHP 发生不安全的反序列化漏洞时,我们有时称之为 PHP 对象注入漏洞。要理解 PHP 对象注入,首先需要了解 PHP 如何对对象进行序列化和反序列化。

When an application needs to store a PHP object or transfer it over the network, it calls the PHP function serialize() to pack it up. When the application needs to use that data, it calls unserialize() to unpack and get the underlying object.

当应用程序需要存储一个 PHP 对象或在网络上传输它时,它会调用 PHP 函数 serialize() 来打包它。当应用程序需要使用该数据时,调用 unserialize() 来解包并获取基础对象。

For example, this code snippet will serialize the object called user :

例如,这段代码片段将序列化名为用户(user)的对象:

<?php
1 class User{
    public $username;
    public $status;
  }
2 $user = new User;
3 $user->username = 'vickie';
4 $user->status = 'not admin';
5 echo serialize($user);
?>

This piece of PHP code declares a class called User . Each User object will contain a $username and a $status attribute 1 . It then creates a new User object called $user 2 . It sets the $username attribute of $user to 'vickie' 3 and its $status attribute to 'not admin' 4 . Then, it serializes the $user object and prints out the string representing the serialized object 5 .

这段 PHP 代码声明了一个名为 User 的类。每个 User 对象都包含一个 $username 和一个 $status 属性 1。接着,它创建了一个名为 $user 的新的 User 对象 2。它将 $user 的 $username 属性设置为 'vickie' 3,并将其 $status 属性设置为 'not admin' 4。然后,将 $user 对象序列化并打印代表序列化对象的字符串 5。

Store this code snippet as a file named serialize_test.php and run it using the command php serialize_test.php . You should get the serialized string that represents the user object:

将此代码片段保存为名为 serialize_test.php 的文件,并使用命令 php serialize_test.php 运行它。您应该会得到表示用户对象的序列化字符串:

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}

Let’s break down this serialized string. The basic structure of a PHP serialized string is data type : data . In terms of data types, b represents a Boolean, i represents an integer, d represents a float, s represents a string, a represents an array, and O represents an object instance of a particular class. Some of these types get followed by additional information about the data, as described here:

让我们分解这个序列化字符串。PHP 序列化字符串的基本结构是数据类型:data。在数据类型方面,b 表示布尔值,i 表示整数,d 表示浮点数,s 表示字符串,a 表示数组,O 表示特定类的对象实例。其中一些类型后面跟随有关数据的其他信息,如下所述:

b:THE_BOOLEAN;
i:THE_INTEGER;
d:THE_FLOAT;
s:LENGTH_OF_STRING:"ACTUAL_STRING";
a:NUMBER_OF_ELEMENTS:{ELEMENTS}
O:LENGTH_OF_NAME:"CLASS_NAME":NUMBER_OF_PROPERTIES:{PROPERTIES}

Using this reference as a guide, we can see that our serialized string represents an object of the class User . It has two properties. The first property has the name username and the value vickie . The second property has the name status and the value not admin . The names and values are all strings.

使用此参考作为指南,我们可以看到我们的序列化字符串表示一个 User 类的对象。它有两个属性。第一个属性名称为 username,值为 vickie。第二个属性名称为 status,值为 not admin。名称和值都是字符串。

When you’re ready to operate on the object again, you can deserialize the string with unserialize() :

当您再次准备对该对象进行操作时,您可以使用 unserialize() 将字符串反序列化:

<?php
1 class User{
    public $username;
    public $status;
  }
  $user = new User;
  $user->username = 'vickie';
  $user->status = 'not admin';
  $serialized_string = serialize($user);

2 $unserialized_data = unserialize($serialized_string);
3 var_dump($unserialized_data);
  var_dump($unserialized_data["status"]);
?>

The first few lines of this code snippet create a user object, serialize it, and store the serialized string into a variable called $serialized_string 1 . Then, it unserializes the string and stores the restored object into the variable $unserialized_data 2 . The var_dump() PHP function displays the value of a variable. The last two lines display the value of the unserialized object $unserialized_data and its status property 3 .

这个代码片段的前几行创建了一个用户对象,并对其进行序列化,将序列化后的字符串存储在变量$serialized_string 中 1。然后,它对字符串进行反序列化,并将恢复的对象存储在变量$unserialized_data 中 2。var_dump()PHP 函数显示变量的值。最后两行显示未序列化对象$unserialized_data 及其状态属性的值 3。

Most object-oriented programming languages have similar interfaces for serializing and deserializing program objects, but the format of their serialized objects are different. Some programming languages also allow developers to serialize into other standardized formats, such as JSON and YAML.

大多数面向对象编程语言具有类似的接口,用于序列化和反序列化程序对象,但其序列化对象的格式是不同的。一些编程语言还允许开发人员将其序列化为其他标准格式,比如 JSON 和 YAML。

Controlling Variable Values

You might have already noticed something fishy here. If the serialized object isn’t encrypted or signed, can anyone create a User object? The answer is yes! This is a common way insecure deserialization endangers applications.

如果序列化对象没有加密或签名,任何人都可以创建一个用户对象,这是一个不安全的反序列化危害应用程序的常见方式。

One possible way of exploiting a PHP object injection vulnerability is by manipulating variables in the object. Some applications simply pass in a serialized object as a method of authentication without encrypting or signing it, thinking the serialization alone will stop users from tampering with the values. If that’s the case, you can mess with the values encoded in the serialized string:

利用 PHP 对象注入漏洞的一种可能方法是通过操纵对象中的变量。有些应用程序为了验证身份,仅仅传递一个序列化的对象作为认证方法,而没有加密或签名它,认为仅使用序列化就足以防止用户篡改值。如果是这种情况,你可以操纵编码在序列化字符串中的值:

<?php
  class User{
    public $username;
    public $status;
  }
  $user = new User;
  $user->username = 'vickie';
1 $user->status = 'admin';
  echo serialize($user);
?>

In this example of the User object we created earlier, you change the status to admin by modifying your PHP script 1 . Then you can intercept the outgoing request in your proxy and insert the new object in place of the old one to see if the application grants you admin privileges.

在之前我们创建的用户对象的示例中,您可以通过修改 PHP 脚本 1 中的状态来将其更改为管理者。然后,您可以拦截代理中的输出请求,并插入新对象替换旧对象,以查看应用程序是否授予您管理员权限。

You can also change your serialized string directly:

您也可以直接更改您的序列化字符串:

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"    admin ";}

If you’re tampering with the serialized string directly, remember to change the string’s length marker as well, since the length of your status string has changed:

如果直接更改序列化的字符串,请记得同时修改字符串的长度标记,因为你的状态字符串的长度已经发生了改变:

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";    s:5 :"admin";}

unserialize() Under the Hood

To understand how unserialize() can lead to RCEs, let’s take a look at how PHP creates and destroys objects.

了解 unserialize() 如何导致 RCE,让我们来看看 PHP 如何创建和销毁对象。

PHP magic methods are method names in PHP that have special properties. If the serialized object’s class implements any method with a magic name, these methods will have magic properties, such as being automatically run during certain points of execution, or when certain conditions are met. Two of these magic methods are __wakeup() and __destruct() .

PHP 魔法方法是 PHP 中具有特殊属性的方法名。如果序列化对象的类实现了任何具有魔法名称的方法,则这些方法将具有魔法属性,例如在执行的某些点自动运行,或者在满足某些条件时自动运行。其中两个魔法方法是 __wakeup() 和 __destruct()。

The __wakeup() method is used during instantiation when the program creates an instance of a class in memory, which is what unserialize() does; it takes the serialized string, which specifies the class and the properties of that object, and uses that data to create a copy of the originally serialized object. It then searches for the __wakeup() method and executes code in it. The __wakeup() method is usually used to reconstruct any resources that the object may have, reestablish any database connections that were lost during serialization, and perform other reinitialization tasks. It’s often useful during a PHP object injection attack because it provides a convenient entry point to the server’s database or other functions in the program.

__wakeup() 方法在实例化期间使用,当程序在内存中创建一个类的实例时使用,这就是 unserialize() 的作用;它接受序列化字符串,该字符串指定该对象的类和属性,并使用该数据创建原始序列化对象的副本。然后搜索__wakeup() 方法并执行其中的代码。__wakeup() 方法通常用于重建对象可能具有的任何资源,重新建立在序列化期间丢失的任何数据库连接,并执行其他重新初始化任务。它通常在 PHP 对象注入攻击中非常有用,因为它提供了方便的入口点到服务器的数据库或程序中的其他功能。

The program then operates on the object and uses it to perform other actions. When no references to the deserialized object exist, the program calls the __destruct() function to clean up the object. This method often contains useful code in terms of exploitation. For example, if a __destruct() method contains code that deletes and cleans up files associated with the object, the attacker might be able to mess with the integrity of the filesystem by controlling the input passed into those functions.

程序然后操作该对象并使用它执行其他动作。当没有对反序列化对象的引用时,程序调用__destruct() 函数清理对象。该方法通常包含有关利用的有用代码。例如,如果__destruct() 方法包含删除和清理与对象相关的文件的代码,攻击者可能能够通过控制传递给这些函数的输入来干扰文件系统的完整性。

Achieving RCE

When you control a serialized object passed into unserialize() , you control the properties of the created object. You might also be able to control the values passed into automatically executed methods like __wakeup() or __destruct() . If you can do that, you can potentially achieve RCE.

当您控制一个传入 unserialize() 的序列化对象时,您就可以控制创建的对象的属性。您还可能能够控制传入自动执行方法(如__wakeup() 或__destruct())的值。如果您能够做到这一点,您有可能实现远程代码执行(RCE)。

For example, consider this vulnerable code example, taken from https://www.owasp.org/index.php/PHP_Object_Injection :

例如,考虑这个脆弱的代码示例,从 https://www.owasp.org/index.php/PHP_Object_Injection 中取得:

1 class Example2
  {
    private $hook;
    function __construct(){
        // some PHP code...
    }
    function __wakeup(){
      2 if (isset($this->hook)) eval($this->hook);
    }
  }

  // some PHP code...

3 $user_data = unserialize($_COOKIE['data']);

The code declares a class called Example2 . It has a $hook attribute and two methods: __construct() and __wakeup() 1 . The __wakeup() function executes the string stored in $hook as PHP code if $hook is not empty 2 . The PHP eval() function takes in a string and runs the content of the string as PHP code. Then, the program runs unserialize() on a user-supplied cookie named data 3 .

该代码声明了一个名为 Example2 的类。它有一个$hook 属性和两个方法:__construct() 和__wakeup()。1.如果$hook 非空,__wakeup() 函数会执行存储在$hook 中的字符串作为 PHP 代码。2.PHP eval() 函数接受一个字符串,并将字符串的内容作为 PHP 代码运行。然后,该程序在名为 data 的用户提供的 cookie 上运行 unserialize()。

Here, you can achieve RCE because the code passes a user-provided object into unserialize() , and there is an object class, Example2 , with a magic method that automatically runs eval() on user-provided input when the object is instantiated.

在这里,您可以实现 RCE,因为代码将用户提供的对象传递给 unserialize(),并且有一个名为 Example2 的对象类,其中有一个魔术方法,在实例化对象时自动针对用户提供的输入运行 eval()。

To exploit this RCE, you’d set your data cookie to a serialized Example2 object, and the hook property to whatever PHP code you want to execute. You can generate the serialized object by using the following code snippet:

要利用这个 RCE,您需要将数据 cookie 设置为序列化的 Example2 对象,将 hook 属性设置为您想要执行的任何 PHP 代码。您可以使用以下代码片段生成序列化对象:

class Example2
{
   private $hook = "phpinfo();";
}
print 1 urlencode(serialize(new Example2));

Before we print the object, we need to URL-encode it 1 , since we’ll be injecting the object via a cookie. Passing the string generated by this code into the data cookie will cause the server to execute the PHP code phpinfo(); , which outputs information about PHP’s configuration on the server. The phpinfo() function is often used as a proof-of-concept function to run in bug reports to proof successful PHP command injection. The following is what happens in detail on the target server during this attack:

在打印对象之前,我们需要对其进行 URL 编码 1,因为我们将通过 cookie 注入该对象。将由此代码生成的字符串传递到数据 cookie 中将导致服务器执行 PHP 代码 phpinfo();,它会输出有关服务器上 PHP 配置的信息。phpinfo() 函数通常用作概念证明函数来运行 bug 报告以证明成功的 PHP 命令注入。以下是在目标服务器上此攻击发生的详细过程:

  1. The serialized Example2 object is passed into the program as the data cookie.
  2. The program calls unserialize() on the data cookie.
  3. Because the data cookie is a serialized Example2 object, unserialize() instantiates a new Example2 object.
  4. The unserialize() function sees that the Example2 class has __wakeup() implemented, so __wakeup() is called.
  5. The __wakeup() function looks for the object’s $hook property, and if it is not NULL , it runs eval($hook) .
  6. The $hook property is not NULL , because it is set to phpinfo(); , and so eval("phpinfo();") is run.
  7. You’ve achieved RCE by executing the arbitrary PHP code you’ve placed in the data cookie.

Using Other Magic Methods

So far, we’ve mentioned the magic methods __wakeup() and __destruct() . There are actually four magic methods you’ll find particularly useful when trying to exploit an unserialize() vulnerability: __wakeup() , __destruct() , __toString() , and __call() .

到目前为止,我们已经提到了魔术方法__wakeup() 和__destruct()。当尝试利用 unserialize() 漏洞时,实际上有四个魔术方法会特别有用:__wakeup(),__destruct(),__toString() 和__call()。

Unlike __wakeup() and __destruct() , which always get executed if the object is created, the __toString() method is invoked only when the object is treated as a string. It allows a class to decide how it will react when one of its objects is treated as a string. For example, it can decide what to display if the object is passed into an echo() or print() function. You’ll see an example of using this method in a deserialization attack in “Using POP Chains” on page 238 .

与__wakeup() 和 __destruct() 不同的是,它们无论对象是否被创建都会执行,__toString() 方法仅在对象被视为字符串时调用。它允许类在对象被视为字符串时决定如何反应。例如,它可以决定如果对象被传递到 echo() 或 print() 函数中时要显示什么内容。您将在第 238 页的“使用 POP 链”中看到使用此方法的示例。

A program invokes the __call() method when an undefined method is called. For example, a call to $object->undefined($args) will turn into $object->__call('undefined', $args) . Again, the exploitability of this magic method varies wildly, depending on how it was implemented. Sometimes attackers can exploit this magic method when the application’s code contains a mistake or when users are allowed to define a method name to call themselves.

当调用未定义的方法时,程序会调用__call() 方法。例如,对$object->undefined($args) 的调用将变为$object->__call('undefined', $args)。再次说明,这个魔术方法的可利用性因其实现方式而大不相同。有时候攻击者可以利用这个魔法方法,当应用程序的代码存在错误或当用户允许定义方法名称调用自己时。

You’ll typically find these four magic methods the most useful for exploitation, but many other methods exist. If the ones mentioned here aren’t exploitable, it might be worth checking out the class’s implementation of the other magic methods to see whether you can start an exploit from there. Read more about PHP’s magic methods at https://www.php.net/manual/en/language.oop5.magic.php .

通常情况下,对于利用这四个魔法方法最为有用。但是还存在许多其他的方法。如果在这里提到的方法不可利用,那么检查类的其他魔法方法实现是否可以从那里开始利用,可能会有所帮助。请参阅 https://www.php.net/manual/en/language.oop5.magic.php 了解更多关于 PHP 魔法方法的信息。

Using POP Chains

So far, you know that when attackers control a serialized object passed into unserialize() , they can control the properties of the created object. This gives them the opportunity to hijack the flow of the application by choosing the values passed into magic methods like __wakeup() .

到目前为止,您已经知道当攻击者控制传递给 unserialize()的序列化对象时,他们可以控制创建的对象的属性。这使他们有机会通过选择传递给像__wakeup()这样的魔术方法的值来劫持应用程序的流程。

This exploit works . . . sometimes. But this approach has a problem: what if the declared magic methods of the class don’t contain any useful code in terms of exploitation? For example, sometimes the available classes for object injections contain only a few methods, and none of them contain code injection opportunities. Then the unsafe deserialization is useless, and the exploit is a bust, right?

这个漏洞有时候有效。但这种方法存在一个问题:如果类的声明魔术方法在利用方面没有任何有用的代码怎么办?例如,有时可用于对象注入的类仅包含几个方法,而且它们都没有代码注入机会。那么,不安全的反序列化就无用了,漏洞就会失败,对吧?

We have another way of achieving RCE even in this scenario: POP chains. A property-oriented programming (POP) chain is a type of exploit whose name comes from the fact that the attacker controls all of the deserialized object’s properties. POP chains work by stringing bits of code together, called gadgets , to achieve the attacker’s ultimate goal. These gadgets are code snippets borrowed from the codebase. POP chains use magic methods as their initial gadget. Attackers can then use these methods to call other gadgets.

我们在这种情况下有另一种实现 RCE 的方式:POP 链。属性导向编程(POP)链是一种攻击利用方式,其名称源于攻击者控制了所有反序列化对象的属性。POP 链通过将被称为小工具的代码片段串联在一起来实现攻击者的最终目标。这些小工具是从代码库中借用的代码片段。POP 链使用魔术方法作为它们的初始小工具。攻击者可以使用这些方法来调用其他小工具。

If this seems abstract, consider the following example application code, taken from https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection :

如果这个看上去抽象的话,不妨看一下以下的应用代码示例,取自 https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection:。

class Example
{
1 private $obj;
  function __construct()
  {
    // some PHP code...
  }
  function __wakeup()
  {
  2 if (isset($this->obj)) return $this->obj->evaluate();
  }
}

class CodeSnippet
{
3 private $code;
   
4 function evaluate()
  {
    eval($this->code);
  }
}

// some PHP code...

5 $user_data = unserialize($_POST['data']);

// some PHP code...

In this application, the code defines two classes: Example and CodeSnippet . Example has a property named obj 1 , and when an Example object is deserialized, its __wakeup() function is called, which calls obj ’s evaluate() method 2 .

这个应用程序定义了两个类:Example 和 CodeSnippet。Example 有一个名为“obj 1”的属性,当反序列化 Example 对象时,它的__wakeup() 函数被调用,该函数调用 obj 的 evaluate() 方法。

The CodeSnippet class has a property named code that contains the code string to be executed 3 and an evaluate() method 4 , which calls eval() on the code string.

CodeSnippet 类具有名为 code 的属性,其中包含要执行的代码字符串,并具有一个 evaluate()方法,该方法在代码字符串上调用 eval()。

In another part of the code, the program accepts the POST parameter data from the user and calls unserialize() on it 5 .

在代码的另一部分,程序接受用户的 POST 参数数据,并在其上调用 unserialize() 5。

Since that last line contains an insecure deserialization vulnerability, an attacker can use the following code to generate a serialized object:

由于最后一行包含不安全的反序列化漏洞,攻击者可以使用以下代码生成序列化对象:

class CodeSnippet
{
  private $code = "phpinfo();";
}
class Example
{
  private $obj;
  function __construct()
  {
    $this->obj = new CodeSnippet;
  }
}
print urlencode(serialize(new Example));

This code snippet defines a class named CodeSnippet and set its code property to phpinfo(); . Then it defines a class named Example , and sets its obj property to a new CodeSnippet instance on instantiation. Finally, it creates an Example instance, serializes it, and URL-encodes the serialized string. The attacker can then feed the generated string into the POST parameter data .

该代码片段定义了一个名为 CodeSnippet 的类,并将其代码属性设置为 phpinfo(); 。然后它定义了一个名为 Example 的类,并在实例化时将其 obj 属性设置为新的 CodeSnippet 实例。最后,它创建一个 Example 实例,对其进行序列化,然后对序列化的字符串进行 URL 编码。攻击者可以将生成的字符串输入 POST 参数数据中。

Notice that the attacker’s serialized object uses class and property names found elsewhere in the application’s source code. As a result, the program will do the following when it receives the crafted data string.

请注意攻击者的序列化对象使用了应用程序源代码中其他地方发现的类和属性名称。因此,当程序接收到制作的数据字符串时,它将执行以下操作。

First, it will unserialize the object and create an Example instance. Then, since Example implements __wakeup() , the program will call __wakeup() and see that the obj property is set to a CodeSnippet instance. Finally, it will call the evaluate() method of the obj , which runs eval("phpinfo();") , since the attacker set the code property to phpinfo() . The attacker is able to execute any PHP code of their choosing.

首先,它会反序列化对象并创建一个 Example 实例。然后,由于 Example 实现了 __wakeup(),程序将调用 __wakeup(),并看到 obj 属性被设置为 CodeSnippet 实例。最后,它将调用 obj 的 evaluate() 方法,运行 eval("phpinfo();"),因为攻击者将 code 属性设置为 phpinfo()。攻击者能够执行任何所选择的 PHP 代码。

POP chains achieve RCE by chaining and reusing code found in the application’s codebase. Let’s look at another example of how to use POP chains to achieve SQL injection. This example is also taken from https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection .

POP 链通过链接和重用应用程序代码库中发现的代码实现 RCE。让我们看另一个使用 POP 链实现 SQL 注入的例子。这个例子也来自 https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection。

Say an application defines a class called Example3 somewhere in the code and deserializes unsanitized user input from the POST parameter data :

应用程序在代码的某处定义了一个名为 Example3 的类,并从 POST 参数数据反序列化未经过滤的用户输入。

class Example3
{
  protected $obj;
  function __construct()
  {
    // some PHP code...
  }
1 function __toString()
  {
    if (isset($this->obj)) return $this->obj->getValue();
  }
}

// some PHP code...

$user_data = unserialize($_POST['data']);

// some PHP code...

Notice that Example3 implements the __toString() magic method 1 . In this case, when an Example3 instance is treated as a string, it will return the result of the getValue() method run on its $obj property.

请注意,示例 3 实现了__toString() 魔术方法 1。在这种情况下,当一个 Example3 实例被视为字符串时,它将返回在其$obj 属性上运行的 getValue() 方法的结果。

Let’s also say that, somewhere in the application, the code defines the class SQL_Row_Value . It has a method named getValue() , which executes a SQL query. The SQL query takes input from the $_table property of the SQL_Row_Value instance:

假设在应用程序的某个地方,代码定义了 SQL_Row_Value 类。它有一个名为 getValue() 的方法,用于执行 SQL 查询。该 SQL 查询从 SQL_Row_Value 实例的$ _table 属性中获取输入:

class SQL_Row_Value
{
  private $_table;
  // some PHP code...
  function getValue($id)
  {
    $sql = "SELECT * FROM {$this->_table} WHERE id = " . (int)$id;
    $result = mysql_query($sql, $DBFactory::getConnection());
    $row = mysql_fetch_assoc($result);
return $row['value'];
  }
}

An attacker can achieve SQL injection by controlling the $obj in Example3 . The following code will create an Example3 instance with $obj set to a SQL_Row_Value instance, and with $_table set to the string "SQL Injection" :

攻击者可以通过控制 Example3 中的$obj 实现 SQL 注入。以下代码将创建一个 Example3 实例,其$obj 设置为 SQL_Row_Value 实例,并将$_table 设置为字符串“SQL 注入”:

class SQL_Row_Value
{
  private $_table = "SQL Injection";
}
class Example3
{
  protected $obj;
  function __construct()
  {
    $this->obj = new SQL_Row_Value;
  }
}
print urlencode(serialize(new Example3));

As a result, whenever the attacker’s Example3 instance is treated as a string, its $obj ’s get_Value() method will be executed. This means the SQL_Row_Value ’s get_Value() method will be executed with the $_table string set to "SQL Injection" .

因此,每当攻击者的 Example3 实例被视为字符串时,其 $obj 的 get_Value() 方法将被执行。这意味着 SQL_Row_Value 的 get_Value() 方法将被执行,并将 $_table 字符串设置为“SQL 注入”。

The attacker has achieved a limited SQL injection, since they can control the string passed into the SQL query SELECT * FROM {$this->_table} WHERE id = " . (int)$id; .

攻击者已经成功进行了有限的 SQL 注入,因为他们可以控制传递到 SQL 查询语句 `SELECT * FROM {$this->_table} WHERE id = " . (int)$id;` 中的字符串。

POP chains are similar to return-oriented programming ( ROP) attacks, an interesting technique used in binary exploitation. You can read more about it on Wikipedia, at https://en.wikipedia.org/wiki/Return-oriented_programming .

POP 链和返回导向编程(ROP)攻击类似,是二进制利用中使用的一种有趣技术。更多信息请参见维基百科网址:https://en.wikipedia.org/wiki/Return-oriented_programming。

Java

Now that you understand how insecure deserialization in PHP works, let’s explore another programming language prone to these vulnerabilities: Java. Java applications are prone to insecure deserialization vulnerabilities because many of them handle serialized objects. To understand how to exploit deserialization vulnerabilities in Java, let’s look at how serialization and deserialization work in Java.

现在你已经了解了 PHP 中不安全反序列化的工作原理,让我们来探究另一种易受这些漏洞影响的编程语言:Java。由于许多 Java 应用程序都处理序列化对象,因此 Java 应用程序容易受到不安全的反序列化漏洞的影响。要了解如何利用 Java 中的反序列化漏洞,让我们来看看 Java 中序列化和反序列化的工作原理。

For Java objects to be serializable, their classes must implement the java.io.Serializable interface. These classes also implement special methods, writeObject() and readObject() , to handle the serialization and deserialization, respectively, of objects of that class. Let’s look at an example. Store this code in a file named SerializeTest.java :

Java 对象要实现序列化,它们的类必须实现 java.io.Serializable 接口。这些类还实现了特殊方法 writeObject() 和 readObject(),用于分别处理该类对象的序列化和反序列化。让我们来看一个例子。将这个代码存储在名为 SerializeTest.java 的文件中:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.io.IOException;

1 class User implements Serializable{
2 public String username;
}

public class SerializeTest{

  public static void main(String args[]) throws Exception{

    3 User newUser = new User();
    4 newUser.username = "vickie";

      FileOutputStream fos = new FileOutputStream("object.ser");
      ObjectOutputStream os = new ObjectOutputStream(fos);
    5 os.writeObject(newUser);
      os.close();

      FileInputStream is = new FileInputStream("object.ser");
      ObjectInputStream ois = new ObjectInputStream(is);
    6 User storedUser = (User)ois.readObject();
      System.out.println(storedUser.username);
      ois.close();
    }
}

Then, in the directory where you stored the file, run these commands. They will compile the program and run the code:

在存储文件的目录中,运行以下命令。它们将编译程序并执行代码:

$ javac SerializeTest.java
$ java SerializeTest

You should see the string vickie printed as the output. Let’s break down the program a bit. First, we define a class named User that implements Serializable 1 . Only classes that implement Serializable can be serialized and deserialized. The User class has a username attribute that is used to store the user’s username 2 .

你应该看到字符串“vickie”作为输出。让我们稍微解析一下这个程序。首先,我们定义了一个名为 User 的类,它实现了 Serializable 接口 1。只有实现了 Serializable 接口的类才能被序列化和反序列化。User 类有一个用户名属性,用于存储用户的用户名 2。

Then, we create a new User object 3 and set its username to the string "vickie" 4 . We write the serialized version of newUser and store it into the file object.ser 5 . Finally, we read the object from the file, deserialize it, and print out the user’s username 6 .

然后,我们创建一个新的用户对象 3,并将其用户名设置为字符串“vickie”4。我们将 newUser 的序列化版本写入文件 object.ser 中 5。最后,我们从文件中读取对象,对其进行反序列化,并打印出用户的用户名 6。

To exploit Java applications via an insecure deserialization bug, we first have to find an entry point through which to insert the malicious serialized object. In Java applications, serializable objects are often used to transport data in HTTP headers, parameters, or cookies.

为了通过不安全的反序列化漏洞利用 Java 应用程序,我们首先必须找到一个入口点来插入恶意序列化对象。在 Java 应用程序中,可序列化对象通常用于在 HTTP 标头,参数或 cookie 中传输数据。

Java serialized objects are not human readable like PHP serialized strings. They often contain non-printable characters as well. But they do have a couple signatures that can help you recognize them and find potential entry points for your exploits:

Java 序列化的对象不像 PHP 序列化的字符串那样易于人类阅读。它们通常包含不可打印的字符。但是它们确实有一些标记可以帮助您识别它们,并找到潜在的漏洞入口:

  • Starts with AC ED 00 05 in hex or rO0 in base64. (You might see these within HTTP requests as cookies or parameters.)
  • The Content-Type header of an HTTP message is set to application/x-java-serialized-object .

Since Java serialized objects contain a lot of special characters, it’s common to encode them before transmission, so look out for differently encoded versions of these signatures as well.

由于 Java 序列化的对象包含许多特殊字符,因此在传输之前对它们进行编码是很常见的,因此也要注意这些签名的不同编码版本。

After you discover a user-supplied serialized object, the first thing you can try is to manipulate program logic by tampering with the information stored within the objects. For example, if the Java object is used as a cookie for access control, you can try changing the usernames, role names, and other identity markers that are present in the object, re-serialize it, and relay it back to the application. You can also try tampering with any sort of value in the object that is a filepath, file specifier, or control flow value to see if you can alter the program’s flow.

当你发现一个用户提供的序列化对象后,你可以尝试通过篡改对象中存储的信息来操纵程序逻辑。例如,如果 Java 对象被用作访问控制的 cookie,你可以尝试更改对象中存在的用户名、角色名和其他标识标记,重新序列化并将其转发回应用程序。你也可以尝试篡改对象中的任何类型的值,包括文件路径、文件指定符或控制流值,以查看是否可以更改程序的流程。

Sometimes when the code doesn’t restrict which classes the application is allowed to deserialize, it can deserialize any serializable classes to which it has access. This means attackers can create their own objects of any class. A potential attacker can achieve RCE by constructing objects of the right classes that can lead to arbitrary commands.

有时,当代码不限制应用程序可反序列化的类时,它可以反序列化任何具有访问权限的可序列化类。这意味着攻击者可以创建任何类的自己的对象。潜在的攻击者可以通过构造正确类的对象来实现 RCE,从而导致任意命令。

Achieving RCE

The path from a Java deserialization bug to RCE can be convoluted. To gain code execution, you often need to use a series of gadgets to reach the desired method for code execution. This works similarly to exploiting deserialization bugs using POP chains in PHP, so we won’t rehash the whole process here. In Java applications, you’ll find gadgets in the libraries loaded by the application. Using gadgets that are in the application’s scope, create a chain of method invocations that eventually leads to RCE.

从 Java 反序列化漏洞到远程代码执行的路径可能比较曲折。要想实现代码执行,通常需要使用一系列的小工具来达到执行代码的目的方法。这种方法类似于使用 POP 的 PHP 反序列化漏洞利用链,因此我们在这里不再赘述整个过程。在 Java 应用程序中,你可以在应用程序加载的库中找到小工具。使用应用程序范围内的小工具,创建一条方法调用链,最终导致远程代码执行。

Finding and chaining gadgets to formulate an exploit can be time-consuming. You’re also limited to the classes available to the application, which can restrict what your exploits can do. To save time, try creating exploit chains by using gadgets in popular libraries, such as the Apache Commons-Collections, the Spring Framework, Apache Groovy, and Apache Commons FileUpload. You’ll find many of these published online.

寻找和链接小工具以制定攻击可以非常耗时。您还受应用程序可用类的限制,这可能会限制攻击的功能。为节省时间,可以使用流行库中的小工具创建攻击链,例如 Apache Commons-Collections、Spring Framework、Apache Groovy 和 Apache Commons FileUpload。您会在网上找到很多这样的工具。

Automating the Exploitation by Using Ysoserial

Ysoserial ( https://github.com/frohoff/ysoserial/ ) is a tool that you can use to generate payloads that exploit Java insecure deserialization bugs, saving you tons of time by keeping you from having to develop gadget chains yourself.

Ysoserial 是一个工具,您可以使用它来生成利用 Java 不安全反序列化漏洞的负载,从而节省时间,无需自己开发工具链。

Ysoserial uses a collection of gadget chains discovered in common Java libraries to formulate exploit objects. With Ysoserial, you can create malicious Java serialized objects that use gadget chains from specified libraries with a single command:

Ysoserial 使用常见的 Java 库中发现的一系列 gadget 链来创建利用对象。使用 Ysoserial,只需一条命令,就可以创建使用来自指定库的 gadget 链的恶意 Java 序列化对象。

$ java -jar ysoserial.jar gadget_chain command_to_execute

For example, to create a payload that uses a gadget chain in the Commons-Collections library to open a calculator on the target host, execute this command:

例如,要创建一个使用 Commons-Collections 库中的 gadget chain 打开目标主机上的计算器的 payload,请执行以下命令:

$ java -jar ysoserial.jar CommonsCollections1 calc.exe

The gadget chains generated by Ysoserial all grant you the power to execute commands on the system. The program takes the command you specified and generates a serialized object that executes that command.

Ysoserial 生成的设备链都赋予您在系统上执行命令的能力。该程序接收您指定的命令,并生成一个序列化对象来执行该命令。

Sometimes the library to use for your gadget chain will seem obvious, but often it’s a matter of trial and error, as you’ll have to discover which vulnerable libraries your target application implements. This is where good reconnaissance will help you.

有时你的设备需要使用的库看起来很明显,但通常需要试错,因为你必须发现目标应用程序实现的脆弱库。这就是一个好的侦查工作将会帮助你的地方。

You can find more resources about exploiting Java deserialization on GitHub at https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet/ .

你可以在 GitHub 上找到更多关于 Java 反序列化的资源,访问链接 https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet/。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文