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特性,也是让我遇到了很多新的方法,多学习多总结。

相关推荐
m0_570466413 小时前
代码随想录算法训练营第二十八天 | 买卖股票的最佳实际、跳跃游戏、K次取反后最大化的数组和
java·开发语言·算法
程序喵大人3 小时前
分享个C++线程池的实现源码
开发语言·c++·线程池
不会吃萝卜的兔子3 小时前
go webrtc - 1 go基本概念
开发语言·golang·webrtc
要做朋鱼燕4 小时前
【C++】 priority_queue 容器模拟实现解析
开发语言·c++·笔记·职场和发展
jiaway4 小时前
【C语言】第四课 指针与内存管理
c语言·开发语言·算法
励志不掉头发的内向程序员4 小时前
C++进阶——继承 (1)
开发语言·c++·学习
Zacks_xdc4 小时前
【前端】使用Vercel部署前端项目,api转发到后端服务器
运维·服务器·前端·安全·react.js
@CLoudbays_Martin115 小时前
为什么动态视频业务内容不可以被CDN静态缓存?
java·运维·服务器·javascript·网络·python·php
中国胖子风清扬6 小时前
Rust 序列化技术全解析:从基础到实战
开发语言·c++·spring boot·vscode·后端·中间件·rust