PHP代码安全杂谈1
一、精度绕过缺陷
php通常采用的是 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。所以,可能会造成一些不必要的麻烦:
image.png以十进制能够精确表示的有理数如 0.1
或 0.7
,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10)
通常会返回 7
而不是预期中的8
,因为该结果内部的表示其实是类似 7.9999999999999991118…
。
二、类型转换的缺陷
理论
PHP提供了is_numeric
函数,用来变量判断是否为数字。PHP弱类型语言的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval数字化再比。
案例代码
<?php
error_reporting(0);
$flag = 'flag{1S_numer1c_Not_S4fe}';
id =_GET['id']; is_numeric($id)?die("Sorry...."):NULL;
if(id>665){ echoflag; }
?>
write-up
首先对GET传入的id
进行检测。通过is_numeric
函数进行检测,如果是数字就GG,如果不是数字就与655比较,大于655输出正确答案。看起来似乎很矛盾,这里又要用到php作为弱类型语言的一个特征:当一个整形和一个其他类型行比较的时候,会先把其他类型intval
数字化再比。也就是说将id构造成数字+字符形式就能成功得到flag,如:id=666gg
。
三、松散比较符的缺陷
理论
php比较相等性的运算符有两种,一种是严格比较,另一种是松散比较。
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
image.png
严格比较符,会先判断两种字符串的类型是否相等,再比较。
松散比较符,会先将字符串类型转换成相同,再比较。
一个简单的例子
image.png另一个例子
image.pngvar_dump(0=="gg"); //true
var_dump(0==="gg"); //false
var_dump(1=="gg"); //false
0
与gg
进行松散性质的不严格比较,会将gg
转换为数值,强制转换,由于gg
是字符串,转化的结果是0
,所以 输出 true
0
与gg
进行严格 性质的严格比较,这里的gg
是字符串类型,和int类型的0
不相等,所以输出 false
0
与gg
进行松散性质的不严格比较,会将gg
转换为数值,强制转换,由于gg
是字符串,转化的结果是0
,不等于1
,所以输出 false
var_dump(1=="1gg"); //true
var_dump(1=="gg1"); //false
1
与1gg
进行松散性质的不严格比较,这里1gg
被强制转换为int类型的时候会从字符串的第一位开始做判断进行转换,这里的1gg
第一位是1
,所以这里1gg
被转换为1
,所以输出 true
1
与gg1
进行严格 性质的严格比较,字符串gg1
的第一位不是数字,所以它被强制转换为0
,所以输出 false
var_dump("0e123" == "0e456"); //true
var_dump("0e123" == "0eabc"); //flase
这里比较特殊,字符串中出现了0e
,PHP手册介绍如下:
当一个字符串欸当作一个数值来取值,其结果和类型如下:如果该字符串没有包含'.','e','E'并且其数值值在整形的范围之内
该字符串被当作int来取值,其他所有情况下都被作为float来取值,
该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。
0e123
与0e456
相互不严格性质比较的时候,会将0e
这类字符串识为科学技术法的数字,0的无论多少次方都是零,所以相等,输出true
0e123
与0eabc
相互进行不严格性质比较的时候,本应该将0e
这类字符串识为科学技术法的数字,但是这里的0e
后面跟着的是abc
,数学中科学计数的指数不可以包含字母。所以这里字符串中虽然是0e
开头,但是后面的abc
却不符合科学技法的规范,所以输出是false
四、sha1() md5()加密函数漏洞缺陷
理论
md5()
和sha1()
对一个数组进行加密将返回 NULL一个小例子
if ($_GET['name'] == $_GET['password']) print 'Your password can not be your name.'; else if (sha1($_GET['name']) === sha1($_GET['password'])) die('Flag: '.$flag);
GET
类型提交了两个字段name
和password
,获得flag要求的条件是:name != password
sha1(name) == sha1(password)
这个乍看起来这是不可能的,但是这里利用
sha1()
函数在处理数组的时候由于无法处理将返回NULL
可以绕过if语句的验证,if条件成立将获得flag
。 构造语句如下:?name[]=a&password[]=b
这里符合了2个拿到flag的条件:
a不等于b
name和password由于是数组,经过sha1()函数嫁给后都返回
NULL
五、字符串处理函数漏洞缺陷
理论
strcmp()
函数:比较两个字符串(区分大小写).具体的用法解释如下:
参数 `str1`第一个字符串。 参数 `str2`第二个字符串。 如果 `str1` 小于 `str2` 返回 `< 0`; 如果 `str1` 大于 `str2` 返回 `> 0`; 如果两者相等,返回 0。
这个函数接受到了不符合的类型,例如
数组
类型,函数将发生错误。但是在5.3
之前的php中,显示了报错的警告信息后,将return 0
!!!! 也就是虽然报了错,但却判定其相等了。
ereg()
函数:字符串正则匹配。strpos()
函数:查找字符串在另一字符串中第一次出现的位置,对大小写敏感。这2个函数都是用来处理字符串的,但是在传入数组参数后都将返回
NULL
。六、parse_str函数变量覆盖缺陷
理论
parse_str
函数的作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量。void parse_str ( string $str [, array &$arr ] )
str 输入的字符串。
arr 如果设置了第二个变量 arr,变量将会以数组元素的形式存入到这个数组,作为替代。实践
测试代码:
<?php error_reporting(0); $flag = 'flag{V4ri4ble_M4y_Be_C0verEd}'; if (empty($_GET['b'])) { show_source(__FILE__); die(); }else{ $a = "www.windbsy.cn"; $b = $_GET['b']; @parse_str($b); if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) { echo $flag; }else{ exit('your answer is wrong~'); } } ?>
考察点
- parse_str变量覆盖缺陷
write-up
找到核心代码:
@parse_str($b); 这里使用了parse_str函数来传递b的变量值 if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) 这里用到的是文章上面的知识点md5()函数缺陷
因为这里用到了
parse_str
函数来传递b
,if的语句的条件是拿$a[0]
来比较的,有因为这里的变量a的值已经三是固定的了:$a = "www.windbsy.cn";
这里其实是我博客的地址~~ 整体代码乍看起来又不可能,但是利用变量覆盖函数的缺陷这里可以对
a
的变量进行重新赋值,后面的的if语句再利用本文前面提到的md5()
比较缺陷进行绕过:image.pnghttp://localhost/?b=a[0]=240610708
参考文献
PHP代码安全杂谈