【WEB】反序列化漏洞(更新中)

0x00 前言

在本文中,我们将介绍什么是不安全的反序列化,并描述它如何使网站可能遭受高强度攻击。我们将重点介绍典型方案,并使用PHP,Ruby和Java反序列化的具体示例演示一些广泛适用的技术。我们还将研究一些方法,您可以避免自己的网站中存在不安全的反序列化漏洞。
在这里插入图片描述

0x01 什么是序列化?

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

  • 将复杂数据写入进程间内存,文件或数据库
  • 例如,通过网络,在应用程序的不同组件之间或在API调用中发送复杂的数据
    至关重要的是,在序列化对象时,其状态也将保留。换句话说,将保留对象的属性及其分配的值。

0x02 序列化与反序列化

反序列化是将字节流还原为原始对象的完整功能副本的过程,其状态与序列化时的状态完全相同。然后,网站的逻辑可以与此反序列化的对象进行交互,就像与任何其他对象进行交互一样。
在这里插入图片描述
许多编程语言为序列化提供本机支持。对象的确切序列化方式取决于语言。某些语言将对象序列化为二进制格式,而其他语言则使用不同的字符串格式,并具有不同程度的人类可读性。请注意,所有原始对象的属性都存储在序列化的数据流中,包括任何私有字段。为防止字段被序列化,必须在类声明中将其显式标记为“ transient”。

请注意,在使用不同的编程语言时,序列化可能被称为marshalling (Ruby) 或 pickling (Python)。在本文中,这些术语与“serialization”同义。

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

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

甚至有可能用完全不同类的对象替换序列化的对象。令人震惊的是,无论预期使用哪种类,网站上可用的任何类的对象都会被反序列化和实例化。因此,不安全的反序列化有时称为“对象注入”漏洞。

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

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

通常会出现不安全的反序列化,因为人们普遍缺乏对用户可控制数据进行反序列化的危险程度的了解。理想情况下,用户输入绝对不应反序列化。

但是,有时网站所有者认为它们是安全的,因为他们对反序列化的数据实施了某种形式的附加检查。这种方法通常是无效的,因为几乎不可能实施验证或消毒以解决所有可能的情况。这些检查从根本上来说也是有缺陷的,因为它们依赖于对数据进行反序列化后对其进行检查,在许多情况下,为防止攻击而为时已晚。

由于通常认为反序列化对象是可信任的,因此也可能会出现漏洞。尤其是当使用具有二进制序列化格式的语言时,开发人员可能会认为用户无法有效读取或操纵数据。但是,尽管可能需要付出更多的努力,但攻击者有可能利用二进制序列化的对象,就象利用基于字符串的格式一样。

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

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

0x05不安全的反序列化有何影响?

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

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

0x06利用反序列化漏洞

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

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

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

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序列化的本机方法是serialize()和unserialize()。如果您具有源代码访问权限,则应从unserialize()代码中的任意位置开始并进行进一步调查。

Java序列化格式

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

任何实现该接口的类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属性)实例化对象。绝对不会检查序列化对象的真实性。然后将这些数据传递到条件语句中,在这种情况下,将允许轻松地进行特权升级。

这种简单的情况一般情况下并不常见。但是,以这种方式编辑属性值说明了访问不安全反序列化所暴露的大量攻击面的第一步。

例子:
在这里插入图片描述
1,使用您自己的凭据登录。请注意,登录后GET /请求包含一个会话cookie,该会话cookie似乎是URL并以Base64编码的。将此请求发送到burp Repeater。另外,突出显示cookie值,然后从上下文菜单中选择“Send to Decoder”。
2,在Burp解码器中,选择“解码为”>“ URL”。在新字符串上,选择“解码为”>“ Base64”以显示cookie是序列化的PHP对象。
注意,该admin属性包含b:0,指示布尔值false。将此更改为b:1。
3,选择“编码为”>“ Base64”,然后选择“编码为”>“ URL”。将URL编码的字符串复制到剪贴板。
在Burp Repeater中,将会话cookie替换为剪贴板中的已修改会话cookie。发送请求。
在这里插入图片描述
4,这里可以把得到的cookies复制到editthiscookie在这里插入图片描述
然后关闭burp

进入管理员面板
在这里插入图片描述

在这里插入图片描述
删除即可

在这里插入图片描述

修改数据类型

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

由于基于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
}

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

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

使用应用程序功能

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

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值