目录
SSRF 定义
SSRF(Server-Side Request Forgery),即服务器端请求伪造,是一种由攻击者构造形成由服务器端发起的一个安全漏洞。一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。
很多 Web 应用都提供了从其他服务器上获取数据的功能。使用用户指定的 URL,Web 应用可以获取图片,下载文件,读取文件内容等。这个功能如果被恶意使用,可以利用存在缺陷的 Web 应用作为代理攻击远程和本地服务器。
SSRF 原理
服务器提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
SSRF 可以做什么
可以对外网服务器所在的内网,本地进行端口扫描,获取一些服务的 Banner 信息。
攻击运行在内网或本地的应用程序。
对内网 Web 应用进行指纹识别,通过访问默认文件实现。
攻击内外网的 Web 应用。SQL注入,struct2,redis 等。
利用 file 协议读取本地文件等。
SSRF 常见的相关函数
1. file_get_contents
<?php
if(isset($_GET['url'])){
$content = file_get_contents($_GET['url']);
$filename = 'ssrf.jpg';
file_put_contents($filename,$content);
$img = "<img src='".$filename."' />";
}
echo $img ;
?>
2. fsockopen
PHP 官方解释
fsockopen
(PHP 4、PHP 5、PHP 7、PHP 8)
fsockopen —打开 Internet 或 Unix 域套接字连接
说明¶
fsockopen (
string $hostname ,
int $port= -1 ,
int &$error_code=null ,
string &$error_message=null ,
? float $timeout=null
):资源| 错误的
启动到由 指定的资源的套接字连接 hostname。
PHP 支持 Internet 和 Unix 域中的目标,如支持的套接字传输列表中所述 。还可以使用stream_get_transsports()检索支持的传输列表。
默认情况下,套接字将以阻塞模式打开。您可以使用stream_set_blocking()将其切换到非阻塞模式 。
函数stream_socket_client()类似,但提供了更丰富的选项集,包括非阻塞连接和提供流上下文的能力。
参数¶
hostname
如果安装了OpenSSL 支持,您可以hostname 使用ssl://或前缀tls://来使用 TCP/IP 上的 SSL 或 TLS 客户端连接来连接到远程主机。
port
端口号。这可以省略,并跳过 -1对于不使用的端口,比如运输 unix://。
error_code
如果提供,则保存在系统级connect()调用中发生的系统级错误号。
如果返回的值为error_codeis 0而函数返回的是false,则表明错误发生在connect()调用之前 。这很可能是由于初始化套接字时出现问题。
error_message
作为字符串的错误消息。
timeout
连接超时,以秒为单位。
注意:
如果您需要为通过套接字读取/写入数据设置超时,请使用stream_set_timeout(),因为fsockopen()的 timeout参数 仅在连接套接字时适用。
返回值¶
fsockopen()返回一个文件指针,它可以与其他文件函数(例如 fgets()、 fgetss()、 fwrite()、 fclose()和 feof())一起使用。如果调用失败,它将返回false
错误/异常¶
E_WARNING如果hostname不是有效域,则 抛出。
<?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;
}
}
$host = $_GET['host'];
$port = $_GET['port'];
$link = $_GET['link'];
echo GetFile($host,$port,$link);
3.curl_exec()
<?php
if(isset($_GET['url'])){
$link = $_GET['url'];
$curlobj = curl_init();
// 初始化新的会话,返回 cURL 句柄,供curl_setopt()、 curl_exec() 和 curl_close() 函数使用。
curl_setopt($curlobj,CURLOPT_POST,0);
// true 时会发送 POST 请求,类型为:application/x-www-form-urlencoded,是 HTML 表单提交时最常见的一种。
curl_setopt($curlobj,CURLOPT_URL,$link);
// 需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。
curl_setopt($curlobj,CURLOPT_RETURNTRANSFER,1);
// true 将curl_exec()获取的信息以字符串返回,而不是直接输出。
$result = curl_exec($curlobj); // 执行 cURL 会话
curl_close($curlobj);
$filename ='./curled/'.rand().'.txt';
file_put_contents($filename,$result); // 将一个字符串写入文件
echo $result;
}
SSRF 漏洞挖掘与绕过
SSRF 的利用
1)访问正常文件
访问正常的文件,提交参数 ?url=http://www.baidu.com/robots.txt
2)端口扫描(扫描内网的机器的端口)
当访问未开发端口,脚本会显示空白或报错。提交参数 ?url=dict://127.0.0.1:1234
当访问开发端口时,脚本会显示 banner 信息。
提交参数 ?url=dict://127.0.0.1:22 ?url=dict://127.0.0.1:3306 等
3)读取系统本地文件
利用 file 协议可以读取系统本地文件,提交参数
?url=file://C:/windows/system32/......
4)内网 Web 应用指纹识别
识别内网应用使用的框架,平台,模块即 CMS 可以为后续的渗透测试提供很多帮助。大多数 Web 应用框架都有一些独立的文件和目录。提供这些文件可以识别出应用的类型,甚至详细的版本。根据这些信息就可以针对性的搜集漏洞进行攻击。
比如可以提供访问下列文件来判断 phpMyAdmin 是否安装及详细版本。
?url=http://loacalhost/phpmyadmin/README
SSRF 的防御
1.统一错误信息,避免用户可以根据错误信息来判断远程服务器端口状态
2.限制请求的端口为 HTTP 常用的端口,如:80,443,8080,8088等
3.黑名单内网IP
4.禁用不需要的协议,仅允许 HTTP,HTTPS
5.过滤返回信息,验证远程服务器对请求的响应是比较简单的方法。