SSRF漏洞
SSRF漏洞
基础知识
概述
SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统(正因为请求是由服务端发起的,所以服务端能请求到与自身相连而与外网隔离的内部系统)
大白话:利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网进行攻击
主要攻击方式
当攻击者想要访问服务器B上的服务,但是由于存在防火墙或者服务器B是属于内网主机等原因导致攻击者无法直接访问。如果服务器A存在SSRF漏洞,这时攻击者可以借助服务器A来发起SSRF攻击,通过服务器A向主机B发起请求,达到攻击内网的目的
示例:
漏洞场景:某网站有一个在线加载功能可以把指定的远程文章加载到本地,链接如下:
1 | http://www.xxx.com/article.php?url=https://blog.csdn.net/qq_43531669/article/details/112498646 |
假如系统没有对url参数进行任何的检查,就可以构造其他的请求,例如:
1 | http://www.xxx.com/article.php?url=http://127.0.0.1:22 |
原理
SSRF漏洞形成的原因大都是由于服务端提供了从其他服务器获取数据的功能但没有对目标地址做过滤与限制。攻击者可以利用改漏洞获取内部系统的一些信息(因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内网系统)
很多网站提供了从其他的服务器上获取数据的功能。通过指定的URL,网站可以从其他地方获取图片、下载文件、读取文件内容等。
SSRF的实质就是利用存在缺陷的Web站点作为代理攻击远程和本地的服务器。
危害
- 对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息。
- 攻击运行在内网或本地的应用程序。
- 对内网Web应用进行指纹识别,识别企业内部的资产信息。
- 攻击内外网的Web应用,主要是使用HTTP GET请求就可以实现的攻击(比如struts2、SQli等)。
- 利用file、gopher、dict协议读取本地文件、执行命令等
http://localhost:6379/
(Redis 默认端口)
http://127.0.0.1:3306/
(MySQL 默认端口)
http://192.168.0.1:8000/
(常见的内网 Web 服务端口)
漏洞检测
漏洞验证
- 通过抓包分析发送的请求是否是由服务器端发送的来判断是否存在SSRF漏洞。
- 在页面源码中查找访问的资源地址,如果该资源地址类型为
http://www.xxx.com/a.php?image=地址
就可能存在SSRF漏洞
漏洞可能出现点
分享功能
1 | http://share.xxx.com/index.php?url=http://www.xxx.com |
通过url参数的获取来实现点击链接的时候跳到指定的分享文章
图片加载/下载
1 | http://image.xxx.com/image.php?image=http://www.xxx.com |
图片加载存在于很多的编辑器中,编辑器上传图片处加载设定好的远程服务器上的图片地址
图片/文章收藏功能
1 | http://title.xxx.com/title?title=http://title.xxx.com/xxx |
例如 title参数是文章的标题地址,代表了一个文章的地址链接,如果收藏功能采用了此种形式保存文章,则在没有限制参数的形式下可能存在SSRF
转码服务
通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
在线翻译
给网址翻译对应网页的内容
邮件系统
比如接收邮件服务器地址
从URL关键字中寻找
share
、wap
、url
、link
、src
、source
、target
、u
、3g
、display
、sourceURl
、imageURL
、domain
绕过
部分存在漏洞,或者可能产生SSRF的功能中做了白名单或者黑名单的处理,来达到阻止对内网服务和资源的攻击和访问。因此想要达到SSRF的攻击,需要对请求的参数地址做相关的绕过处理
常见限制
1.限制为
http://www.xxx.com
域名
采用http基本身份认证的方式绕过。即@http://www.xxx.com@www.xxc.com
2.限制请求IP不为内网地址
当不允许ip为内网地址时
(1)采取短网址绕过
(2)采取特殊域名
(3)采取进制转换
3.限制请求只为http协议
(1)采取302跳转
(2)采取短地址
绕过限制为某种域名
@绕过
当网站限制只能访问 http://www.xxx.com
类型的域名时,可以采用http基本身份认证的方式绕过
如:http://www.xxx.com@www.xxc.com
在对@解析域名中,不同的处理函数存在处理差异,例如:
1 | http://www.aaa.com@www.bbb.com@www.ccc.com |
在PHP的parse_url中会识别 www.ccc.com
,而libcurl则识别为 www.bbb.com
要求URL中必须包含
http://notfound.ctfhub.com
,来尝试利用URL的一些特殊地方绕过这个限制吧构造payload:
1 ?url=http://notfound.ctfhub.com@127.0.0.1/flag.php如果要求以
http://notfound.ctfhub
开头.com 结尾的话,依旧可以使用@payload
1 ?url=http://notfound.ctfhub@127.0.0.1/flag.php.com或者:
1 ?url=http://ctf.@127.0.0.1/flag.php#show此类需要某某开头 某某结束的题目均可使用@进行绕过
绕过限制请求IP不为内网地址
利用句号
1 | 127。0。0。1 >>> 127.0.0.1 |
利用[::]
可以利用[::]
来绕过localhost
1 | http://[::]:80/ >>> http://127.0.0.1 |
添加端口号
1 | http://127.0.0.1:8080 |
利用短网址
使用解析到内网的域名
如果服务端没有先解析IP再过滤内网地址,我们就可以使用localhost等解析到内网的域名。
另外 xip.io 提供了一个方便的服务,这个网站的子域名会解析到对应的IP,例如192.168.0.1.xip.io,解析到192.168.0.1
xip.io 是一个通配符 DNS 解析服务,它可以将特定格式的域名解析为任意 IP 地址,常用于开发和内网测试
采用进制转换
若对IP进行正则过滤,可以通过改写IP形式来绕过
对于这种过滤我们采用改编IP的写法的方式进行绕过,例如127.0.0.1
这个IP地址可以被改写成:
-
十进制整数表示
2130706433
八进制表示
0177.0.0.01
十六进制表示
0x7F000001
混合进制
127.0x0.1
127.1
0x7f.1
访问改写后的IP地址时,Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作。
另外,0.0.0.0
这个IP可以直接访问到本地,也通常被正则过滤遗漏
302跳转
限制请求只为http协议时使用
限制请求只为http协议
采用302跳转
采用短地址
回环地址绕过
回环地址不止127.0.0.1
,这便有了很大的操作空间
1 | url=http://0/flag.php |
利用URL伪协议
Gopher协议(万能协议)
gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再在一个url参数中构造POST或者GET请求,从而达到攻击内网应用的目的。例如可以使用gopher协议对与内网的Redis服务进行攻击
file协议
读取文件很好用
在有回显的情况下,利用 file 协议可以读取任意内容
file://var/www/html/flag.php
dict协议
dict 协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源,能用来探测安装软件版本信息,查看端口,操作内网redis服务
协议格式:dict://<host>:<port>/<dict-path>
一般用dict://<host>:<port>/info
探测端口应用信息
1 | dict://127.0.0.1:6379 //探测redis是否存活 |
漏洞利用
产生漏洞的函数
SSRF涉及到的危险函数主要是网络访问,支持伪协议的网络读取
file_get_contents()
file_get_contents
函数可以对本地文件进行读取,也可以对远程文件进行读取
条件
PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8
利用
1 |
|
这个函数的作用是将整个文件读入一个字符串中,并且此函数是用于把文件的内容读入到一个字符串中的首选方法
比如:下面的代码执行结果是输出test.txt
文件里面的字符串
1 |
|
fsockopen()
条件
PHP 4, PHP 5, PHP 7, PHP 8
利用
1 |
|
fsockopen
函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限
curl_exec()
条件
PHP 4 >= 4.0.2, PHP 5, PHP 7, PHP 8
利用
1 |
|
curl_exec
函数用于执行指定的cURL会话
1.一般情况下PHP不会开启fopen的gopher wrapper
2.file_get_contents
的gopher协议不能URL编码
3.file_get_contents
关于Gopher的302跳转会出现bug,导致利用失败
4.curl/libcurl 7.43 上gopher协议存在bug(%00截断) 经测试7.49 可用
5.curl_exec()
//默认不跟踪跳转,
6.file_get_contents() // file_get_contents
支持php://input
协议
步骤
把url中的内容改成内网的其他服务器上地址和端口,探测内网信息,比如端口开放情况
1 | ?url=http://127.0.0.1:22 |
再打开Burp,抓包发到Intruder,设置Payload(端口处)
爆破,检测到了内网主机的80端口(例子)是开放的
也可以通过SSRF漏洞读取内网服务器的文件,例如将url修改为
1 | ?url=file:///c:/windows/system.ini |
防御SSRF
- 禁止跳转
- 禁用除http和https外的协议,如:
file://
、gopher://
、dict://
等。 - 限制请求的端口为http常用的端口,如 80、443、8080。
- 统一错误信息,避免用户可以根据错误信息来判断远程服务器的端口状态。
- 对请求地址设置白名单或者限制内网IP,以防止对内网进行攻击。
参考: