9th Geek SSRF

9 篇文章 0 订阅
8 篇文章 0 订阅

前言

学校举办的第九届极客大挑战,其中一道根据 Blackhat 议题出的 ssrf 题目,也是第一次尝试阅读 php 源码,望大牛们勿喷

代码分析

 <?php

function check_inner_ip($url)
{
    $match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
    if (!$match_result)
    {
        die('url fomat error1');
    }
    try
    {
        $url_parse=parse_url($url);
    }
    catch(Exception $e)
    {
        die('url fomat error2');
    }
    $hostname=$url_parse['host'];
    echo $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 || ip2long('0.0.0.0')>>24 == $int_ip>>24;
}

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 = $_POST['url'];
if(!empty($url)){
    safe_request_url($url);
}
else{
    highlight_file(__file__);
}

//hint23333:
//flag in flag.php
//phpinfo in phpinfo.php

?> 

check_inner_ip 通过 url_parse 检测是否为内网 ip 。

如果满足不是内网 ip ,通过 curl 请求 url 返回结果。

这是 github 上开源,根据 p师傅文章写的防御 ssrf 攻击代码,详情可以查看:安全编码系列–ssrf漏洞防御脚本

漏洞利用

乍一看好像并没有利用点,跳转也做了处理,最终都要经过 check_inner_ip 函数检测。但是忽略了 php_url_parsecurl 同时处理 url 不同。

【Blackhat】SSRF的新纪元:在编程语言中利用URL解析器

这里面关于 curl 的利用提到了,当处理这个地址时

在这里插入图片描述

curl 和 php_url_parse 处理后最终的目标不一样

在这里插入图片描述

php_url_parse 认为 google.com 为目标的同时,curl 认为 evil.com:80 是目标。

文章作者向 curl 团队报告了这个问题,得到了一个补丁,但是补丁又可以通过空格的方式绕过。

在这里插入图片描述

有趣的是当作者再次向官方团队报告漏洞时,被告知它本来就是要让你来传给他正确的URL参数的,并且他们表示,这个漏洞不会修复。?

我们再来分析一下代码逻辑,检测是否内网 ip 通过 parse_url,而最后请求是用 curl 完成的。当遇到上面的 url 格式时,parse_url 判断的是第二个 @ 后接的地址,curl 请求的是第一个。

于是利用思路就有了,让 parse_url 处理外部网站,最后 curl 请求内网网址。

构造 payload:http://Str3am@127.0.0.1:80 @www.baidu.com/flag.php

漏洞分析

题目环境是 php 7.0.32,本地使用 phpstudy 搭建,使用 php 7.0.12,这里也就此版本分析。

这里也推荐一篇翻译的关于 php 源码阅读的基础指南:phps-source-code-for-php-developers

parse_url

函数申明位于 /ext/standard/url.h,具体可以访问 https://github.com/php/php-src/blob/PHP-7.0.12/ext/standard/url.h

在这里插入图片描述

具体定义位于 /ext/standard/url.c

在这里插入图片描述

主要函数 php_url_parse_ex() 从 97 行开始

在这里插入图片描述

这里迫于篇幅,不再对具体过程详解,这篇文章关于源码的详解很精彩 PHP源码分析之parse_url()的2个小trick

str 为处理的 urlsepppue 字符指针,用于标记 url 中字符位置,也是这个函数处理的大致方式。先提取协议(scheme)如 http,https 并存储,接着获取请求参数(query)和锚点(fragment),获取端口(port)最后检测主机(host)并存储。

243 行检测 userpass,对于 http://Str3am@127.0.0.1:80 @www.baidu.com/flag.php,此时变量 e 指向末尾的 p,变量 s 指向 http:// 之后的 S(e-s) 代表所指字符之间的内容(包含),所以函数 zend_memrchr 处理的内容即 Str3am@127.0.0.1:80 @www.baidu.com/flag.php

在这里插入图片描述

问题就出在这个 zend_memrchr 函数,定义位于 /Zend/zend_operators.h,193 行,是从字符串最末尾开始检测,那么对于两个 @ ,就会解析到最后一个

在这里插入图片描述

跟着下来,最后获取主机(host)的时候,(p-s) 的内容即 www.baidu.com

在这里插入图片描述

最后的解析结果如下

在这里插入图片描述

curl

定义位于 /ext/curl/php_curl.h,实现位于 /ext/curl/interface.c,这里是对 libcurl 经行调用,由于能力原因就不继续往下分析了。

在这里插入图片描述

另一道非预期解

比赛时因为非预期被下了,这里也来分析一波

 <?php
//error_reporting(E_ALL);
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)
    {
        dir('url fomat error');
        return false;
    }
    $hostname=$url_parse['host'];

    //var_dump($hostname);

    $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
    {
        //var_dump($url);
        $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 = $_POST['url'];
if(!empty($url)){
    safe_request_url($url);
}
else{
    highlight_file(__file__);
}

//hint23333:
//flag in flag.php
//phpinfo in phpinfo.php

?> 

一开始对比代码,以为是 try..catch 块里的 return false 造成非预期。在师傅的指点下发现函数 check_inner_ip() 中少了对 0.0.0.0 过滤,于是试了一下 url=http://0.0.0.0/flag.php,发现竟然可以,但是在本机 windows 环境下复现时却不行。

查阅了相关资料,0.0.0.0 代表本机 ipv4 的所有地址,猜测可能发布的时候绑定用的 0.0.0.0,这样做包括本地ip和外网ip都能访问到服务,导致问题。

后记

这道题也可以使用 DNS 重绑定(DNS rebinding)解决,有兴趣的师傅们可以去了解下,个人感觉可能是 ssrf 的通解了

参考链接

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Geek Uninstaller是一款免费的软件卸载工具,其官网提供了最新版本的软件下载和详细的功能介绍。在官网上,用户可以下载到各种版本的Geek Uninstaller,包括免费版和Pro版。其中,免费版提供了基本的软件卸载功能,而Pro版则包含了更多强大的功能,如快速卸载和强制卸载等。 除了软件下载,官网还提供了详细的软件功能介绍和使用教程。用户可以通过阅读这些文档来了解Geek Uninstaller的使用方法和注意事项。在官网上还提供了一个FAQ页面,用于解答用户常见问题,这对于新手使用Geek Uninstaller非常有用。 此外,官网上还提供了用户论坛和反馈渠道。用户可以在论坛中交流使用心得,分享问题解决方案和提出意见建议等,而反馈渠道则提供给用户一个直接向Geek Uninstaller团队反映问题的途径,这将有助于开发团队更好地了解用户需求,改进产品质量和用户体验。 总的来说,Geek Uninstaller的官网提供了一站式的下载、教程和用户交流服务,让用户更方便地了解和使用这款软件。如果你正在寻找一款高效的软件卸载工具,不妨前往Geek Uninstaller官网,下载免费版试用一下。 ### 回答2: Geek Uninstaller是一款免费的卸载软件,主要用于卸载Windows操作系统中的应用程序。它可以彻底地删除程序,包括程序残留文件、注册表项和启动项,确保卸载完全彻底,不留任何脏东西在电脑里面。Geek Uninstaller采用简单易用的界面,用户可以通过单击程序图标来启动卸载过程,也可以使用搜索功能来查找需要卸载的程序。此外,Geek Uninstaller还提供了强制卸载功能,用于卸载那些无法正常卸载的程序。此外,还有自启动项管理和快捷方式管理功能。Geek Uninstaller官网提供了软件的下载、安装以及使用说明。用户可以在官网上找到最新版本的软件,并且可以通过官网获得帮助和支持。Geek Uninstaller还提供了付费版,这个版本包括更多的功能,例如批量卸载程序、更新检查等。Geek Uninstaller是一款非常实用的软件,适合任何一位需要卸载应用的用户,不管是个人还是企业机构,都可以使用它来管理电脑里面的程序。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值