继续介绍一些 Web hacking 相关的漏洞。
IDOR
IDOR (Insecure direct object reference),不安全的对象直接引用,这是一种访问控制漏洞。
当 Web 服务器接收到用户提供的输入来检索对象时 (包括文件、数据、文档),如果对用户输入数据过于信任,Web 服务器没有对输入进行验证以确认所请求的对象属于提出请求的用户,就会出现这种类型的漏洞。
比如有如下 URL,其对应页面如下所示:
该页面展示的是对应 id 的订单信息,此处 id 为 1234,在 URL 中也有所体现。
此时将 URL 改为 1000,正常来说,应该无法访问到,因为 id 为 1000 的订单并非由我们生成。但是由于该 Web 服务器无条件信任用户输入,它返回了 id 为 1000 的订单信息:
基于上述例子,我们可以知道 URL 中传输的数值很可能是 IDOR 漏洞的入口。有些时候,这些数值会经过编码,如 Base64 编码;有些时候,这些数值会经过 Hash 处理。面对这种数值,想验证是否存在 IDOR,只需要反推出原始数值后,修改,再进行编码或 Hash 监控页面响应即可。
更特殊的情况是,我们无法反推出原始数值。那么我们可以创建两个账户,将这些账户对应的数值进行交换。如果可以成功访问到另一个账户的个人信息,则可以说明存在 IDOR 漏洞。
当然,前面提到的都是出现在 URL 中的漏洞入口,实际上 IDOR 也可能出现在其他地方,只要有参数传递且和用户信息有关的地方,都可能存在 IDOR 漏洞。
File inclusion
文件包含 包括了 本地文件包含 (Local file inclusion)、远程文件包含 (Remote file inclusion) 和 目录遍历 (directory traversal)。
当我们向 Web 服务器请求一个文件时,它的运行逻辑可能是这样的:
然而,如果 Web 服务器对用户输入完全信任,从不验证,那么用户就可以传递任意输入,从而导致漏洞发生。
文件包含可能会导致代码、凭证或其他重要文件泄露,此外如果攻击者可以通过其他渠道向 Web 服务器上传文件的话,那么就可以利用该文件与 文件包含 漏洞形成联动,以获得 RCE 漏洞 (Remote command execution)。
目录遍历
目录遍历 也被称为 路径遍历,它允许攻击者操作 Web 应用程序的 URL 来定位和访问那些存储在应用程序根目录之外的文件或目录。
在 PHP 语言中,用于读取文件内容的函数有许多,比较常见的就是 file_get_contents 函数。但是该函数不是引起目录遍历的主要原因,主要原因是未对输入进行验证或过滤。
前面提到过正常访问 Web 应用中的文件过程图,假如未对用户输入进行过滤和验证,用户就可以利用 目录遍历 漏洞来读取其他文件:
如上图所示,用户在本应接收文件的参数后面构造了 "../../../../etc/passwd" 字符串。这表示 PHP 中的文件读取函数将从当前目录开始,往上级目录跳 4 次,然后获取 /etc 文件夹中的 passwd 文件。
下面是 操作系统 中常见的重要文件路径表:
|-----------------------------|-------------------------------------------------------|
| Location | 描述 |
| /etc/issue | 记录了在登陆提示符前打印的信息或系统标识 |
| /etc/profile | 控制全系统默认变量,如 导出变量、文件创建掩码 (umask)、终端类型、用于显示新邮件到达时间的邮件信息 |
| /proc/version | 指定了 Linux 内核版本 |
| /etc/passwd | 拥有访问系统权限的所有注册用户 |
| /etc/shadow | 包含系统用户密码信息 |
| /root/.bash_history | 记录了 root 用户的命令历史记录 |
| /var/log/dmessage | 包含全局系统信息,包括系统启动时记录的信息 |
| /var/mail/root | 所有给 root 用户的邮件 |
| /root/.ssh/id_rsa | 服务器上 root 用户或任何已知有效用户的 SSH 私钥 |
| /var/log/apache2/access.log | 访问 Apache 服务的请求数 |
| C:\boot.ini | 包含计算机 BIOS 固件的启动选项 |
本地文件包含 (LFI)
本地文件包含 漏洞通常是由于开发人员缺乏安全意识导致的 。在 PHP 中,容易引起该漏洞的函数有 include、require、include_once、require_once等函数,这些函数的作用一般都是将已有文件插入到当前文件中,可以方便开发人员复用文件。在此重点讨论 PHP 中的 LFI 漏洞,当然这不代表使用其他语言时不会出现 LFI 漏洞。
情况 1
在 index.php 中,有这样一串代码:
php
<?PHP
include($_GET["lang"]);
?>
它表示从 GET 请求中获取 lang 参数,并将参数中的文件包含进来。比如,当访问 index.php?lang=EN.php 时,就会将英文版的页面返回回来;而如果访问 index.php?lang=CN.php 则会返回中文版的页面。
此时有攻击者构造了这样的 URI :index.php?lang=/etc/passwd ,由于开发人员未对输入做任何过滤,也未在 include 函数中指定包含目录,此时,/etc/passwd 文件就被包含了进来:
情况 2
开发人员在栽了个跟头后,在 include 函数中指定了目录:
php
<?PHP
include("languages/". $_GET['lang']);
?>
这样,就只能访问 languages 目录下的不同语言的页面了。事实是,虽然有用但不多。
攻击者发现直接传入 index.php?lang=/etc/passwd 没用了,于是再构造了一个 URI:index.php?lang=../../../../etc/passwd。
攻击者又一次得逞了。
情况 3
在前两个情况中,我们一直在进行白盒测试。假如现在我们进行黑盒测试,这种情况下,错误信息就十分重要。
现在我们只能得到如上的报错信息。根据这个报错,我们可以推断出:
- include 函数指定了路径 /includes,且添加了 .php 后缀。
- 当前 Web 应用的根目录为 /var/www/html
对于第 2 个推断,我们可以依然使用 ../../../../etc/passwd 来利用。但是由于这次,它不仅指定路径,还指定了后缀名:
在 PHP 5.3.4 以下版本,可以使用 空字节 进行截断 (如 URL 编码中的 %00 和 十六进制中的 0x00)。它的原理在于,%00 和 0x00 都会被解析为 NULL,而当一个字符串在解析时出现 NULL 时,NULL 后的字符就会被丢弃。
情况 4
依然黑盒测试。现在倒好,/etc/passwd 这一文件被过滤了。这种情况有两种方法解决:
- %00 截断。只不过有 PHP 版本要求。
- 在文件名后加 /. 符号
第一个方法前面说过,重点讲讲第二个方法。
以 cd 命令为例,如果我们输入 cd ..,它会将我们带到上一个目录中;而如果输入 cd .,则会停留在原目录中。同样的,如果我们输入 /etc/passwd/..,它会跳到 /etc 目录中;而如果我们输入 /etc/passwd/.,它依然会停留在原目录,并且匹配不上 /etc/passwd 这一字符串。
情况 5
依然黑盒。现在开发人员过滤掉了 ../ 字符串,其将 ../ 替换为了空字符串:
我们可以尝试使用 双写法绕过:....//....//....//....//etc/passwd。双写法 原理如下图所示:
如果在过滤时只过滤一次,那么我们写两次时,就只会过滤掉其中一次,剩下那次依然可以正常保留。
经过 双写法 之后,得到结果:
情况 6
依然黑盒。现在在输入时必须给上指定的文件夹:
这种解法也算是简单,只需构造出如下 URI:THM-profile/../../../../etc/passwd 即可:
远程文件包含 (RFI)
远程文件包含 是可以在 Web 应用中包含远程文件的漏洞。与 LFI 相似,RFI 也是未对用户输入进行过滤和验证时发生的,它允许攻击者在函数中注入外部 URL。RFI 的一个前提是:allow_url_fopen选项必须开启。
RFI 的风险高于 LFI,因为 RFI 漏洞允许攻击者在服务器上实现 RCE。除此之外,RFI 攻击成功的后果还有:
- 敏感信息泄露
- 跨站脚本攻击 (Cross-site scripting, XSS)
- 拒绝服务攻击 (Denial of service, DoS)
要成功实施 RFI,攻击者的服务器必须可以同目标 Web 服务器通信。攻击者在其服务器上托管恶意文件,然后通过 HTTP 请求将恶意文件注入至对应函数中,并在目标 Web 应用上执行恶意文件。
下图展示了 RFI 的利用过程:
修复
关于 文件包含 漏洞的修复建议如下:
- 将系统和服务(包括 Web 应用框架)升级为最新版本
- 关闭 PHP 报错,以避免泄露 Web 程序的路径和其他信息
- 配置 WAF (Web application firewall,网络应用程序防火墙)
- 若 Web 应用不需要远程调用文件的功能的话,就将它禁用。如 allow_url_fopen 和 allow_url_include
- 仔细分析 Web 应用,只允许使用需要的协议和 PHP 函数
- 不要相信用户的输入,并确保进行适当的输入验证
- 对文件名和路径设置黑白名单管理
挖掘 LFI 漏洞
碰到一个 Web 应用时,要通过什么步骤发现存在 LFI 漏洞呢?
- 找到一个入口点,可以是 GET、POST、Cookie 甚至 HTTP 头
- 输入有效的输入,查看 Web 服务器的运行情况
- 输入无效的输入,比如特殊字符或常用文件名。在输入无效输入后查找错误,以披露 Web 应用的当前路径;如果没有错误,那么试错可能是最好的选择
- 了解是否有输入验证或是否有任何过滤
- 尝试注入有效条目以读取敏感文件
Challenge
一
上面写着输入表单崩溃了,只能通过 POST 请求发送 file 参数才可以正常运行。
那就直接 Burpsuite 抓包,转换请求方式,发送,得到 flag:
二
本关提示只有 admin 才可以访问该页面。通过上面的抓包可以发现,Cookie 中有一个字段 THM 取值为 Guest,将其改为 admin 会返回什么结果呢:
可以看到,修改 Cookie 是有效的,那么就猜测后端是接收 Cookie 后,将值对应的文件包含起来。现在将 Cookie 使用 /etc/passwd 进行测试:
与我们的猜想大致,通过报错可以推断出它对输入进行了处理,限制了文件夹以及添加了后缀名。还可以注意到 PHP 版本为 5.2,因此可以使用 %00 截断来绕过后缀名。而限制文件夹则可以使用 ../ 来绕过。
这样我们就构造了一个 URI:../../../../etc/flag2 来获取当前关卡的 flag:
三
这一关没有提示,只有这样的输入框。我们直接输入 /etc/flag3 来看看返回结果:
从报错中可以推断出:
- 给输入指定了后缀名。
- 过滤了 / 和 数字。
- PHP 使用版本为 5.2
此处没有指定文件夹名,只限制了后缀名,故可以直接使用 %00 截断。主要问题是过滤了 / 和 数字,第一时间尝试使用 双写法 绕过,发现失效了:
看来这次过滤不止一次。看来输入框是比较难弄了。在抓包时,我们发现 Cookie 依然有 THM 字段,因此尝试能不能通过 Cookie 利用漏洞。
没有反应。那就尝试更换请求方式:
可以看到使用 POST 请求后,/ 和 数字都没有被过滤了。但是居然没有直接显示结果,看来还是有指定文件夹限制,只是没有展示出来吧。因此构造一个 URI:../../../etc/flag3%00 即可:
四
本关卡提示说,要利用 RFI 漏洞实现 RCE,获取到目标的 hostname。
那基本思路就是在攻击方写一个 PHP 脚本获取 hostname:
然后搭建一个 python 的轻型 HTTP 服务来让目标 Web 服务器访问。
这样就可以获得目标机的 hostname。
Intro to SSRF
SSRF (Server-side request forgery),服务端请求伪造。这是一个允许恶意用户让 Web 服务器向攻击者选择的资源发出额外或经过编辑的 HTTP 请求的漏洞。
SSRF 有两种类型,一种是常规 SSRF,它可以将数据返回到攻击者的屏幕上;另一种是盲 SSRF,即没有信息返回到攻击者屏幕上。
SSRF 会导致:
- 访问未经授权的区域
- 访问 客户/组织 的数据
- 扩展至内网的能力
- 揭示身份验证令牌/凭据
Example
如上图所示,这显示了攻击者如何完全控制 Web 服务器请求页面。Expected Request 是 Web 服务器希望接收的内容,红色部分则是网站用于获取信息的 URL。攻击者可以通过将红色区域修改为他们想要的 URL 来实现 SSRF。
上图展示了攻击者如何使用目录遍历控制路径访问指定 URL。当 website.thm 接收到 ../ 这样的信息时,意味着要向上移动一个目录,就会删除请求中的 /stock 部分,并将最终请求转换为 /api/user。
在上图中,攻击者可以控制请求的子域名。注意此处以 &x 为结尾,它可以阻止剩余路径内容附加到攻击者 URL 末尾,并将剩余路径内容作为查询字符串上的参数。
回归到原始请求上,即攻击者可以强制 Web 服务器请求攻击者选定的服务器。通过此种方式,我们可以捕获到发送给攻击者指定域名的请求头。这些请求头种可能包含由 website.thm 发送的认证凭据或 API 密钥。
下面是一次简单展示:
本题中,攻击者可以控制请求的子域名,flag 就在 https://server.website.thm/flag?id=9 这个 URL 中。这种情况如前面所示,我们直接访问在可控制输入的地方输入 URL,并在后面加上 &x 参数。
注意最底下的那一行小字,它表示了当前正在请求的 URL。
按下回车,flag 返回回来。
Finding SSRF
- 当完整的 URL 作为参数显示在地址栏中:
- 表单的隐藏字段中:
- 部分 URL,比如 主机名:
- 只有路径的 URL:
面对可能存在 SSRF 漏洞的地方,我们需要反复试验才能找到有效负载。如果使用的是盲 SSRF,其输出不会反馈在屏幕上,因此需要使用外部 HTTP 日志工具来监控请求,比如 requestbin.com,又或者是 我们自己的 HTTP 服务器 或 BurpSuite 的 Collaborator 客户端。
Defeating common SSRF defenses
意识到 SSRF 漏洞风险的且安全意识较高的开发人员可能会在 Web 应用中实施检查,以确保请求的资源符合特定规则。通常有三种方法:
Deny list
拒绝列表 指除了列表中指定的资源或与特定模式匹配的资源外,所有请求都被接受。Web 应用可以使用拒绝列表保护 敏感端点、IP地址 或 不被公众访问的域,同时仍允许访问其他位置。限制访问的一个特定端点就是 localhost,它可能会包含服务器性能数据或更多敏感信息。但攻击者可通过使用其他 localhost 引用 (如 0、0.0.0.0、0000、127.1、127.*.*.*、2130706433、017700000001)或具有解析到 IP 地址 127.0.0.1 的 DNS 记录 (如 127.0.0.1.nip.io)的子域来绕过拒绝列表。
此外在云环境中,阻止对 IP 地址 169.254.169.254 也是有用的。因为该 IP 地址包含已部署云服务器的元数据,其中可能包含敏感信息。攻击者可以通过在自己的域上注册一个子域,并将 DNS 记录指向 IP 地址 169.254.169.254,从而绕过这一限制。
Allow list
允许列表 指除了出现在列表中或符合特定模式匹配的资源外,所有请求都被拒绝。比如参数中使用的 URL 必须以 https://website.thm 开头的规则,那么攻击者可以在其自己的域名上创建一个子域(如 https://website.thm.attackers-domain.thm)来快速规避这一规则。
Open redirect
如果上述的方法不起作用,还可以使用 开放式重定向 (Open redirect)。
开放式重定向 是服务器上的一个端点,网站访问者会被自动重定向到另一个网站地址,比如 https://website.thm/link?url=https://tryhackme.com。创建这种端点是为了记录访问者点击该链接的次数,以用于广告/营销目的。
但假如这里面存在一个潜在的 SSRF 漏洞,且其严格的规则只允许以 https://website.thm/ 开头的 URL,那么攻击者可以利用上述功能将内部 HTTP 请求重定向到攻击者选择的域上。
Practical
现在有一个网站页面,它有两个目录。一个是 /private 目录,但它无法在当前 IP 地址上被访问:
另一个是 /customers/new-account-page,这是新版的用户账户页面,允许用户选择头像:
首先我们查看这些图像的信息,通过开发者工具可以看到图像的字段中包含了它们的路径:
而当某一图像被选择为头像时,它就用 data URI 显示了,其中使用了 Base64 编码:
现在我们尝试修改图片路径为 /private:
然后把这个修改过的图片更新为自己的头像后发现:
URL 不能以 private 开始。那我们就不以 private 开始,同时最后得到的就是 private 中的内容,即构造一个 URL 为 x/../private:
再次将修改后的图片更新为头像,发现一串 base64 编码:
将其解码后,得到 flag: