• 以web102为例,代码如下:

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

    highlight_file(__FILE__);
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    $v3 = $_GET['v3'];
    $v4 = is_numeric($v2) and is_numeric($v3);
    if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
    }
    else{
    die('hacker');
    }

    ?>

主要思路(相当新颖):

  • 这道题主要使用了这几个点对相关函数进行绕过:
    • 使用先base64加密,之后采用bin2hex函数将原始二进制数据转化为16进制数据,该16进制数据最多只会存在一个字母e,会被识别为科学计数法。可以让我们绕过is_numeric函数。
    • 之后便是使用伪装成科学技术法表示的恶意代码,在首位随便加上两个数字如22,绕过substr函数,题目中该函数的第一和第二位参数分别是要截取的目标字符和起始字符的索引值。
    • 之后便是使用file_put_contents函数,将我们的密文传入新建的文件,把file_put_content函数的首位参数以php伪协议的方式来写,可以让被写入文件的数据经过这样一个伪协议的处理后再写入,即再反向解码,把恶意代码写入文件。

几个点详细介绍

双重加密

  • 我们的原始恶意代码:<?= 'cat *';,注解:
    这里的**`**符号,如同systempassthru函数,用于执行外部操作环境的指令。 与systempassthru最大的区别就是它不会返回布尔值,它只会返回一个执行后的字符串,可以用变量接收,并用于后续操作。 **这里为什么我们需要使用这个函数?而不是另外两种执行外部操作环境命令的指令?** 因为进行双重加密后,我们需要让加密结果最多只留字母e`,(科学计数法在php中的表示可以不分大小写)。
    所以要让原命令的组成尽量简单,不要有太多多余字符。

substr绕过

  • 对于substr的绕过,请牢记,在我们需要的密文前加n位无效数字,让该函数直接截取到我们的恶意代码。

file_put_contents与php伪协议

  • 对于该函数,第一个参数为要写入的目标文件名,第二个参数为要写入文件的字符串内容。

  • 若我们在这题构建这样的payloads:
    题目源码:

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

    highlight_file(__FILE__);
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    $v3 = $_GET['v3'];
    $v4 = is_numeric($v2) and is_numeric($v3);
    if($v4){
    $s = substr($v2,2);//v2传入双重加密过后的密文
    $str = call_user_func($v1,$s);//v1传入hex2bin
    echo $str;
    file_put_contents($v3,$str);
    /*
    v3的payloads传入php://filter/write=convert.base64-decode/resource=1.php
    这里在传入v3的payload,放在file_put_contents中首位参数,那么二号参数,即字符串,在传入的时候
    就会通过base64的解码,并且传入resource中的1.php。
    */
    }
    else{
    die('hacker');
    }

    ?>