1.什么是SSRF?

SSRF(Server-Side Request Forgery,服务器端请求伪造) 是一种网络攻击,攻击者通过欺骗服务器,使其向本不该访问的内部或外部资源发出 HTTP 请求。这种攻击允许攻击者间接利用服务器发起请求,绕过防火墙、访问内网系统,甚至读取本地资源。

SSRF 攻击的基本概念:
在 SSRF 攻击中,攻击者并不直接访问目标资源,而是通过服务器充当代理来发起请求。通常,服务器会处理来自客户端的 URL、IP 地址或其他资源的请求,而 SSRF 攻击者则通过修改这些请求参数,使服务器访问攻击者指定的内部或外部资源。

SSRF 攻击的典型场景:
文件上传或下载功能:服务器接受文件的 URL 或者文件路径,然后下载或处理该文件。
URL 或 API 调用:服务器接受来自客户端的 URL 并发起相应的请求,如获取远程 API 数据。
图片加载或资源获取:服务器从 URL 获取图片或其他资源并返回给用户。

SSRF 攻击示例:

  1. 内网探测: 通过 SSRF,攻击者可以让服务器访问内网中的敏感资源(如内部管理系统、数据库服务等),这通常是外部攻击者无法直接访问的。
    示例:
http://example.com/page?url=http://127.0.0.1/admin

攻击者将 URL 参数修改为内部 IP 地址 127.0.0.1,服务器被欺骗去访问本地的管理页面。

  1. 获取敏感信息: 攻击者通过 SSRF 访问内部的 API 或者云服务的元数据服务器,可能获取敏感的配置信息。

示例: 在 AWS(亚马逊云服务)中,元数据服务器通常位于 http://169.254.169.254/latest/meta-data/,可以泄露云主机的关键信息,如临时凭证。

http://example.com/page?url=http://169.254.169.254/latest/meta-data/
  1. 跨站请求伪造(CSRF)结合 SSRF: SSRF 还可以与 CSRF 攻击结合使用,攻击者可以利用服务器发起对外部或第三方系统的恶意请求。

SSRF 攻击的影响:

  1. 访问内部网络资源:攻击者可以通过服务器访问原本受限的内部网络资源,包括数据库、管理界面等。
  2. 数据泄露:服务器可能被用于访问内部系统或云基础设施的敏感信息,比如配置信息、数据库内容等。
  3. 执行恶意操作:通过特定的请求,攻击者可以操控服务器发送请求,从而执行某些恶意操作(例如,修改数据、执行远程代码等)。

SSRF 的常见绕过手段:
攻击者通常会使用一些绕过方法来避免服务器的内网 IP 检查,比如:

  1. 使用 DNS 解析绕过:攻击者使用一个恶意的域名,它解析为内网 IP 地址,从而绕过直接的 IP 检查。
  2. 多次重定向:攻击者让服务器跟随 URL 重定向,从外部 URL 重定向到内网地址,绕过简单的 URL 白名单过滤。
  3. 伪造 IP 地址:通过伪造不同的 IP 格式(如整数形式的 IP 地址),绕过内网 IP 检查。、

2.解题思路

首先我们来看一下源代码:

<?php 
highlight_file(__FILE__);
function check_inner_ip($url) 
{ 
    $match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url); 
    if (!$match_result) 
    { 
        die('url fomat error'); 
    } 
    try 
    { 
        $url_parse=parse_url($url); 
    } 
    catch(Exception $e) 
    { 
        die('url fomat error'); 
        return false; 
    } 
    $hostname=$url_parse['host']; 
    $ip=gethostbyname($hostname); 
    $int_ip=ip2long($ip); 
    return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16; 
} 

function safe_request_url($url) 
{ 
     
    if (check_inner_ip($url)) 
    { 
        echo $url.' is inner ip'; 
    } 
    else 
    {
        $ch = curl_init(); 
        curl_setopt($ch, CURLOPT_URL, $url); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
        curl_setopt($ch, CURLOPT_HEADER, 0); 
        $output = curl_exec($ch); 
        $result_info = curl_getinfo($ch); 
        if ($result_info['redirect_url']) 
        { 
            safe_request_url($result_info['redirect_url']); 
        } 
        curl_close($ch); 
        var_dump($output); 
    } 
     
} 

$url = $_GET['url']; 
if(!empty($url)){ 
    safe_request_url($url); 
} 

?>

check_inner_ip 通过 url_parse 检测是否为内网 ip 。
如果满足不是内网 ip ,通过 curl 请求 url 返回结果。

根据【Blackhat】SSRF的新纪元:在编程语言中利用URL解析器提供的思路,url_parse 与curl的结果出现了差异:
http://foo@evil.com@google.com/
当 php_url_parse 认为 google.com 为目标的同时,curl 认为 evil.com:80 是目标。
在这里插入图片描述
根据网上的资料,parse_url 和 cURL 是 PHP 中的两个不同功能,用于处理 URL,但它们的用途和处理方式有所不同。下面是它们的主要区别:

  1. parse_url
    功能:
    parse_url 是一个用于解析 URL 字符串的函数。它将一个完整的 URL 拆分为不同的组成部分,如协议、主机、端口、路径、查询参数等。
    使用示例:
$url = "http://www.example.com/path?arg=value#anchor";
$parsed = parse_url($url);
var_dump($parsed);

输出:

array(5) {
  ["scheme"] => string(4) "http"
  ["host"] => string(11) "www.example.com"
  ["path"] => string(5) "/path"
  ["query"] => string(8) "arg=value"
  ["fragment"] => string(6) "anchor"
}

目的:
主要用于从 URL 中提取和分析信息。它不会发起任何网络请求。

  1. cURL
    功能:
    cURL 是一个强大的库,用于发送 HTTP 请求和处理响应。它支持多种协议(HTTP、HTTPS、FTP等)和多种选项(如设置请求头、处理重定向、发送数据等)。
    使用示例:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.example.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;

目的:
主要用于与外部服务进行网络交互,获取数据或发送请求。它能够处理请求和响应,但不会解析 URL。

主要区别

  1. 功能性:
    parse_url 主要用于解析和分析 URL,而 cURL 用于实际的网络请求。

  2. 返回值:
    parse_url 返回一个数组,包含 URL 的各个部分;cURL 返回请求的响应数据或状态。

  3. 操作对象:
    parse_url 不涉及网络交互;cURL 则是与网络进行通信的工具。

  4. 用法场景:
    使用 parse_url 来获取 URL 的结构信息,例如提取主机或路径;
    使用 cURL 来获取或发送数据,处理 HTTP 请求和响应。

理解了后我们构造payload就容易了:
Payload 1:

http://foo@127.0.0.1:80 @www.baidu.com/flag.php

Payload解析:
在 URL 中使用两个 @ 符号实际上是利用了 URL 的标准格式和解析规则,来达到绕过防护机制的目的。让我们详细分析一下这两个 @ 符号的作用及其在 SSRF 攻击中的角色。

  1. URL 标准格式
    根据 URL 规范(RFC 3986),URL 的基本结构如下:
    scheme://username:password@host:port/path?query#fragment
    其中,username:password@ 是可选的,用来提供访问主机的身份认证信息(即在访问时指定用户名和密码)。常见的例子包括:
    http://user:pass@host/path
    这里的 user:pass 是用于访问 host 的认证信息。
    在这种情况下,@ 符号用于分隔用户认证信息和主机名,解析时只使用 user:pass 作为认证,实际访问的仍然是 host。

  2. 第一个 @ 的作用
    在构造的 payload http://foo@127.0.0.1:80@www.baidu.com/flag.php 中,第一个 @ 符号的作用是:
    把 foo作为访问 127.0.0.1:80 的认证信息。
    foo 是用户名,URL 的前半部分看起来像 http://foo@127.0.0.1:80,这意味着向 127.0.0.1 的请求是使用 foo 作为认证用户名。
    由于 username:password@ 是标准的 URL 格式,这部分通常不会触发异常。

  3. 第二个 @ 的作用
    第二个 @ 符号的作用是:
    在某些情况下,URL 解析器会忽略第二个 @ 后面的部分,将它作为路径或注释的一部分,而不影响请求的目标主机。
    根据 URL 标准,第二个 @ 后面的 www.baidu.com/flag.php 实际上会被解析为请求的路径(即 /flag.php),而不会真正改变主机名。
    因此,即使你在 URL 中加入 www.baidu.com,它也不会改变实际请求的主机名,主机仍然是 127.0.0.1。

  4. 实际请求结果
    URL 解析器会将整个 URL http://foo@127.0.0.1:80@www.baidu.com/flag.php 解析为:
    主机:127.0.0.1
    端口:80
    认证:foo
    路径:/flag.php
    这样,最终发出的请求仍然是访问本地 127.0.0.1 上的 /flag.php,而不是 www.baidu.com。

  5. 为什么能获取到 flag?
    通过构造双 @ 符号的 URL,攻击者能够:
    误导服务器,使其访问 127.0.0.1 而不是外部的 www.baidu.com。
    绕过内网访问限制,因为服务器自身访问 127.0.0.1 时可能不会受到防火墙的限制,直接访问内部的 /flag.php 文件。
    如果本地存在 /flag.php 并且没有严格的访问控制,服务器就会返回 flag,成功触发 SSRF 攻击。

  6. 总结
    第一个 @:用于指定用户名(如 foo)和主机(如 127.0.0.1),符合 URL 规范。
    第二个 @:利用 URL 解析机制,欺骗服务器忽略 www.baidu.com 并继续访问指定的主机(即 127.0.0.1),从而绕过外部主机的验证。

这种双 @ 技巧在 SSRF 攻击中非常有效,因为它能够混淆 URL 解析器的工作逻辑,导致服务器错误地访问内部资源。

Payload 2:

http://127.0.0.1/flag.php

在一个典型的 SSRF(Server-Side Request Forgery)攻击中,http://127.0.0.1/flag.php 能够成功获取到 flag 通常是因为以下几个原因:

  1. 127.0.0.1 是本地回环地址
    127.0.0.1 是本地回环地址,表示访问的是服务器自身。通常情况下,应用程序的某些资源或服务可能只在本地服务器上暴露,并且无法通过外部网络访问。
    举例:
    某些服务(如数据库、管理面板、或调试接口)会绑定在 127.0.0.1 上,因为它们不希望被暴露到外部网络,认为这些内部服务是安全的,只有本地系统可以访问。
    然而,在 SSRF 攻击中,攻击者可以通过服务器发起请求,访问这些本地服务,并获取内部资源。例如:
    /flag.php 文件可能在服务器的文件系统上可访问,且只有本地请求能访问到它。

  2. 内网资源的暴露
    在很多应用中,/flag.php 或类似的敏感资源仅对内网或本地访问开放,不会对外网用户公开。例如:
    竞赛环境(CTF)中的 Flag 可能被放置在一个本地可访问的路径中,且这些资源通常不会公开对外。
    当服务器发出请求访问 127.0.0.1/flag.php 时,由于请求来自服务器自身,它能够绕过防火墙或权限限制,直接获取到敏感信息。

  3. 缺乏对本地请求的限制
    一些服务器内部没有对本地请求进行足够的访问控制,因此即使 /flag.php 是敏感文件,只要请求是从本地发出的,服务器可能会直接返回文件内容。例如:
    本地的 /flag.php 文件可能通过服务器应用返回敏感的 flag 值,原本是给管理员或内部服务使用的。
    攻击者通过 SSRF,可以利用服务器来替他们发送请求访问这些文件,从而获得敏感数据。

  4. 服务的信任模型
    许多系统内部服务、API 或应用依赖于信任模型,即假设来自本地的请求是可信的。例如:
    内部系统可能通过 127.0.0.1 或 localhost 来提供管理功能或调试接口,而这些接口默认不会进行强认证,因为假设只有管理员能从本地访问这些接口。
    SSRF 攻击绕过了这种信任模型,使得攻击者可以伪装成本地用户,发起对本地资源的请求。
    http://127.0.0.1/flag.php与:http://foo@127.0.0.1:80 @www.baidu.com/flag.php的区别在哪里?
    http://127.0.0.1/flag.php 和 http://foo@127.0.0.1:80@www.baidu.com/flag.php 之间的区别主要体现URL 解析方式、使用 @ 符号的目的,以及可能的攻击手法。让我们分别分析两者的不同点:

  5. 直接访问本地资源:http://127.0.0.1/flag.php
    这个 URL 是标准的访问方式,目标是服务器自身的本地资源(127.0.0.1)上的 /flag.php 文件。
    127.0.0.1 是本地回环地址,表示请求直接发往本地主机(服务器本身)。
    服务器会解析这个 URL 并向其本地文件 /flag.php 发起请求,若存在该文件并且可访问,服务器会返回文件的内容。
    使用场景:这种 URL 通常在 SSRF(Server-Side Request Forgery)攻击中利用,攻击者诱导服务器访问自身的敏感资源(例如 /flag.php)。

  6. 利用双 @ 符号混淆解析:http://foo@127.0.0.1:80@www.baidu.com/flag.php
    这个 URL 的构造方式是利用了 URL 解析器对 @ 符号的处理方式,试图混淆实际的请求地址。
    foo@127.0.0.1:80:这是用户认证信息 (foo) 和目标地址 (127.0.0.1:80) 的一部分。
    在 URL 规范中,foo@ 通常表示用户名,用于认证访问服务器。
    这个部分会被 URL 解析器解释为要访问 127.0.0.1,同时使用 foo 作为用户名。
    第二个 @ 符号后的 www.baidu.com/flag.php:
    URL 中的第二个 @ 符号实际上会被 URL 解析器忽略,主要目的是造成混淆。
    根据标准 URL 解析,www.baidu.com/flag.php 只会被当作路径的一部分,而不会被当作真正的主机地址。
    实际解析的结果:主机仍然是 127.0.0.1,请求的路径为 /flag.php。
    使用场景:这个双 @ 技巧常见于某些 SSRF 攻击中,攻击者利用这种 URL 混淆机制绕过应用的安全防护。例如:
    某些安全检查可能只针对 URL 中的主机名部分进行过滤,以为目标是 www.baidu.com,却没有意识到服务器实际仍在请求 127.0.0.1。

  7. 区别总结
    直接访问 vs 混淆:
    http://127.0.0.1/flag.php 直接访问本地资源,没有任何混淆,明确表示请求发送到本地的 127.0.0.1。
    http://foo@127.0.0.1:80@www.baidu.com/flag.php 利用了 URL 的结构,通过双 @ 符号混淆,意图让应用程序或防火墙误认为目标是 www.baidu.com,但实际请求仍然指向 127.0.0.1。
    绕过安全检查:
    http://foo@127.0.0.1:80@www.baidu.com/flag.php 中的第二个 @ 符号,通常是用来绕过安全防护机制(如 URL 过滤器)的。例如,某些安全检查可能只对主机名进行过滤,认为 www.baidu.com 是安全的域名,从而绕过了对本地回环地址 127.0.0.1 的阻止。
    攻击手段:
    http://127.0.0.1/flag.php 是直接的 SSRF 攻击,目标清晰。
    http://foo@127.0.0.1:80@www.baidu.com/flag.php 则是在攻击时添加了 URL 混淆,可能用来绕过一些弱的安全机制或应用层过滤,最终仍然对 127.0.0.1 发起请求。

  8. 为什么能获取到 Flag?
    在这两种情况下,最终目标都是服务器上的本地资源 /flag.php:
    在第一个 URL 中,服务器直接向自身发起请求,读取并返回 /flag.php 的内容。
    在第二个 URL 中,通过构造双 @,服务器可能会绕过一些防护机制(如针对外网域名的限制),最终仍然向 127.0.0.1 发起请求,成功读取 /flag.php。
    两者最终的结果都是让服务器自身访问了 127.0.0.1 的 flag.php 文件,进而泄露了 flag。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐