前些阵子开始研究ctf,先从web开始吧. 趁着博客刚建起来,把碰到过的php代码审计类题都总结一下,持续更新.

1. 搞定php弱类型

php弱类型问题很常见,问题在于==与!=的比较.php中==不同于===,===在进行比较前先判断等式两边类型是否相同,而==会先将两边类型转化成相同再比较.php类型比较表

'' == 0 == false '123' == 123             //'123'强制转换为123
'abc' == 0         //intval('abc')==0
'123a' == 123            //intval('123a')==123
'0x01' == 1             //被识别为十六进制
'0e123456789' == '0e987654321'  //被识别为科学计数法
[false] == [0] == [NULL] == ['']
NULL == false == 0
true == 1

所以,我们有,

  • 当一个整型和一个其他类型比较时,会把其他类型转换为整型再比较(所以会有'123'==123)
  • 当整型与字符串比较,且字符串中有字母时,类型转换的结果取决于字符串左边的数字('123a'==123),到非数字(除.)即截止.
  • 若字符串以'0x'开头,或是以'0'开头,后面接一串数字或前段为数字,则会分别被识别为十六进制和八进制.(经测试此特性在php7中失效)
  • 同样,若字符串以'0e'开头,则会被识别为科学计数法.
可以看到,如果是"=="或"!=",就存在通过弱类型比较绕过.

趁热打铁,来看一个switch弱类型
    // 第一种:弱类型,1e==1
    // $x1=1e
    // 第二种:利用数组名字bypass
    // $x1=1[]
    // 传入后为string(3) "1[]",但在switch那里为1
    if (isset($_GET['x1']))
    {
            $x1 = $_GET['x1'];
            $x1=="1"?die("ha?"):NULL;
            switch ($x1)
            {
            case 0:
            case 1:
                    $a=1;
                    break;
            }
    }

2. 变量覆盖

变量覆盖ctf题遇见过,就顺便查查把变量覆盖问题总结一下. 变量覆盖漏洞就是用户将自定义的值替换原有的变量值.以下介绍几种情况.

$$使用不当导致的变量覆盖问题
    //$$变量覆盖漏洞通常出现在foreach中,获取键名再将键名作为变量名,将值赋予该变量
    //存在用户输入?test='haha'覆盖了上面的$test变量
    <?
    $test = '';
    if($_GET){
        foreach($_GET as $key => $value){
            $$key = $value;
        }
    }
extract()函数导致的变量覆盖问题

extract从数组中将变量导入到当前的符号表,就是以键名为变量名,以值为变量的值提取数组.有一个flag参数,是关于提取出的变量名与已有冲突的解决办法,[详情前往.](http://php.net/manual/zh/function.extract.php)

//flag=EXTR_PREFIX_SAME 表示如果有冲突,在变量名前加上前缀,本例中前缀为wddx
<?php
$size = "large";
$var_array = array("color" => "blue",
                   "size"  => "medium",
                   "shape" => "sphere");
extract($var_array, EXTR_PREFIX_SAME, "wddx");
echo "$color, $size, $shape, $wddx_size\n";
?>
//输出为blue, large, sphere, medium
//可见没有覆盖,因为加了前缀.
parse_str函数导致的变量覆盖问题

parse_str(string,array) 函数用于把查询字符串解析到变量中,如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量。

<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";
// Recommended
parse_str($str, $output);
echo $output['first'];  // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz
// DISCOURAGED
parse_str($str);
echo $first;  // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz
?>
import_request_variables()函数导致的变量覆盖问题
    将 GET/POST/Cookie 变量导入到全局作用域中,全局变量注册。
    在5.4之后被取消,只可在4-4.1.0和5-5.4.0可用。
    //导入POST提交的变量值,前缀为post_
    import_request_variable("p", "post_");
    //导入GET和POST提交的变量值,前缀为gp_,GET优先于POST
    import_request_variable("gp", "gp_");
    //导入Cookie和GET的变量值,Cookie变量值优先于GET
    import_request_variable("cg", "cg_");

3. strcmp字符串比较漏洞

//flag[]=''strcmp比较数组与字符串时会出错,5.3之前返回0,5.3之后返回NULL
//利用弱类型比较,仍然能绕过
define('FLAG', 'pwnhub{THIS_IS_FLAG}');
if (strcmp($_GET['flag'], FLAG) == 0) {
echo "success, flag:" . FLAG;
}

4. md5(原值不等,md5相等)

/*解决方法1:md5('QNKCDZO')='0e830400451993494058024219903391'
            md5('240610708')='0e462097431906509019562988736854'
            利用弱类型相等绕过
           则:x1=QNKCDZO&x2=240610708
 解决方法2:md5以数组为参数时报错返回NULL
           则:?x1[]=1&x2[]=2
           */
if ($_GET['x1'] != $_GET['x2'])
    echo "wrong!!!"
else if (md5($_GET['x1']) === md5($_GET['x2']))
    die('Flag: '.$flag);

今天先更新这几个,在学习过程中再充实总结,继续更新.