**考点:**md5($str, true) 原始二进制,ffifdyop 经典串,PHP弱比较和强比较,md5数组绕过
打开题目,是个查询框:

bp抓包,看响应头,能够发现:
sql
select * from 'admin' where password=md5($pass,true)

md5() 有两种输出格式:
php
md5("hello") // 默认 raw=false → 返回 32 位十六进制字符串
// 结果:5d41402abc4b2a76b9719d911017c592
md5("hello", true) // raw=true → 返回 16 字节"原始二进制"
// 结果:]A@*v... (一堆乱码/不可见字符)
这段sql语句 的意思是:我们输入的密码 $pass,先算 md5($pass, true)(注意是 true,吐二进制 ),然后把结果直接拼进 SQL 语句去查数据库。
换算过来就是:
sql
select * from 'admin' where password='你输入的密码的md5值'
因为 md5($pass, true) 吐的是二进制乱码 ,而这串乱码被直接塞进 SQL 的字符串里 。所以我们需要找到一个字符串,它的 MD5 二进制里恰好包含 'or' 这几个字符
ffifdyop就符合这个要求:
第一步 :算 ffifdyop 的普通 MD5(32 位十六进制):
sql
md5('ffifdyop') = 276f722736c95d99e921722cf9ed621c
第二步 :把前几位十六进制转成 ASCII 字符:
| 十六进制 | 转成 ASCII 字符 | 说明 |
|---|---|---|
27 |
'(单引号) |
字符串开头的引号 |
6f |
**o** |
字母 o |
72 |
**r** |
字母 r |
27 |
'(单引号) |
字符串结尾的引号 |
36 |
**6** |
数字 6 |
第三步:拼进 SQL:
sql
-- 原本应该是:
select * from 'admin' where password='xxxx一堆乱码'
-- 实际变成:
select * from 'admin' where password=''or'6É]é!r,ùíb'
- password = '' → 密码等于空字符串 → 假(false)
- or → 或者(这是逻辑"或")
- '6É]é!r,ùíb' → 一个以数字 6 开头的字符串 → MySQL 把它转成数字时取开头的 6,非零 = 真(true)
假 OR 真 = 真 → 整个 WHERE 条件恒为真 → 数据库返回 admin 的所有记录 → 登录绕过
成功后进入下一个页面:

查看网页源代码:

能看到有两个条件必须同时满足:
$a != $b→ a 和 b 原值不相等 (用!=弱比较判断不等)md5($a) == md5($b)→ a 和 b 的 MD5 值弱相等 (用==弱比较)
在PHP中,有个方法叫做数组绕过,因为PHP 的 md5() 无法处理数组 ,传入数组会返回 NULL(并报 Warning 但不中断)。两个 NULL 弱相等(NULL == NULL 为 true),而 a[] 和 b[] 原值又不相等 → 条件满足
所以我们可以构造:
sql
?a[]=1&b[]=2
在url中添加后跳转:

要求 :POST 传 param1、param2,原值不全等 ,但 md5() 结果强全等 (=== 要求值和类型都相同)。
然而数组绕过依然有效...... md5(数组) 返回 NULL,两个 NULL 在 === 下值和类型都相同(都是 NULL)→ 强全等成立。
所以我们可以构造:
sql
param1[]=1¶m2[]=2
在POST里提交:
