开发人员在对比Hash字符串的时候常用到等于(==)、不等于(!=)进行比较。如果Hash值以0e开头,后面都是数字,当与数字进行比较时,就会被解析成0 × 10n,会被判断与0相等,使攻击者可以绕过某些系统逻辑。
Hash一般翻译成“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射,pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
var_dump(
'0e123456789'
==
0
);
//bool
(
true
)
var_dump(
'0e123456789'
==
'0'
);
//bool
(
true
)
var_dump(
'0e1234abcde'
==
'0'
);
//bool
(
false
)
当密码经过散列计算后可能会以0e开头。下面代码在进行密码判断时可以绕过登录逻辑。
$username = $_POST[
'username'
];
$password = $_POST[
'password'
];
$userInfo = getUserPass($username);
//当userInfo中的密码以0e开头时,随意构造password即可登录系统。
if
($userInfo[
'password'
]==md5($password)){
//Hash比较缺陷
echo
'登录成功'
;
}
else
{
echo
'登录失败'
;
}
使用hash_equals()函数比较Hash值,可以避免对比被恶意绕过。hash_equals()函数要求提供的两个参数必须是相同长度的字符串,如果所提供的字符串长度不同,会立即返回false。上面的代码应修改如下。
$username = $_POST[
'username'
];
$password = $_POST[
'password'
];
$userInfo = getUserPass($username);
//当userInfo中的密码以0e开头时,随意构造password即可登录系统。
if
(hash_equals($userInfo[
'password'
]),md5($password))){
//Hash比较缺陷
echo
'登录成功'
;
}
else
{
echo
'登录失败'
;
}
hash_equals()函数在PHP5.6中得到支持,如果PHP版本号低于5.6,自己写个这样的函数就可以了,代码如下:
if
(!function_exists(
'hash_equals'
)){
function
hash_equals
($a,$b)
{
if
(!is_string($a)||!is_string($b)){
return
false
;
}
$len = strlen($a);
if
($len !== strlen($b)){
return
false
;
}
$status =
0
;
for
($i =
0
; $i<$len; $i++){
$status |= ord($a[i]) ^ ord($b[$i]);
}
return
$status ===
0
;
}
}
原文始发于微信公众号(衡阳信安):渗透培训笔记:Hash比较缺陷
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论