探索不安全的反序列化漏洞:PHP、Ruby 和 Java 的实战应用

发表时间: 2022-11-18 08:41


https://portswigger.net/web-security/deserialization/exploiting

利用不安全的反序列化漏洞

在本节中,我们将教您如何使用 PHP、Ruby 和 Java 反序列化中的示例来利用一些常见场景。我们希望证明利用不安全的反序列化实际上比许多人认为的要容易得多。如果您能够使用预构建的小工具链,则在黑盒测试期间也是如此。

我们还将指导你完成创建自己的基于反序列化的高严重性攻击的过程。尽管这些通常需要源代码访问,但它们在理解基本概念后也比您想象的更容易学习。具体而言,我们将介绍以下主题:

  • 如何识别不安全的反序列化实验室
  • 修改网站 LABS 期望的序列化对象
  • 将恶意数据传递到危险的网站功能实验室
  • 注入任意对象类型LABS
  • 链接方法调用以控制数据流到危险的接收器小工具 实验室
  • 手动创建您自己的高级漏洞利用实验室
  • PHAR 反序列化实验室

注意

尽管许多实验室和示例都基于 PHP,但大多数开发技术对其他语言也同样有效。

如何识别不安全的反序列化

识别不安全的反序列化相对简单,无论您是白盒测试还是黑盒测试。

在审核期间,应查看传递到网站的所有数据,并尝试识别任何看起来像序列化数据的数据。如果您知道不同语言使用的格式,则可以相对容易地识别序列化数据。在本节中,我们将展示 PHP 和 Java 序列化的示例。识别序列化数据后,可以测试是否能够控制它。

提示

对于Burp Suite Professional的用户,Burp Scanner将自动标记任何似乎包含序列化对象的HTTP消息。

PHP 序列化格式

PHP 使用大部分人类可读的字符串格式,字母表示数据类型,数字表示每个条目的长度。例如,考虑具有以下属性的对象:User

$user->name = "carlos"; $user->isLoggedIn = true;

序列化时,此对象可能如下所示:

O:4:"User":2:{s:4:"name":s:6:"carlos"; s:10:"isLoggedIn":b:1;}

这可以解释如下:

  • O:4:"User"- 具有 4 个字符的类名的对象"User"
  • 2- 对象有 2 个属性
  • s:4:"name"- 第一个属性的键是 4 个字符的字符串"name"
  • s:6:"carlos"- 第一个属性的值是 6 个字符的字符串"carlos"
  • s:10:"isLoggedIn"- 第二个属性的键是 10 个字符的字符串"isLoggedIn"
  • b:1- 第二个属性的值是布尔值true

PHP 序列化的本机方法是 and。如果您有源代码访问权限,则应首先在代码中查找任何位置并进一步调查。serialize()unserialize()unserialize()

Java 序列化格式

某些语言(如 Java)使用二进制序列化格式。这更难阅读,但如果您知道如何识别一些明显的迹象,您仍然可以识别序列化数据。例如,序列化的 Java 对象始终以相同的字节开头,这些字节按十六进制和 Base64 编码。ac ed rO0

实现接口的任何类都可以序列化和反序列化。如果您具有源代码访问权限,请记下使用该方法的任何代码,该方法用于从 an 读取和反序列化数据。
java.io.SerializablereadObject()InputStream

操作序列化对象

利用某些反序列化漏洞就像更改序列化对象中的属性一样简单。由于对象状态保持不变,因此可以研究序列化数据以识别和编辑感兴趣的属性值。然后,您可以通过其反序列化过程将恶意对象传递到网站中。这是基本反序列化攻击的第一步。

从广义上讲,在操作序列化对象时可以采用两种方法。您可以直接以字节流形式编辑对象,也可以使用相应的语言编写简短脚本来自己创建和序列化新对象。使用二进制序列化格式时,后一种方法通常更容易。

修改对象属性

篡改数据时,只要攻击者保留有效的序列化对象,反序列化过程就会使用修改后的属性值创建服务器端对象。

举一个简单的例子,考虑一个网站,该网站使用序列化对象在 Cookie 中存储有关用户会话的数据。如果攻击者在 HTTP 请求中发现此序列化对象,他们可能会对其进行解码以查找以下字节流:User

O:4:"User":2:{s:8:"username";s:6:"carlos";s:7:"isAdmin";b:0;}

属性是一个明显的兴趣点。攻击者只需将属性的布尔值更改为 (true),重新编码对象,并使用此修改后的值覆盖其当前 cookie。孤立地看,这没有任何效果。但是,假设网站使用此 cookie 来检查当前用户是否有权访问某些管理功能:isAdmin1

$user = unserialize($_COOKIE); if ($user->isAdmin === true) { // allow access to admin interface }

此易受攻击的代码会根据来自 cookie 的数据(包括攻击者修改的属性)实例化对象。在任何时候都不会检查序列化对象的真实性。然后将此数据传递到条件语句中,在这种情况下,将允许轻松提升权限。UserisAdmin

这种简单的场景在野外并不常见。但是,以这种方式编辑属性值演示了访问不安全反序列化公开的大量攻击面的第一步。

实验室学徒修改序列化对象

修改数据类型

我们已经了解了如何修改序列化对象中的属性值,但也可以提供意外的数据类型。

基于 PHP 的逻辑特别容易受到这种操作的影响,因为在比较不同的数据类型时,其松散的比较运算符 () 的行为。例如,如果您在整数和字符串之间执行松散比较,PHP 将尝试将字符串转换为整数,这意味着计算结果为。==5 == "5"true

不寻常的是,这也适用于任何以数字开头的字母数字字符串。在这种情况下,PHP 将根据初始数字有效地将整个字符串转换为整数值。字符串的其余部分将被完全忽略。因此,在实践中被视为。5 == "5 of something" 5 == 5

当将字符串与整数进行比较时,这变得更加奇怪:0

0 == "Example string" // true

为什么?因为字符串中没有数字,即 0 个数字。PHP 将整个字符串视为整数。0

考虑这样一种情况:此松散比较运算符与反序列化对象中的用户可控制数据结合使用。这可能会导致危险的逻辑缺陷。

$login = unserialize($_COOKIE) if ($login['password'] == $password) { // log in successfully }

假设攻击者修改了密码属性,使其包含整数而不是预期的字符串。只要存储的密码不以数字开头,条件将始终返回,从而启用身份验证绕过。请注意,这只能因为反序列化会保留数据类型。如果代码直接从请求中获取密码,则将转换为字符串,并且条件将计算为。0true0false

请注意,在修改任何序列化对象格式的数据类型时,请务必记住更新序列化数据中的任何类型标签和长度指示符。否则,序列化的对象将损坏,并且不会反序列化。

实验室医生修改序列化数据类型

当直接使用二进制格式时,我们建议使用Hackvertor扩展,可从BApp商店获得。使用 Hackvertor,您可以将序列化数据修改为字符串,它将自动更新二进制数据,相应地调整偏移量。这可以为您节省大量的手动工作。

使用应用程序功能

除了简单地检查属性值外,网站的功能还可能对反序列化对象中的数据执行危险操作。在这种情况下,可以使用不安全的反序列化传入意外数据,并利用相关功能进行破坏。

例如,作为网站“删除用户”功能的一部分,通过访问属性中的文件路径来删除用户的个人资料图片。如果这是从序列化对象创建的,则攻击者可以通过将具有 set 的修改对象传递到任意文件路径来利用此漏洞。删除他们自己的用户帐户也会删除此任意文件。$user->image_location$userimage_location

实验室医生使用应用程序功能利用不安全的反序列化

此示例依赖于攻击者通过用户可访问的功能手动调用危险方法。但是,当您创建自动将数据传递到危险方法的漏洞时,不安全的反序列化变得更加有趣。这是通过使用“神奇方法”来实现的。

魔术方法

魔术方法是方法的特殊子集,您不必显式调用它们。相反,每当发生特定事件或方案时,都会自动调用它们。魔术方法是各种语言中面向对象编程的共同特征。它们有时通过在方法名称前面加上前缀或用双下划线括起来来指示。

开发人员可以向类添加魔术方法,以便预先确定在发生相应事件或场景时应执行哪些代码。调用魔术方法的确切时间和原因因方法而异。PHP 中最常见的例子之一是,每当实例化类的对象时都会调用它,类似于 Python 的对象。通常,像这样的构造函数魔术方法包含用于初始化实例属性的代码。但是,开发人员可以自定义魔术方法以执行他们想要的任何代码。__construct()__init__

Magic methods are widely used and do not represent a vulnerability on their own. But they can become dangerous when the code that they execute handles attacker-controllable data, for example, from a deserialized object. This can be exploited by an attacker to automatically invoke methods on the deserialized data when the corresponding conditions are met.

Most importantly in this context, some languages have magic methods that are invoked automatically during the deserialization process. For example, PHP's method looks for and invokes an object's magic method. unserialize()__wakeup()

In Java deserialization, the same applies to the method, which is used to read data from the initial byte stream and essentially acts like a constructor for "re-initializing" a serialized object. However, classes can also declare their own method as follows: ObjectInputStream.readObject()SerializablereadObject()

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // implementation }

以这种方式声明的方法充当在反序列化期间调用的魔术方法。这允许类更紧密地控制其自身字段的反序列化。readObject()

您应该密切注意包含这些类型的魔术方法的任何类。它们允许您在对象完全反序列化之前将数据从序列化对象传递到网站代码中。这是创建更高级攻击的起点。

注入任意对象

正如我们所看到的,有时可以通过简单地编辑网站提供的对象来利用不安全的反序列化。但是,注入任意对象类型可以开辟更多可能性。

在面向对象的编程中,对象可用的方法由其类确定。因此,如果攻击者可以操纵作为序列化数据传入的对象类,则他们可以影响反序列化之后甚至反序列化期间执行的代码。

反序列化方法通常不会检查它们正在反序列化的内容。这意味着您可以传入网站可用的任何可序列化类的对象,并且该对象将被反序列化。这有效地允许攻击者创建任意类的实例。此对象不属于预期类这一事实无关紧要。意外的对象类型可能会导致应用程序逻辑出现异常,但此时恶意对象已经实例化。

如果攻击者有权访问源代码,他们可以详细研究所有可用的类。为了构建一个简单的漏洞利用,他们将寻找包含反序列化魔术方法的类,然后检查它们中是否有任何对可控数据执行危险操作。然后,攻击者可以传入此类的序列化对象,以使用其魔术方法进行攻击。

实验室医生PHP 中的任意对象注入

包含这些反序列化魔术方法的类也可用于发起更复杂的攻击,涉及一长串方法调用,称为“小工具链”。

小工具链

“小工具”是应用程序中存在的代码片段,可以帮助攻击者实现特定目标。单个小工具可能不会直接对用户输入执行任何有害操作。但是,攻击者的目标可能只是调用一种方法,该方法将其输入传递到另一个小工具中。通过以这种方式将多个小工具链接在一起,攻击者可能会将其输入传递到危险的“接收器小工具”中,从而造成最大的损害。

重要的是要了解,与某些其他类型的漏洞利用不同,小工具链不是攻击者构建的链式方法的有效载荷。网站上已经存在所有代码。攻击者唯一控制的是传递到小工具链中的数据。这通常使用在反序列化期间调用的魔术方法(有时称为“启动小工具”)来完成。

在野外,许多不安全的反序列化漏洞只能通过使用小工具链来利用。这有时可以是一个简单的一步或两步链,但构造高严重性攻击可能需要更复杂的对象实例化和方法调用序列。因此,能够构建小工具链是成功利用不安全反序列化的关键方面之一。

使用预构建的小工具链

手动识别小工具链可能是一个相当艰巨的过程,如果没有源代码访问,几乎是不可能的。幸运的是,您可以使用一些选项来使用预先构建的小工具链,您可以先尝试。

有几种工具可以提供一系列已在其他网站上成功利用的预先发现的链。即使您无权访问源代码,也可以使用这些工具来识别和利用不安全的反序列化漏洞,而工作量相对较低。由于广泛使用包含可利用小工具链的库,这种方法成为可能。例如,如果Java的Apache Commons Collections库中的小工具链可以在一个网站上被利用,那么实现该库的任何其他网站也可以使用相同的链被利用。

Ysoserial

Java反序列化的一个这样的工具是“ysoserial”。这使您可以为认为目标应用程序正在使用的库选择提供的小工具链之一,然后传入要执行的命令。然后,它基于所选链创建适当的序列化对象。这仍然涉及一定量的试验和错误,但它比手动构建自己的小工具链要少得多。

实验室医生利用 Apache Commons 利用 Java 反序列化

请注意,并非所有 ysoserial 中的小工具链都允许您运行任意代码。相反,它们可能对其他目的有用。例如,可以使用以下命令来帮助快速检测几乎任何服务器上的不安全反序列化:

  • 该链触发对提供的 URL 的 DNS 查找。最重要的是,它不依赖于使用特定易受攻击的库的目标应用程序,并且可以在任何已知的 Java 版本中工作。这使其成为用于检测目的的最通用的小工具链。如果在流量中发现序列化对象,可以尝试使用此小工具链生成触发与 Burp 协作者服务器的 DNS 交互的对象。如果是这样,则可以确定在目标上发生了反序列化。URLDNS
  • JRMPClient是另一个可用于初始检测的通用链。它会导致服务器尝试与提供的 IP 地址建立 TCP 连接。请注意,您需要提供原始 IP 地址而不是主机名。此链在所有出站流量都带有防火墙(包括 DNS 查找)的环境中可能很有用。您可以尝试使用两个不同的 IP 地址生成有效负载:本地地址和防火墙外部地址。如果应用程序立即响应具有本地地址的有效负载,但挂起具有外部地址的有效负载,导致响应延迟,则表明小工具链工作,因为服务器尝试连接到防火墙地址。在这种情况下,响应中的细微时差可以帮助您检测服务器上是否发生反序列化,即使在盲情况下也是如此。

PHP Generic Gadget Chains

Most languages that frequently suffer from insecure deserialization vulnerabilities have equivalent proof-of-concept tools. For example, for PHP-based sites you can use "PHP Generic Gadget Chains" (PHPGGC).

实验室医生使用预构建的小工具链利用 PHP 反序列化

注意

重要的是要注意,该漏洞是用户可控数据的反序列化,而不仅仅是网站代码或其任何库中存在小工具链。小工具链只是在注入有害数据后操纵其流动的一种手段。这也适用于依赖于反序列化不受信任数据的各种内存损坏漏洞。换句话说,即使网站以某种方式设法堵塞了所有可能的小工具链,该网站也可能仍然容易受到攻击。

使用记录的小工具链

在目标应用程序使用的框架中,可能并不总是有专用工具可用于利用已知的小工具链。在这种情况下,总是值得在线查看是否有任何可以手动调整的记录漏洞。调整代码可能需要对语言和框架有基本的了解,有时可能需要自己序列化对象,但这种方法仍然比从头开始构建漏洞利用要省力得多。

实验室医生使用记录的小工具链利用 Ruby 反序列化

即使您找不到现成的小工具链,您仍然可以获得有价值的知识,帮助您创建自己的自定义漏洞。

创建自己的漏洞利用

当现成的小工具链和记录的漏洞利用不成功时,您将需要创建自己的漏洞利用。

要成功构建自己的小工具链,您几乎肯定需要源代码访问。第一步是研究此源代码,以标识一个类,该类包含反序列化期间调用的魔术方法。评估此神奇方法执行的代码,以查看它是否直接对用户可控制的属性执行任何危险操作。以防万一,这总是值得检查的。

如果神奇的方法本身不可利用,它可以作为小工具链的“启动小工具”。研究启动小工具调用的任何方法。其中是否有任何一项对您控制的数据造成危险?如果没有,请仔细查看它们随后调用的每个方法,依此类推。

重复此过程,跟踪您有权访问的值,直到您到达死胡同或识别出将可控数据传递到的危险接收器小工具。

确定如何在应用程序代码中成功构造小工具链后,下一步是创建包含有效负载的序列化对象。这只是研究源代码中的类声明并创建具有漏洞利用所需的适当值的有效序列化对象的情况。正如我们在前面的实验中看到的,在使用基于字符串的序列化格式时,这相对简单。

使用二进制格式(例如在构造 Java 反序列化漏洞时)可能特别麻烦。对现有对象进行细微更改时,您可能习惯于直接使用字节。但是,当进行更重大的更改(例如传入一个全新的对象)时,这很快就会变得不切实际。用目标语言编写自己的代码通常要简单得多,以便自己生成和序列化数据。

创建自己的小工具链时,请寻找机会使用此额外的攻击面来触发次要漏洞。

实验室专家开发用于 Java 反序列化的自定义小工具链

通过仔细研究源代码,您可以发现更长的小工具链,这些链可能允许您构建高严重性攻击,通常包括远程代码执行。

实验室专家开发用于 PHP 反序列化的自定义小工具链

PHAR 反序列化

到目前为止,我们主要研究的是利用反序列化漏洞,其中网站显式反序列化用户输入。但是,在PHP中,即使没有明显的方法使用,有时也可以利用反序列化。unserialize()

PHP 提供了几个 URL 样式的包装器,可用于在访问文件路径时处理不同的协议。其中之一是thewrapper,它提供了一个用于访问PHP存档()文件的流接口。phar://.phar

PHP 文档显示清单文件包含序列化的元数据。至关重要的是,如果您在流上执行任何文件系统操作,则此元数据将被隐式反序列化。这意味着 astream 可能是利用不安全反序列化的向量,前提是您可以将此流传递到文件系统方法中。PHARphar:// phar://

对于明显危险的文件系统方法,例如,网站可能已经实施了对策,以减少它们被恶意使用的可能性。然而,诸如此类的方法,虽然没有那么明显的危险,但可能没有得到很好的保护。include()fopen()file_exists()

此技术还要求您以某种方式将其上传到服务器。例如,一种方法是使用图像上传功能。如果您能够创建多语言文件,并且使用简单的amasquerading,则有时可以绕过网站的验证检查。如果您可以强制网站从流加载此多语言“”,则通过元数据注入的任何有害数据都将被反序列化。由于 PHP 读取流时不检查文件扩展名,因此文件使用图像扩展名并不重要。PHARPHARJPGJPGphar:// PHAR

只要网站支持对象的类,就可以以这种方式调用这两种theandmagic方法,从而允许您使用这种技术启动小工具链。__wakeup()__destruct()