利用不安全的反序列化漏洞
翻译
原文:https://portswigger.net/web-security/deserialization/exploiting
- name: 翻译
desc: 原文:https://portswigger.net/web-security/deserialization/exploiting
bgColor: '#F0DFB1'
textColor: 'green'
2
3
4
# 1利用不安全的反序列化漏洞
在本节中,我们将使用 PHP、Ruby 和 Java 反序列化的示例教你如何利用一些常见的场景。我们希望证明利用不安全的反序列化实际上比许多人想象中的要容易得多。如果你能够使用预构建的小工具链,那么在黑盒测试期间也是如此(容易)。
我们还将指导你创建自己的反序列化、完成高严重性攻击的过程。虽然这些操作通常需要访问源代码,但一旦你理解了基本概念,它们也可能比你想象的更容易学习。具体而言,我们将涵盖以下主题:
- 如何识别不安全的反序列化 (opens new window)LABS
- 修改网站预期的序列化对象 (opens new window)LABS
- 将恶意数据传递到危险的网站功能中 (opens new window)LABS
- 注入任意对象类型 (opens new window)LABS
- 链接方法调用,以控制数据流进入到危险的接收器小工具中 (opens new window)LABS
- 手动创建自己的高级漏洞载荷 (opens new window)LABS
- PHAR 反序列化 (opens new window)LABS
笔记
尽管许多实验和示例都是基于 PHP 的,但大多数利用技术对其他语言也同样适用。
# 2如何识别不安全的反序列化
无论你是在白盒测试还是黑盒测试中,识别不安全的反序列化都相对简单。
在审查期间,你应该查看传递到网站的所有数据,并尝试识别任何看起来像序列化数据的内容。如果你知道不同语言使用的格式,则可以相对容易地识别序列化数据。在本节中,我们将展示 PHP 和 Java 序列化的示例。当你识别序列化数据后,可以测试是否能够控制它。
提示
对于 Burp Suite Professional (opens new window) 的用户,Burp Scanner (opens new window) 将自动标记任何可能包含序列化对象的 HTTP 消息。
# 2.1PHP序列化格式
PHP 使用一种人类可读的字符串格式,字母表示数据类型,数字表示每个条目的长度。例如,考虑具有以下属性的User
对象:
$user->name = "carlos";
$user->isLoggedIn = true;
2
当序列化时,这个对象可能看起来像这样:
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 序列化的原生方法是serialize()
和unserialize
。如果你具有源代码的访问权限,则应首先在代码中的任何位置查找unserialize()
并进一步审查。
# 2.2Java序列化格式
某些语言(如 Java)使用二进制序列化格式。这更加难以阅读,但如果你能够识别一些特征,那你仍然可以识别序列化数据。例如,序列化的 Java 对象始终以相同的字节开头,这些字节在十六进制中编码为ac ed
,在 Base64 中编码为rO0
。
任何实现接口java.io.Serializable
的类都可以被序列化和反序列化。如果你具有源代码的访问权限,请留意使用了readObject()
方法的任何代码,该方法用于从InputStream
中读取和反序列化数据。
# 3操纵序列化对象
利用某些反序列化漏洞,就像更改序列化对象中的属性一样简单。由于对象状态是持久化的,因此你可以研究序列化的数据,以识别和编辑感兴趣的属性值。然后,你可以通过其反序列化过程,将恶意对象传递到网站中。这是基本反序列化漏洞利用的初始步骤。
一般来说,在操作序列化对象时,可以采用两种方法。你可以直接以字节流的形式编辑对象,也可以使用相应的语言来编写一个简短的脚本,从而自己创建并序列化新对象。在使用二进制序列化格式时,后一种方法通常更容易。
# 3.1修改对象属性
篡改数据时,只要攻击者保存了有效的序列化对象,反序列化过程就会创建一个服务器端对象,该对象具有篡改后的属性值。
举个简单的例子,假设一个网站使用序列化的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
}
2
3
4
此易受攻击的代码会根据 cookie 中的数据来实例化User
对象(包括攻击者修改后的isAdmin
属性)。在任何时候都不会检查序列化对象的真实性。然后,此数据将传递到条件语句中,在这种情况下,可以轻松地进行权限提升。
这种简单的场景在野外并不常见。但是,以这种方式编辑属性值,演示了由不安全的反序列化暴露的大量攻击面的第一步。
- name: 实验室-学徒
desc: 修改序列化对象 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-modifying-serialized-objects
bgColor: '#001350'
textColor: '#39d50c'
2
3
4
5
6
# 3.2修改数据类型
我们已经理解了如何修改序列化对象中的属性值,但也有可能提供意外的数据类型。
基于 PHP 的逻辑特别容易受到这种操作的影响,因为在比较不同的数据类型时,其具有松散的比较运算符(==
)的行为。例如,如果你对 整数和字符串 进行松散比较,PHP 会尝试将字符串转换为整数,这意味着5 == "5"
的计算结果为true
。
不同寻常的是,这也适用于任何以数字开头的字母、数字和字符串。在这种情况下,PHP 会根据初始数字,有效地将整个字符串转换为整数值。字符串的其余部分将被完全忽略。因此,5 == "5 of something"
实际上被视为5 == 5
。
当一个字符串与整数0
比较时,这变得更加奇怪:
0 == "Example string" // true
为什么?因为字符串中没有数字,即 0 个数字。PHP 将整个字符串视为整数0
。
考虑这样一种情况,把这个松散的比较运算符,与来自反序列化对象的用户可控数据结合使用。这可能会导致危险的逻辑缺陷 (opens new window)。
$login = unserialize($_COOKIE)
if ($login['password'] == $password) {
// log in successfully
}
2
3
4
假设攻击者修改了password
属性,使其包含整数0
而不是预期的字符串。只要存储的密码不是以数字开头,条件判断将始终返回true
,从而造成身份验证绕过。请注意,这只有在反序列化并 保留数据类型 时才有可能。如果代码直接从请求中获取密码,则0
将被转换为字符串,条件的计算结果会变为false
。
请注意,在修改任何序列化对象格式的数据类型时,请记住,一定要更新序列化数据中的任何 类型标签和长度指示符。否则,序列化对象将损坏,并且不会被反序列化。
- name: 实验室-从业者
desc: 修改序列化数据类型 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-modifying-serialized-data-types
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
当直接使用二进制格式时,我们建议使用 BApp 商店提供的 Hackvertor (opens new window) 扩展。使用 Hackvertor (opens new window),你可以将序列化数据修改为字符串,它会自动更新二进制数据,并相应地调整偏移量。这可以为你节省大量的手动工作。
# 4使用应用程序功能
除了简单地检查属性值之外,网站的功能还可能对反序列化对象中的数据执行危险的操作。在这种情况下,你可以使用不安全的反序列化来传入意外的数据,并利用相关功能造成损害。
例如,作为网站 “删除用户” 功能的一部分,用户的个人资料图片可以通过访问$user->image_location
属性中的文件路径来删除。如果此$user
是从序列化对象创建的,则攻击者可以传入一个修改过的对象,将image_location
设置为任意文件路径。然后,当他们删除自己的用户帐户时,也会删除此任意文件。
- name: 实验室-从业者
desc: 通过应用程序功能利用不安全的反序列化 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-using-application-functionality-to-exploit-insecure-deserialization
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
此示例依赖于 攻击者通过用户可访问的功能 手动调用危险方法。但是,当你创建一个 自动将数据传递到危险方法中 的漏洞载荷时,不安全的反序列化将会变得更加有趣。这是通过使用 “魔术方法” 来实现的。
# 5魔术方法
魔术方法是一个特殊的方法子集,你不必显式调用。相反,每当发生特定事件 或 处于特定场景时,都会自动调用它们。魔术方法是各种语言中 面向对象编程 的共同特征。它们有时在方法名前面加前缀,或用双下划线括起来表示。
开发人员可以在类中添加魔术方法,以便预先确定 当发生相应事件或场景时 应执行哪些代码。魔术方法被调用的确切时间和原因会因方法而异。PHP 中最常见的例子之一是__construct()
,每当类的对象被实例化时就会调用它,类似于 Python 的__init__
。通常,像这样的构造函数魔术方法中,包含用于初始化实例属性的代码。但是,开发人员可以自定义魔术方法,来执行他们想要的任何代码。
魔术方法被广泛使用,它们本身并不代表漏洞。但是,当它们处理攻击者可控数据时(例如,来自反序列化对象的数据),它们可能会变得危险。攻击者可利用此漏洞,在满足相应条件时 自动调用反序列化数据的方法。
在这方面最重要的是,某些语言有一些神奇的方法,可以在反序列化期间自动调用。例如,PHP 的unserialize()
方法会查找并调用对象的__wakeup()
魔术方法。
在 Java 反序列化中,这同样适用于ObjectInputStream.readObject()
方法,该方法用于从初始字节流中读取数据,本质上就像构造函数,用于 “重新初始化” 一个序列化的对象。但是,Serializable
类也可以声明自己的readObject()
方法,如下所示:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
// implementation
}
2
3
4
以这种方式声明的readObject()
方法充当一个魔术方法,在反序列化期间被调用。这允许 类 更紧密地控制其自身字段的反序列化。
你应该密切关注包含这些类型的魔术方法的任何类。它们允许你在对象完全反序列化之前,将数据从序列化对象传递到网站的代码中。这是创建更高级漏洞载荷的起点。
# 6注入任意对象
正如我们所看到的,有时可以通过简单地编辑网站提供的对象,来利用不安全的反序列化。但是,注入任意对象类型可以开辟更多可能性。
在面向对象编程中,对象可用的方法由其类决定。因此,如果攻击者可以操纵作为序列化数据传入的对象类,则可以影响在反序列化之后、甚至是反序列化期间执行的代码。
反序列化方法 通常不检查它们正在反序列化的内容。这意味着,你可以传入网站可用的 任何能够被序列化的类对象,并且该对象后续将被反序列化。这实际上允许攻击者创建任意类的实例。这个对象 不属于预期的类 这一事实并不重要。意外的对象类型可能会在应用程序逻辑中导致异常,但此时恶意对象已经实例化。
如果攻击者有权访问源代码,他们可以详细研究所有可用的类。为了构造一个简单的漏洞载荷,他们会寻找包含反序列化魔术方法的类,然后检查其中是否有任何一个类 对可控数据执行了危险操作。然后,攻击者可以传入此类的序列化对象,以使用其魔术方法进行攻击。
- name: 实验室-从业者
desc: PHP中的任意对象注入 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-arbitrary-object-injection-in-php
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
包含这些反序列化魔术方法的类,也可用来发起更复杂的攻击,这涉及一长串的方法调用,称为 “小工具链”。
# 7小工具链
“小工具” 是存在于应用程序中的代码片段,可以帮助攻击者实现特定目标。单个小工具可能不会直接对用户输入执行任何有害操作。然而,攻击者的目标可能只是调用一个方法,将他们的输入传递到另一个小工具。通过这种方式将多个小工具链接在一起,攻击者可能会将其输入传递到危险的 “接收器小工具” 中,在那里它可能会造成最大的损害。
重要的是要理解,与其他类型的漏洞载荷不同,小工具链不是由 攻击者构造的链式方法 形成的有效载荷。所有的代码都已经存在于网站上,攻击者唯一控制的是传递到小工具链中的数据。这通常是使用在反序列化期间调用的魔术方法完成的,这有时被称为 “启动小工具”。
# 7.1使用预构建的小工具链
手动识别小工具链可能是一个相当艰巨的过程,如果没有源代码的访问权限,这几乎是不可能的。幸运的是,你可以先尝试一些 使用预构建小工具链 的选项。
有几种工具可以提供一系列预先发现的链,这些链已在其他网站上成功利用。即使你无法访问源代码,也可以使用这些工具,以相对较少的努力,来识别和利用不安全的反序列化漏洞。这种方法之所以成为可能,是因为包含可利用小工具链的库被广泛使用。例如,如果 Java 的 Apache Commons Collections 库中的小工具链可以在一个网站上被利用,那么使用该库的任何其他网站 也可以被相同的链利用。
# 7.1.1ysoserial
有一个这样的 Java 反序列化工具是 “ysoserial”。你就可以确认目标应用程序正在使用的库,选择一个提供的小工具链,然后传入要执行的命令。随后,它基于所选的链 创建适当的序列化对象。这仍然涉及一定的试验和错误,但与手动构建自己的小工具链相比,它的劳动强度要低得多。
笔记
在 Java 16 版本及更高版本中,你需要为 Java 设置一系列命令行参数才能运行 ysoserial。例如:
java -jar ysoserial-all.jar \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
--add-opens=java.base/java.net=ALL-UNNAMED \
--add-opens=java.base/java.util=ALL-UNNAMED \
[载荷] '[命令]'
2
3
4
5
6
- name: 实验室-从业者
desc: 通过Apache Commons利用Java反序列化 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-exploiting-java-deserialization-with-apache-commons
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
并非 ysoserial 中的所有小工具链都允许你运行任意代码。相反,它们可能用于其他目的。例如,你可以使用以下方法,来帮助你快速检测 几乎任何服务器上的 不安全的反序列化:
URLDNS
链会触发对所提供 URL 的 DNS 查找。最重要的是,它不依赖于 使用特定易受攻击库 的目标应用程序,并且可以在任何已知的 Java 版本中工作。出于检测目的,这使其成为最通用的小工具链。如果你在流量中发现了序列化对象,你可以尝试使用此小工具链生成一个对象,该对象会触发与 Burp Collaborator 服务器的 DNS 交互。如果确实发生了交互,则可以确定目标发生了反序列化。JRMPClient
是另一个通用链,可用于初始检测。它会导致服务器尝试与所提供的 IP 地址建立 TCP 连接。请注意,你需要提供原始 IP 地址,而不是主机名。此链在所有出站流量(包括DNS查找)都受防火墙保护的环境中可能很有用。你可以尝试使用两个不同的 IP 地址生成有效负载:一个是本地 IP 地址,另一个是防火墙的外部 IP 地址。如果应用程序立即响应本地地址的有效负载,但挂起外部地址的有效负载,从而导致响应延迟,则表明小工具链工作正常,因为服务器试图连接到防火墙地址。在这种情况下,响应中的细微时间差可以帮助你检测服务器上是否发生了反序列化,即使在盲目情况下也是如此。
# 7.1.2PHP通用小工具链
大多数经常遭受不安全反序列化漏洞的语言,都有类似的概念验证工具。例如,对于基于 PHP 的站点,你可以使用 “PHP通用小工具链”(PHPGGC)。
- name: 实验室-从业者
desc: 通过预构建的小工具链利用PHP反序列化 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-exploiting-php-deserialization-with-a-pre-built-gadget-chain
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
笔记
需要注意的是,该漏洞是用户可控数据的反序列化,而不仅仅是网站代码或其任何库中存在的小工具链。小工具链只是一种在注入有害数据后,操纵有害数据流的手段。这也适用于 依赖不受信任数据反序列化 的各种内存破坏漏洞。换句话说,即使网站设法以某种方式堵塞了所有可能的小工具链,它仍然可能容易受到攻击。
# 7.1.3使用收录在案的小工具链
在目标应用程序使用的框架中,可能并不总是有 专用的工具 可用于利用已知的小工具链。在这种情况下,总是值得到网络上去搜索,看看是否有任何 收录在案的漏洞载荷 可以供你手动调整。调整代码可能需要对语言和框架有一些基本的了解,有时可能需要自己序列化对象,但和从头开始构建漏洞比起来,这种方法所付出的努力仍然要少得多。
- name: 实验室-从业者
desc: 使用收录在案的小工具链利用Ruby反序列化 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-exploiting-ruby-deserialization-using-a-documented-gadget-chain
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
即使你找不到可用的小工具链,你仍然可以获得宝贵的知识,帮助你创建自己的自定义漏洞。
# 8创建你自己的漏洞载荷
当现成的小工具链 和 收录在案的漏洞载荷不成功时,你将需要创建自己的漏洞载荷。
要成功构建自己的小工具链,你几乎肯定需要访问源代码。第一步是研究这个源代码,以确定一个类,该类包含一个在反序列化期间调用的魔术方法。评估此魔术方法执行的代码,以查看它是否直接对 用户可控属性 执行了任何危险的操作。为了万无一失,这总是值得检查的。
如果魔术方法本身无法被利用,可以把它作为小工具链的 “启动小工具”。研究启动小工具调用的任何方法,其中的任何一个方法是否会 对你控制的数据 执行危险操作?如果没有,请仔细查看它们随后调用的每个方法,依此类推。
重复此过程,跟踪你可以访问的值,直到你到达死胡同 或 识别出一个危险的接收器小工具,你的可控数据将被传递到其中。
一旦你弄清楚了如何在应用程序代码中成功构造一个小工具链,下一步就是创建一个包含有效负载的序列化对象。这只是一个案例,研究源代码中的类声明 并创建一个有效的序列化对象,该对象具有漏洞载荷所需的适当值。正如我们在前面的实验中看到的那样,在使用基于字符串的序列化格式时,这相对简单。
使用二进制格式(例如在构建 Java 反序列化漏洞时)可能特别麻烦。在对现有对象进行细微更改时,你可能习惯于直接使用字节。但是,当进行更重大的更改时,例如传入一个全新的对象,这很快就变得不切实际了。为了自行生成并序列化数据,用目标语言编写自己的代码,通常要简单得多。
在创建自己的小工具链时,请留意 利用这些额外攻击面 触发次要漏洞的机会。
- name: 实验室-专家
desc: 开发用于Java反序列化的自定义小工具链 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-developing-a-custom-gadget-chain-for-java-deserialization
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
通过仔细研究源代码,你可以发现更长的小工具链,这些小工具链可能允许你构建高严重性攻击,通常包括远程代码执行。
- name: 实验室-专家
desc: 开发用于PHP反序列化的自定义小工具链 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-developing-a-custom-gadget-chain-for-php-deserialization
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
# 9PHAR 反序列化
到目前为止,我们主要研究了 “网站显式地对用户输入反序列化” 的漏洞载荷。但是,在 PHP 中,即使没有明显使用unserialize()
方法,有时也可以利用反序列化。
PHP 提供了几个 URL 样式的包装器,可用于在访问文件路径时处理不同的协议。其中之一是phar://
包装器,它提供了一个用于访问 PHP 存档(.phar
)文件的流接口。
PHP 文档显示PHAR
清单文件包含序列化的元数据。至关重要的是,如果你对phar://
流执行任何文件系统操作,则此元数据将被隐式反序列化。这意味着phar://
流可能是利用不安全反序列化的向量,前提是你可以将此流传递到文件系统方法中。
对于明显危险的文件系统方法,例如include()
或fopen()
,网站可能已经实施了对策,以减少它们被恶意使用的可能性。但是,像file_exists()
这样的方法并不那么明显危险,可能没有得到很好的保护。
此技术还要求你以某种方式将PHAR
上传到服务器。例如,一种方法是使用图像上传功能。如果你能够创建一个多语言文件,并将PHAR
伪装成简单的JPG
,你有时可以绕过网站的验证检查。如果你可以强制网站从phar://
流加载这个多语言 “JPG
”,则你通过PHAR
元数据注入的任何有害数据都将被反序列化。由于 PHP 读取流时不会检查文件扩展名,因此文件是否使用图像扩展名并不重要。
只要网站支持对象的类,就可以通过这种方式调用__wakeup()
和__destruct()
魔术方法,从而允许你使用这种技术启动小工具链。
- name: 实验室-专家
desc: 使用PHAR反序列化部署自定义小工具链 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-using-phar-deserialization-to-deploy-a-custom-gadget-chain
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
这项发明性的技术,被列入我们 2018 年的十大 Web 黑客技术。
# 10通过内存破坏利用反序列化
即使不使用小工具链,仍然有可能利用不安全的反序列化。如果所有其他方法都失败了,通常会有公开收录的内存破坏漏洞,可以通过不安全的反序列化来利用这些漏洞。这些漏洞通常会导致远程代码执行。
反序列化方法,如 PHP 的unserialize()
很少针对此类攻击进行强化,并且暴露了大量的攻击面。这本身并不总是被视为漏洞,因为这些方法从一开始 就不是为了处理用户可控制的输入。