反序列化漏洞的利用
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"——表示具有四个字符的类名对象User
- 2 ——表示对象具有两个属性
- s:4:"name"——表示第一个属性的变量名是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 以十六进制编码、像rO0 以Base64进行编码。
操作序列化对象
利用一些反序列化漏洞就像更改序列化对象中的属性一样简单。由于对象状态被持久化,您可以研究序列化数据以识别和编辑感兴趣的属性值。然后,您可以通过其反序列化过程将恶意对象传递到网站中。这是基本反序列化利用的第一步。
从广义上讲,在操作序列化对象时可以采用两种方法:
- 可以直接以字节流的形式编辑对象
- 可以用相应的语言编写一个简短的脚本来自己创建和序列化新对象。
在使用二进制序列化格式时,后一种方法通常更容易。
修改对象属性
假设一个网站:
它使用一个序列化的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属性。任何时候都不会检查序列化对象的真实性。然后将此数据传递到条件语句中,在这种情况下,将允许轻松提升权限。
这种简单的场景在实际环境中并不常见。但是,以这种方式编辑属性值是访问不安全反序列化暴露的大量攻击面的第一步。
修改数据类型
在PHP中,(==)运算符属于一个弱类型的比较,只比较值而不比较类型
类似于 6=="6"这种返回的是true,PHP自动将字符串转换为数字类型
更加有趣的是,这样也适用于任何以数字开头的字符串,PHP只会将初始有效数字转换为整数值进行比较,而后面的字符串完全忽略,比如 5=="5 adddfeasfs" 返回的是true
甚至: 0 == "aaaadfafsaf" 返回的是true ,因为没有数字在这里,PHP将整个字符串设置为整数0.
这种松散的不叫会带来反序列化的危险:
比如以下代码:
$login = unserialize($_COOKIE)
if ($login['password'] == $password) {
// log in successfully
}
只要存储的密码不以数字开头,条件就会始终返回true,从而启用身份验证绕过。请注意,这是唯一可能的,因为反序列化保留了数据类型。如果代码直接从请求中获取密码,则0将转换为字符串,条件将评估为false。
请注意,在修改任何序列化对象格式的数据类型时,记住更新序列化数据中的任何类型标签和长度指示器也很重要。否则,序列化的对象将被破坏并且不会被反序列化。
直接使用二进制格式时,我们建议使用 BApp 商店提供的 Hackvertor 扩展。使用 Hackvertor,您可以将序列化数据修改为字符串,它会自动更新二进制数据,相应地调整偏移量。这可以为您节省大量的手动工作。
0=="sdsdsfaf" true
0=="123sdsdsfaf" false
1=="sdsdsfaf" false
1=="1sdsdsfaf" true
1=="a 1 sdsdsfaf" false
使用应用程序功能
除了简单地检查属性值外,网站的功能还可能对来自反序列化对象的数据执行危险的操作。在这种情况下,您可以使用不安全的反序列化来传递意外数据并利用相关功能进行破坏。
例如,作为网站“删除用户”功能的一部分,通过访问$user->image_location属性中的文件路径来删除用户的个人资料图片。如果这$user是从序列化对象创建的,攻击者可以通过将修改后的对象与image_location设置为任意文件路径来利用它。删除他们自己的用户帐户也会删除这个任意文件。
魔术方法
魔术方法是各种语言中面向对象编程的共同特征。它们有时通过在方法名称前加上双下划线或将其括起来来表示。
开发者可以在类中添加魔术方法,以便预先确定在相应的事件或场景发生时应该执行哪些代码。调用魔术方法的确切时间和原因因方法而异。PHP 中最常见的例子之一是__construct(),每当类的对象被实例化时就会调用它,类似于 Python 的__init__. 通常,像这样的构造函数魔术方法包含初始化实例属性的代码。但是,开发人员可以自定义魔术方法来执行他们想要的任何代码。
魔术方法被广泛使用,本身并不代表漏洞。但是,当它们执行的代码处理攻击者可控制的数据时,例如来自反序列化对象的数据,它们就会变得危险。攻击者可以利用这一点在满足相应条件时自动调用反序列化数据的方法。
最重要的是,在这种情况下,某些语言具有在反序列化过程中自动调用的魔术方法。
例如,PHP 的unserialize()方法查找并调用对象的__wakeup()魔术方法。
在 Java 反序列化中,同样适用于readObject()方法,它本质上就像一个用于“重新初始化”序列化对象的构造函数。该ObjectInputStream.readObject()方法用于从初始字节流中读取数据。但是,可序列化的类也可以声明自己的readObject()方法,如下所示:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {...};
这允许类更紧密地控制其自身字段的反序列化。至关重要的是,以readObject()这种方式声明的方法充当在反序列化期间调用的魔术方法。
您应该密切注意包含这些类型的魔术方法的任何类。它们允许您在对象完全反序列化之前将数据从序列化对象传递到网站代码中。这是创建更高级漏洞利用的起点。
注入任意对象
正如我们所见,有时可以通过简单地编辑网站提供的对象来利用不安全的反序列化。然而,注入任意对象类型可以开辟更多的可能性。
在面向对象的编程中,对象可用的方法由它的类决定。因此,如果攻击者可以操纵作为序列化数据传入的对象类,他们就可以影响在反序列化之后甚至反序列化期间执行的代码。
反序列化方法通常不会检查它们正在反序列化的内容。这意味着您可以传入网站可用的任何可序列化类的对象,并且该对象将被反序列化。这有效地允许攻击者创建任意类的实例。该对象不属于预期类别这一事实无关紧要。意外的对象类型可能会导致应用程序逻辑异常,但此时恶意对象已经被实例化。
如果攻击者可以访问源代码,他们可以详细研究所有可用的类。为了构建一个简单的漏洞利用,他们会寻找包含反序列化魔术方法的类,然后检查它们中是否有任何人对可控数据执行危险操作。然后攻击者可以传入此类的序列化对象以使用其魔术方法进行攻击。
包含这些反序列化魔法方法的类也可用于发起更复杂的攻击,涉及一长串方法调用,称为“小工具链”。
小工具链
“小工具”是应用程序中存在的一段代码,可以帮助攻击者实现特定目标。单个小工具可能不会直接对用户输入进行任何有害操作。然而,攻击者的目标可能只是调用一个方法,将他们的输入传递给另一个小工具。通过以这种方式将多个小工具链接在一起,攻击者可能会将他们的输入传递到一个危险的“接收器小工具”中,从而造成最大的损害。
重要的是要了解,与其他一些类型的漏洞利用不同,小工具链不是攻击者构建的链式方法的有效载荷。所有代码已经存在于网站上。攻击者唯一控制的是传递到小工具链的数据。这通常使用在反序列化期间调用的魔术方法来完成,有时称为“启动小工具”。
在外界,许多不安全的反序列化漏洞只能通过使用小工具链来利用。这有时可能是一个简单的一两步链,但构建高严重性攻击可能需要更复杂的对象实例化和方法调用序列。因此,能够构建小工具链是成功利用不安全反序列化的关键方面之一。
使用预先构建的小工具链
手动识别小工具链可能是一个相当艰巨的过程,如果没有源代码访问权限几乎是不可能的。幸运的是,您可以先尝试一些使用预先构建的小工具链的选项。
有多种工具可以提供一系列已在其他网站上成功利用的预先发现的链。即使您无法访问源代码,您也可以使用这些工具以相对较少的工作量来识别和利用不安全的反序列化漏洞。由于包含可利用的小工具链的库的广泛使用,这种方法成为可能。例如,如果 Java 的 Apache Commons Collections 库中的小工具链可以在一个网站上被利用,那么实现该库的任何其他网站也可以使用相同的链来利用。
ysoserial
一种用于 Java 反序列化的工具是“ysoserial”。这使您可以为您认为目标应用程序正在使用的库选择提供的小工具链之一,然后传入您要执行的命令。然后,它根据选定的链创建适当的序列化对象。这仍然涉及一定数量的反复试验,但与手动构建自己的小工具链相比,它的劳动密集度要低得多。
请注意,并非 ysoserial 中的所有小工具链都允许您运行任意代码。相反,它们可能用于其他目的。例如,您可以使用以下方法来帮助您在几乎任何服务器上快速检测不安全的反序列化:
- 该URLDNS链会触发对提供的 URL 的 DNS 查找。最重要的是,它不依赖于使用特定易受攻击的库的目标应用程序,并且可以在任何已知的 Java 版本中运行。这使其成为用于检测目的的最通用的小工具链。如果您在流量中发现序列化对象,您可以尝试使用此小工具链生成一个对象,该对象触发与 Burp Collaborator 服务器的 DNS 交互。如果是这样,您可以确定在您的目标上发生了反序列化。
- JRMPClient是另一个可用于初始检测的通用链。它会导致服务器尝试与提供的 IP 地址建立 TCP 连接。请注意,您需要提供原始 IP 地址而不是主机名。该链在所有出站流量都受到防火墙保护的环境中可能很有用,包括 DNS 查找。您可以尝试使用两个不同的 IP 地址生成有效负载:一个本地地址和一个受防火墙保护的外部地址。如果应用程序立即响应具有本地地址的负载,但挂起具有外部地址的负载,导致响应延迟,这表明 gadget 链有效,因为服务器尝试连接到防火墙地址。在这种情况下,响应中细微的时间差异可以帮助您检测服务器上是否发生反序列化,即使是在盲目的情况下。
PHP 通用小工具链
大多数经常遭受不安全反序列化漏洞影响的语言都有等效的概念验证工具。例如,对于基于 PHP 的站点,您可以使用“PHP 通用小工具链”(PHPGGC)。
需要注意的是,该漏洞是用户可控数据的反序列化,而不仅仅是网站代码或其任何库中存在的小工具链。小工具链只是一种在注入有害数据后操纵其流动的手段。这也适用于依赖于不受信任数据的反序列化的各种内存损坏漏洞。换句话说,即使网站确实以某种方式设法插入了所有可能的小工具链,它也可能仍然容易受到攻击。
使用记录的小工具链
总是值得在网上查看是否有任何记录的漏洞可以用来攻击目标网站。即使没有用于自动生成序列化对象的专用工具,您仍然可以找到流行框架的文档小工具链并手动调整它们。
创建自己的漏洞利用
当现成的小工具链和记录的漏洞利用不成功时,您将需要创建自己的漏洞利用。
要成功构建您自己的小工具链,您几乎肯定需要访问源代码。第一步是研究此源代码以识别包含在反序列化期间调用的魔术方法的类。评估这个魔法方法执行的代码,看看它是否直接对用户可控的属性做了任何危险的事情。这总是值得检查以防万一。
如果魔术方法本身无法利用,它可以作为小工具链的“启动小工具”。研究启动小工具调用的任何方法。这些是否会对您控制的数据造成危险?如果没有,请仔细查看它们随后调用的每个方法,依此类推。
重复此过程,跟踪您可以访问哪些值,直到您到达死胡同或识别您的可控数据被传递到的危险接收器小工具。
一旦您确定了如何在应用程序代码中成功构建小工具链,下一步就是创建一个包含您的负载的序列化对象。这只是研究源代码中的类声明并使用漏洞利用所需的适当值创建有效序列化对象的一个案例。正如我们在之前的实验中所见,这在处理基于字符串的序列化格式时相对简单。
使用二进制格式,例如在构建 Java 反序列化漏洞时,可能特别麻烦。对现有对象进行细微更改时,您可能会习惯于直接使用字节。但是,当进行更重要的更改时,例如传入一个全新的对象,这很快就变得不切实际。为了自己生成和序列化数据,用目标语言编写自己的代码通常要简单得多。
在创建您自己的小工具链时,寻找机会使用这个额外的攻击面来触发二级漏洞。
本文来自博客园,作者:{Zeker62},转载请注明原文链接:https://www.cnblogs.com/Zeker62/p/15175718.html