一、 PHP弱类型漏洞及其利用
先看一下弱类型的定义:
PHP是弱类型语言,不需要明确的定义变量的类型,变量的类型根据使用时的上下文所决定,也就是变量会根据不同表达式所需要的类型自动转换,比如求和,PHP会将两个相加的值转为long、double再进行加和。每种类型转为另外一种类型都有固定的规则,当某个操作发现类型不符时就会按照这个规则进行转换,这个规则正是弱类型实现的基础。
1.1 基本弱类型漏洞
1.1.1 数组比较
-
在php手册中写道,当数组(array)与任何非数组进行比较时,数组总是最大的,所以有以下结果:
var_dump([]>0); //bool(true) var_dump([]>9999); //bool(true) var_dump([]>'a'); //bool(true) #数组大于字符串 var_dump([[]]>[]); //bool(true) #自然二位数组大于一维数组
1.1.2 bool类型的true比较
-
bool类型的true跟任意字符可以弱类型相等
var_dump(true == 'a'); //bool(true)
1.1.3 ==
与===
的区别
==
-
在进行比较时,向将两边的类型转化成相同类型再进行比较,如果涉及到数值内容的字符串,则字符串会被转化成数值(通过
intval()
函数),并且按照数值大小进行比较(>
、<
同理)。补充:
-
“根据php手册中所讲,字符串的开头决定了它转换后的值,如果该字符串以合法的数值开始,则使用该合法数值,否则其值为0”
-
如果字符创中没有包含
.
、e
、E
并且其数值在整形范围内,该字符被当做int来取值,其他所有情况都被作为float来取值。 -
0e开头的字符串被认为是科学技术计数法,且0e开头的数值为0
-
当一个整形与其他类型进行比较时,会先把其他类型进行
intval()
后再进行比较
所以有如下结果:
var_dump(1 == '1'); //bool(true) var_dump(1 == '1a'); //bool(true) var_dump(0 == '0exxx'); //bool(true) var_dump(0 == 'a1'); //bool(true) var_dump(1 + '1'); //int(2) var_dump(1 + '1a'); //int(2) var_dump(1 + '0exxx'); //int(1) var_dump(1 + 'a1'); //int(1)
-
===
-
先判断两边类型是否相同,再比较大小。
===
是防止了==
弱类型比较漏洞,弱两边类型不同,则直接false而不会再进行比较。var_dump(1 === '1'); //bool(false) var_dump(1 === '1a'); //bool(false) var_dump(0 === '0exx'); //bool(false) var_dump(0 === 'a1'); //bool(false)
1.2 弱类型的函数漏洞利用
1.2.1 md5()
、sha1()
函数
-
函数介绍
md5()函数计算字符串的MD5散列,使用RSA数据安全,包括MD5报文摘要算法。语法:
md5(string,raw)
md5 和 sha1 无法处理数组,但是 php 没有抛出异常,直接返回 fasle。sha1([]) === false md5([]) === false
-
md5()应用
- md5()绕过
==
PHP在处理哈希字符串时,会利用
!=
或==
来对哈希值进行比较,如上所示它把每一个以“0e”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都以“0e”开头,那么PHP将会认为他们相同,都是0。如下例:
var_dump(md5('QNKCDZO') == md5(240610708)) //bool(true) var_dump(md5('QNKCDZO') === md5(240610708)) //bool(false)
上面两个值可使md5()的开头为0e,通过
==
弱类型比较可输出‘yes’,但无法通过===
。常用md5()开头为“0e”的字符:
md5('QNKCDZO') //0e830400451993494058024219903391 md5('240610708') //0e462097431906509019562988736854 md5('s878926199a') //0e545993274517709034328855841020 md5('s155964671a') //0e342768416822451524974117254469 md5('s214587387a') //0e848240448830537924465865611904 md5('s878926199a') //0e545993274517709034328855841020 md5('s1091221200a') //0e940624217856561557816327384675 md5('s1885207154a') //0e509367213418206700842008763514
- md5([])绕过
===
由于md5()函数不能处理数组,所以在md5()遇到数组时会警告并且返回null,然而可以忽略警告并通过数组绕过
===
,如下:var_dump(@md5([]) == @md5([])) //bool(true) var_dump(@md5([]) === @md5([])) //bool(true),@表示忽略警告 var_dump(null === null); //bool(true)
- md5()函数
sql
绕过
md5 和 sha1 支持第二个参数,如果为 true,则会将 hash 后的 16 进制字符串以 16 进制转成字符串的形式返回,如果在 SQL 语句中这样写,会存在注入的问题。
提供一个字符串:
ffifdyop
,md5后,276f722736c95d99e921722cf9ed621c
再转成字符串:'or'6É]™é!r,ùíb
,即md5('ffifdyop',true) = 'or'6É]™é!r,ùíb
// 可以实现绕过 $password = "ffifdyop"; $sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'"; var_dump($sql);
- md5()绕过
-
sha1()函数
sha1()与md5()基本原理相同,所以此处仅列出sha1()开头为“0e”的字符串。
sha1('aaroZmOk') //0e66507019969427134894567494305185566735 sha1('aaK1STfY') //0e76658526655756207688271159624026011393 sha1('aaO8zKZF') //0e89257456677279068558073954252716165668 sha1('aa3OFF9m') //0e36977786278517984959260394024281014729
1.2.2 strcmp()
、strcasecmp()
函数
-
strcmp(string1, string2)
、strcasecmp(string1, string2)
比较两个字符串,前者不区分大小写,后者区分大小写。若
string1 > string2
,返回> 0
;若string1 < string2
,返回< 0
;若string1 = string2
,返回0
。然而该函数无法处理数组,当出现数组时,返回null
。(下例中@表示忽略警告)var_dump(@strcmp([],1)); //NULL var_dump(@strcmp([],'flag') == 0); //bool(true) var_dump(@strcmp([],'flag') === 0); //bool(false)
1.2.3 switch()
函数
-
switch()语句用于根据多个不同的条件执行不同的动作。
$n = '2a'; switch($n){ case 2: echo 'yes'; break; default: echo 'no'; break; } //yes
此例中,为数值类型的case,switch会将参数转换为数值(此处可理解为通过
==
进行比较。)。
1.2.4 is_numeric()
函数
-
is_numeric()
函数判断变量是否为数字&#