最新的2021 Top 10已经出来了,我们从A01开始进行一次详细解读,本系列会详细介绍各个漏洞的变化与内容,并会着重介绍新增的漏洞情况。本篇解读A08 Software and Data Integrity Failures(软件和数据完整性故障)。
弱点因素
对应的 CWEs 数量 | 最大发生率 | 平均发生率 | 最大覆盖范围 | 平均覆盖范围 | 平均加权漏洞 | 平均加权影响 | 出现次数 | 相关 CVEs 总量 |
---|---|---|---|---|---|---|---|---|
10 | 16.67% | 2.05% | 75.04% | 45.35% | 6.94 | 7.94 | 47,972 | 1,152 |
弱点简介
这是 2021 年的新类型,着重在软体更新,关键资料及持续性整合/部署(CI/CD)流程未经完整性验证之假设。同时在 CVE/CVSS 资料加权后之最高影响之一。值得注意的 CWE 包含 CWE-502:不受信任资料之反序列化,CWE-829:包含来自不受信任控制领域之功能及 CWE-494:下载未经完整性验证之程式码。
弱点描述
程式码或基础架构未能保护软体及资料之完整性受到破坏。举例来说,物件或资料经编码或序列化到一个对攻击者可读写之结构中将导致不安全的反序列化。另一种形式则是应用程式依赖来自于不受信任来源,典藏库及内容递送网路之外挂,函式库或模组。不安全的持续性整合/部署(CI/CD)流程则会造成潜在的未经授权存取,恶意程式码或系统破坏。最后,现在许多应用程式拥有自动更新功能,但自动更新功能在缺乏充足完整性验证功能时就下载并安装更新到处于安全状态下的应用程式。攻击者能上传自制更新档案,更新档案将传播到所有已安装之应用程式并在这些应用程式上执行。
弱点描述
确保不受信任之客户端不会收到未签署或加密之序列化资料并利用完整性检查或数位签章来侦测窜改或重放攻击。
利用数位签章或类似机制确保软体或资料来自预期之提供者
确保函式库及从属套件,例如 npm 或 Maven,是从受信任的典藏库取得。
使用软体供应链安全工具(例如 OWASP Dependency Check 或 OWASP CycloneDX)确保元件没有已知弱点。
- 适当地设定持续性整合/部署(CI/CD)流程的组态及存取控制以确保程式码在组建及部署流程中的完整性。
攻击情境范例
情境 1 不安全的反序列化:一个反应式应用程式呼叫 Spring Boot 微服务。程式设计师们试图确保他们的代码是不可变的。他们的解决方案是在双向所有请求讯息中包含序列化的用户状态。攻击者注意到“R00”Java 物件签章并使用 Java Serial Killer 工具(用来执行 Java 反序列化攻击)在应用程式服务器远端执行程式码。
情境 2 未签署之更新:许多家用路由器、机上盒、装置韧体等未以通过签署之韧体验证更新档案。未签署韧体是越来越多攻击者的目标且情况只会变得更糟。这是一个主要问题,因为只能以新版本修复此机制并期待旧版本自然淘汰,没有其他方法。
情境 3 SolarWinds 恶意更新:众所周知,某些国家会攻击更新机制,最近一次值得注意的是对 SolarWinds Orion 的攻击。该软体开发商拥有安全组建和更新完整性流程。尽管如此,这些流程仍被破坏并在几个月时间中向 18,000 多个组织送出高度针对性的恶意更新,其中大约 100 个组织受到了影响。这是历史上此类性质最深远、最重大的资安事件之一。
延伸--反序列化说明
重点:反序列化不是命令执行,命令执行只是他的一个呈现方式,现在这个点有很多误解
什么是序列化?
序列化是将复杂的数据结构(例如对象及其字段)转换为“更扁平”格式的过程,该格式可以作为顺序的字节流发送和接收。序列化数据使其更容易:
将复杂数据写入进程间内存,文件或数据库
例如,通过网络,在应用程序的不同组件之间或在API调用中发送复杂的数据
至关重要的是,在序列化对象时,其状态也将保留下来。换句话说,将保留对象的属性及其分配的值。
序列化与反序列化
反序列化是将字节流还原为原始对象的完整功能副本的过程,其状态与序列化时的状态完全相同。然后,网站的逻辑可以与此反序列化的对象进行交互,就像与任何其他对象进行交互一样。
许多编程语言为序列化提供本机支持。对象的确切序列化方式取决于语言。某些语言将对象序列化为二进制格式,而其他语言则使用不同的字符串格式,并具有不同程度的人类可读性。请注意,所有原始对象的属性都存储在序列化的数据流中,包括任何私有字段。为防止字段被序列化,必须在类声明中将其显式标记为“ transient”。
请注意,在使用不同的编程语言时,序列化可能被称为封送(Ruby)或酸洗(Python)。
什么是不安全的反序列化?
不安全的反序列化是指网站对用户可控制的数据进行反序列化时。这可能使攻击者能够操纵序列化的对象,以将有害数据传递到应用程序代码中。
甚至有可能用完全不同类的对象替换序列化的对象。令人震惊的是,无论预期使用哪种类,网站上可用的任何类的对象都会被反序列化和实例化。因此,不安全的反序列化有时称为“对象注入”漏洞。
意外类的对象可能会导致异常。但是,到此时,损坏可能已经完成。许多基于反序列化的攻击是在反序列化完成之前完成的。这意味着即使网站本身的功能未与恶意对象直接交互,反序列化过程本身也可以发起攻击。因此,其逻辑基于强类型语言的网站也可能容易受到这些技术的攻击。
不安全的反序列化漏洞如何产生?
通常会出现不安全的反序列化,因为人们普遍缺乏对用户可控制数据进行反序列化的危险程度的了解。理想情况下,用户输入绝对不应反序列化。
但是,有时网站所有者认为它们是安全的,因为他们对反序列化的数据实施了某种形式的附加检查。这种方法通常是无效的,因为几乎不可能实施验证或消毒以解决所有可能的情况。这些检查从根本上来说也是有缺陷的,因为它们依赖于对数据进行反序列化后对其进行检查,在许多情况下,为防止攻击而为时已晚。
由于通常认为反序列化对象是可信任的,因此也可能会出现漏洞。尤其是当使用具有二进制序列化格式的语言时,开发人员可能会认为用户无法有效读取或操纵数据。但是,尽管可能需要付出更多的努力,但攻击者有可能利用二进制序列化的对象,就象利用基于字符串的格式一样。
由于现代网站中存在大量依赖关系,因此基于反序列化的攻击也成为可能。一个典型的站点可能会实现许多不同的库,每个库也都有自己的依赖性。这将创建大量难以安全管理的类和方法。由于攻击者可以创建任何这些类的实例,因此很难预测可以对恶意数据调用哪些方法。如果攻击者能够将一系列意外的方法调用链接在一起,并将数据传递到与初始源完全无关的接收器中,则尤其如此。因此,几乎不可能预料到恶意数据的流动并堵塞每个潜在的漏洞。
简而言之,可以说不可能安全地反序列化不受信任的输入。
不安全的反序列化有何影响?
不安全的反序列化的影响可能非常严重,因为它为大规模增加攻击面提供了切入点。它允许攻击者以有害的方式重用现有的应用程序代码,从而导致许多其他漏洞,通常是远程执行代码。
即使在无法执行远程代码的情况下,不安全的反序列化也可能导致特权升级,任意文件访问和拒绝服务攻击。
如何利用不安全的反序列化漏洞
如何识别不安全的反序列化
无论是白盒测试还是黑盒测试,识别不安全的反序列化都是相对简单的。
在审核期间,你应该查看传递到网站的所有数据,并尝试识别任何看起来像序列化数据的内容。如果你知道不同语言使用的格式,则可以相对轻松地识别序列化数据。
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 十六进制和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属性)实例化对象。绝对不会检查序列化对象的真实性。然后将这些数据传递到条件语句中,在这种情况下,将允许轻松地进行特权升级。
这种简单的情况在野外并不常见。但是,以这种方式编辑属性值说明了访问不安全反序列化所暴露的大量攻击面的第一步。
修改数据类型
我们已经看到了如何修改序列化对象中的属性值,但是也可以提供意外的数据类型。
基于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。
请注意,以任何序列化的对象格式修改数据类型时,务必记住也要更新序列化数据中的任何类型标签和长度指示符,这一点很重要。否则,序列化的对象将被破坏,并且不会被反序列化。
如何防止不安全的反序列化漏洞
一般而言,除非绝对必要,否则应避免对用户输入进行反序列化。在许多情况下,它可能带来的利用的高度严重性以及防范这些利用的难度超过了收益。
如果确实需要反序列化来自不受信任来源的数据,请采用强大的措施以确保数据未被篡改。例如,您可以实施数字签名来检查数据的完整性。但是,请记住,在开始反序列化过程之前,必须进行任何检查。否则,它们几乎没有用。
如果可能,应避免完全使用通用的反序列化功能。这些方法的序列化数据包含原始对象的所有属性,包括可能包含敏感信息的私有字段。相反,您可以创建自己的特定于类的序列化方法,以便至少可以控制公开哪些字段。
最后,请记住,该漏洞是用户输入的反序列化,而不是随后处理数据的小工具链的存在。不要依靠尝试消除在测试过程中发现的小工具链。由于跨库依赖项的网络(几乎肯定存在于您的网站上),尝试全部插入它们是不切实际的。在任何给定时间,公开记录的内存损坏漏洞利用也是一个因素,这意味着您的应用程序可能容易受到攻击。