buuctf 刷题记录 [第二章 web进阶]SSRF Training

buuctf 刷题记录 [第二章 web进阶]SSRF Training

RinE7t.png
这个界面绝对是我做过的题里面最好看的了

点击interesting challenge显示源码

<?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); 
} 

?>

这道题可以动态分析加静态分析一起使用

代码审计

RinYNV.png
这段代码相当于main函数

GET方式传入url赋值给$url

检测url是否为空

不为空则执行safe_request_url函数

Rina3F.png

safe_request_url函数在一开始就有个通过check_inner_ip($url)进行校验的判断

接下来看check_inner_ip($url)函数

Rin6N6.png

check_inner_ip函数:
首先preg_match正则匹配,检查传入的url是否是url格式

Rin29O.png

如果是url格式,则继续执行safe_request_url

RinxDs.png

1. curl_close — 关闭一个cURL会话
2. curl_copy_handle — 复制一个cURL句柄和它的所有选项
3. curl_errno — 返回最后一次的错误号
4. curl_error — 返回一个保护当前会话最近一次错误的字符串
5. curl_escape — 使用 URL 编码给定的字符串
6. curl_exec — 执行一个cURL会话
7. curl_file_create — 创建一个 CURLFile 对象
8. curl_getinfo — 获取一个cURL连接资源句柄的信息
9. curl_init — 初始化一个cURL会话
10. curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄
11. curl_multi_close — 关闭一组cURL句柄
12. curl_multi_exec — 运行当前 cURL 句柄的子连接
13. curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
14. curl_multi_info_read — 获取当前解析的cURL的相关传输信息
15. curl_multi_init — 返回一个新cURL批处理句柄
16. curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
17. curl_multi_select — 等待所有cURL批处理中的活动连接
18. curl_multi_setopt — 为 cURL 并行处理设置一个选项
19. curl_multi_strerror — Return string describing error code
20. curl_pause — Pause and unpause a connection
21. curl_reset — Reset all options of a libcurl session handle
22. curl_setopt_array — 为cURL传输会话批量设置选项
23. curl_setopt — 设置一个cURL传输选项
24. curl_share_close — Close a cURL share handle
25. curl_share_init — Initialize a cURL share handle
26. curl_share_setopt — Set an option for a cURL share handle.
27. curl_strerror — Return string describing the given error code
28. curl_unescape — 解码给定的 URL 编码的字符串
29. curl_version — 获取cURL版本信息

题目中主要用到

curl_init — 初始化一个cURL会话
curl_setopt — 设置一个cURL传输选项
curl_exec — 执行一个cURL会话
curl_getinfo — 获取一个cURL连接资源句柄的信息
curl_close — 关闭一个cURL会话

其中curl_setopt用法为

Riuk2F.png

好吧,太乱了,直接看例子

Riu12D.png

以上的curl部分大致功能就是初始化,然后获取一个网页

Riu3xe.png

这个if的作用就是如果没有获取到信息,就重复获取,重复执行safe_request_url函数

最后把exec后的数据dump出来
var_dump($output);

之后就出函数了

接下来将parse_url后的url赋值给$url_parse

Rin4ud.png

如果parse_url执行失败,则返回false

RinTEt.png

Rin7UP.png

由此可见,parse_url会将url分成6个部分

[scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor

接下来,是一个赋值过程

RinqC8.png

hostname得到的是前面url_parse分离出来的host部分

ip 得到的是将hostname转换为ip地址后的数值(就是把url转成ip地址)
RinOgg.png

int_ip得到的是经过计算后的纯数字形式的ip地址?
RinXvQ.png

然后是一个return
Rinvuj.png

绕过

我们最终的目的是要curl 127.0.0.1/flag然后得到dump出来的数据

那么该怎么绕过这两重检测呢

直接传入http://a:@127.0.0.1:80@baidu.com/flag.php

payload:
http://c83f8e90-2d41-465e-b179-6fdf4b9bfa8f.node3.buuoj.cn/challenge.php/?url=http://a:@127.0.0.1:80@baidu.com/flag.php

其中flag.php是在界面给的提示

RiuNVI.png

这个payload的作用就是

检测到的urlcurl请求的url不一致

原理我记得好久之前我看wooyun上好像有过,但是我记不住了😕

这个payload传入后

safe_request_url检测之后 parse_url取到的hostbaidu.com

而真正curl的确是127.0.0.1/flag
Riu2aq.png

这个原理还要去找找呀。。。

参考:https://blog.csdn.net/wuyaowangchuan/article/details/110433971

最后附上整篇的审计

<?php 
highlight_file(__FILE__);//用PHP高亮显示当前的文件
function check_inner_ip($url) //获取url的域名,将域名转为ip,然后再判断这个ip是否是私有地址
{ 
    $match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url); 
  	//返回$url的匹配,值将是 0 次(不匹配)或 1 
    //^从开头开始匹配
   	//? 匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的
    //( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)
    //.	匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 
    //*	匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*
    //$:从字符串末尾进行匹配
    if (!$match_result) 
    { 
        die('url fomat error'); 
        //如果url不符合正则表达式
    } 
    try 
    { 
        $url_parse=parse_url($url); 
        //分解出一个URL的各个部
        // $url_parse是一个数组
        
    } 
    catch(Exception $e) 
    { 
        die('url fomat error'); 
        return false; 
    } 
    $hostname=$url_parse['host']; //hostname 为主机名,也就是域名
    $ip=gethostbyname($hostname); //通过域名获取IP地址
    $int_ip=ip2long($ip); //ip2long:将IPv4的ip地址(以小数点分隔形式)转换为int
    
    //ip2long('127.0.0.0')>>24 == $int_ip>>24这里的话,我搜网上也没搜到时什么,自己推测,>>代表着右移
    //ip2long('127.0.0.0')>>24,就是把127.0.0.0的整形右移24位,IP一共32位,右移后不就剩个A段“127”了
    //$int_ip>>24,就是把经过parse_url()后的IP右移24位,如果我们输入的是127.0.0.1,有以后应该就是127
    //综上所述,这里就是一个IP白名单,不允许我们输入类似127.*.*.*、10.*.*.*、172.16.*.*、192.168.*.*
    
    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; 
    //判断是否是私有地址,是则这个函数就返回1
} 

function safe_request_url($url) 
{ 
     
    if (check_inner_ip($url)) 
    //判断url是否是私有地址
    { 
        echo $url.' is inner ip'; 
    } 
    else 
    {
        $ch = curl_init(); //初始化新的会话,返回 cURL 句柄,供curl_setopt()、 curl_exec() 和 curl_close() 函数使用
        curl_setopt($ch, CURLOPT_URL, $url); //访问的域名
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //
        curl_setopt($ch, CURLOPT_HEADER, 0); //
        //curl_setopt函数参数解释:https://www.cnblogs.com/lilyhomexl/p/6278921.html
        $output = curl_exec($ch); //抓取URL并把它传递给浏览器
        $result_info = curl_getinfo($ch); //php curl请求在curl_exec()函数执行之后,可以使用curl_getinfo()函数获取CURL请求输出的相关信息
        //[php curl curl_getinfo()返回参数详解](https://www.cnblogs.com/zqifa/p/php-curl-3.html)
        if ($result_info['redirect_url']) 
        { 
            safe_request_url($result_info['redirect_url']); 
        } 
        curl_close($ch); // 关闭cURL资源,并且释放系统资源
        var_dump($output); //执行
    } 
     
} 

$url = $_GET['url']; 
if(!empty($url)){ 
    safe_request_url($url); 
} 
posted @ 2021-06-20 03:01  MuRKuo  阅读(1625)  评论(1编辑  收藏  举报