01
—
简介
说到PHP弱类型大家肯定不陌生,但是对于PHP中==和===比较时存在的差异,哪一个安全哪一个不安全,肯定很多程序员并不清楚。
网络上有很多大佬们的文章都是介绍PHP弱类型原理,及其可能存在的问题理论,还有就是CTF里面经常出现的场景,然而在具体的实战当做却很少有案例或者文章,本文中将我在具体的项目和代码审计中遇到的由PHP弱类型引发的漏洞进行一个初步总结,例如由PHP弱类型导致的密码重置、登录绕过、SQL注入、命令执行、Hash碰撞等漏洞等等,后续遇到新场景继续补充。
02
—
PHP弱类型是什么
PHP作为最受欢迎的开源脚本语言,也被称为是世界上最好的语言,越来越多的应用于Web开发领域。
PHP属于弱类型语言,即定义变量的时候不用声明它是什么类型。作为一个程序员,弱类型确实给程序员书写代码带来了很大的便利,这一便利也是PHP语言的一个特性,但是在安全领域,特性既漏洞,这些特性在代码里面经常就是漏洞最容易出现的地方。
03
—
PHP比较操作符
在弄明白PHP操作符之前,需要明白PHP变量类型及它们的意义。例如,"42" 是一个字符串而 42 是一个整数。FALSE 是一个布尔值而 "false" 是一个字符串。HTML 表单并不传递整数、浮点数或者布尔值,它们只传递字符串。
PHP的比较操作符有==(等于)松散比较,它不会去检查条件式的表达式的类型;===(完全等于)严格比较,也就是恒等,它会检查查表达式的值与类型是否相等,所以这里面就会引入很多有意思的问题。例如NULL,0,”0”,array()使用==和false比较时,都是会返回true的,而使用===却不会,如下图:
在松散比较的时候,PHP会将他们的类型进行强制转换从而统一类型,比如说字符到数字,非bool类型转换成bool类型。一个数字和一个字符串进行比较,PHP会把字符串转换成数字再进行比较。PHP转换的规则的是:若字符串以数字开头,则取开头数字作为转换结果,若无则输出0。当有一个对比参数是整数的时候,会把另外一个参数强制转换为整数,如下图:
PHP官方也给出了类型比较表,表格中显示了PHP类型和比较运算符在松散和严格比较时的作用及返回值,应该是比较详细的,如下图:
使用PHP函数对变量$x进行比较:
松散比较(==)情况
严格比较(===)情况
从上面的表格中已经很清晰的看到==和===比较时的区别了。
04
—
实战中导致的漏洞解析
下面以实战中遇到的真实场景为例介绍PHP弱类型导致的漏洞。
DedeCMS密码重置
2018年01月09日,Dedecms官方更新了DedeCMS V5.7 SP2正式版,在此版本及之前版本存在任意用户密码重置漏洞,具体导致漏洞的代码如图:
在找回密码时,当$dopost = safequestion时,通过传入的member_id查询出对应id用户的安全问题和答案信息,当我们传入的问题和答案不为空,并且等于系统设置的问题和答案时就进入sn()函数。
这里如果用户设置了问题和答案,我们并不知道问题和答案是什么,就无法进入sn()函数。但是如果此用户没有设置问题和答案呢?此时系统默认问题是”0”,答案是空。
那么我们传入答案$safeanswer = “”时:
$row[‘safeanswer’] == $safeanswer;成立。
但是传入问题$safequestion = “0”时:
empty($safequestion)为真,此时$safequestion = ””,而$row[safequestion] = “0”
此时$row[safequestion] == $safequestion;不成立。
所以现在要让问题相等,才能绕过if判断,此时如果熟悉PHP的话,会想到PHP的弱类型问题,如下图:
利用弱类型的特性,==松散比较,"0.0"、"0."、"0e123"在empty()函数是不为空,并且松散比较"0"时为真,成功进入if条件,进入sn()函数重置密码。此漏洞在渗透测试和众测时应该已经被刷很多遍了吧。详细漏洞分析见:https://mp.weixin.qq.com/s/MTES86qMVDquKrZq2oolVA
ZPanel密码重置
Sentora / ZPanel由于设计缺陷,导致存在密码重置漏洞,具体漏洞代码如下:
生成一个随机的验证密码Token,然后更新数据库,发送重置密码链接。
然后点击重置密码链接后,验证token有效性,重置密码。
但是在重置密码完成后,系统又设置了ac_resethash_tx为空,那么我们就可以以空的token继续重置密码了!!!
具体漏洞细节:https://blogs.securiteam.com/index.php/archives/3386
HDwikiSQL注入
具体漏洞代码如图:
从代码中看到,首先从GET里获得doctype($doctype = $this->get[2];),然后进入一个switch语句。如果进入case 2和case3保持$doctype的值不变,但是进入default将$doctype改为1,最后看到这句$count=$this->db->fetch_total('focus',"type=$doctype"),直接将$doctype带入SQL语句,所以只要这个switch语句不影响$doctype的值也就是必须进入case 2和case 3分支,就导致SQL注入漏洞。
如果要进行SQL注入那么$doctype必须是字符串,但是$doctype必须进入case 2和case3分支,case 2和case 3分支又是整型数字,所以就是很经典的弱类型问题进行松散比较的点了。
我们只需要将$doctype的值,也就是GET获取的参数第一位设置为2或者3就好了,后面跟上sql语句就成功绕过进行sql注入了,如下图:
如上图成功绕过进行sql注入。
某产品命令执行
在偶然一次渗透测试项目中,登录到某产品中,发现如下代码(图为伪代码):
从代码中看到$file进入到命令执行,但是$file中可控的变量是$config,而且这里判断只有$config==2时才能进入命令执行流程,所以利用弱类型的类型强制转换就可绕过判断进入命令执行。
WordPress Cookie伪造
在WordPress 3.8.2的官方补丁中有这样一个补丁
很明显,就改变松散比较和严格比较。
在WordPress中$hmac来源于cookies,是我们可控的一个输入参数,结构如下:
$hash是以下代码生成一个md5值,当$hmac == $hash 时,登录成功。由于是==松散比较,所以存在如下几种情况都可以登录成功:
//第一种情况,完全相等。
$hmac= ‘1f253e501c301bf5bf293c40d7d92ded’;
$hash= ‘1f253e501c301bf5bf293c40d7d92ded’;
//第二种情况,第一位为数字,第二位为字母
$hmac= 1;
$hash= ‘1f253e501c301bf5bf293c40d7d92ded’;
//第三种情况。第一位为字母
$hmac= 0;
$hash= ‘af253e501c301bf5bf293c40d7d92ded’;
//第四种情况,第一位为0,第二位为e,后面所有位为数字
$hmac= “0”;
由于$hmac最后获取的是一个字符串,所以很显然第四种情况是可以成立的。所以最后这个漏洞的利用方式还可以是:让$hmac = ‘0’,通过生成$hash,获得一个,第一位为0,第二位为e,后面所有位为数字的$hash。
变相信息泄露
在PHP中进行比较运算时,如果遇到了0e\d+这种字符串,就会将这种字符串解析为科学计数法,例如0e10,‘e’会识别为次方,0的10次方为0,如下图所示,两个不同字符串但是在==下他们的md5值相等:
那么我们来扩展一下:
然后用密码QNKCDZO尝试登录用户A。
倘若成功登录,则证明此网站采用了不完备的加密体制md5一次加密。
倘若成功登录,则证明此网站采用了明文进行存储密码!
这样既可通过反证法采集目标信息了,当然还有其他扩展大家可以自己开开脑洞。
其他
除了在PHP的弱类型中存在强制类型转换外,MySQL中也同样存在类似的特性,在mysql里面,当字段类型为整型,而where语句中的值不为整型的时候,会被转换成整型才放入查询。也就是说,如果where code='xxx',xxx不为整型的话,则会先将xxx转换成整数,才放入查询,也就是说,如果我们传入的字符串为0aaa,则会转换成0,再执行。详见P牛的博客:
https://www.leavesongs.com/PENETRATION/findpwd-funny-logic-vul.html
06
—
防护方案
为了避免意想不到的运行效果,根基实际场景应该使用严格比较。
05
—
参考链接
http://php.net/manual/zh/types.comparisons.php
领取专属 10元无门槛券
私享最新 技术干货