【攻防世界】web | easyphp 详解WP
payload
?a=1e9&b=53724&c={"m":"2023a","n":[[1,2],0]}
打开环境是PHP的代码审计题目,通过构造特殊输入绕过多重条件检查。解题的关键在于利用 PHP 语言的弱类型特性、函数行为差异和逻辑矛盾点
大概审计完全部的代码后,知道key1
和key2
都为true 或者都为 1 才能回显 flag
首先审计代码看到
$a = $_GET['a'];
$b = $_GET['b'];
$c=(array)json_decode(@$_GET['c']);
说明有三个GET类型的变量a,b,c,所以payload格式为:
?a=xxx&b=xxx&c=xxx
1.审计$a的条件语句代码:
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
要求:a必须存在,转换为整数后大于6000000,且字符串长度≤3
使用科学技术法取一个大于6000000的数就行,后面转化为字符串,这里取$a=1000000000=1e9,1e9 是科学计数法的简写形式,简写时,“×10ⁿ” 可写成 “eⁿ” 或 “Eⁿ”(e/E 代表 “10 的幂次”)。1 × 10⁹ = 1,000,000,000
为什么 1e9 适合之前的 PHP 场景?
在之前的 PHP 代码中,a=1e9
能满足条件,核心原因是:
- 作为字符串时长度为 3:“1e9” 由字符
1
、e
、9
组成,长度 = 3,满足strlen(a) ≤ 3
; - 转换为整数后数值足够大:PHP 会将字符串 “1e9” 解析为数值
1000000000
(10 亿),远大于 6000000,满足intval(a) > 6000000
。
2.审计$b的条件语句代码:
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
要求:b必须存在,且其md5哈希值的最后6位必须是'8b184b'
根据这个要求,md5的值一般都需要去撞库才能得到,编写下面的撞库脚本
<?php
for ( $i = 1; $i < 999999999; $i++ ){
if ('8b184b' === substr(md5($i),-6,6) ){
echo($i);
}
}
?>
得到$b=53724
3.审计$c的条件语句代码:
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
两个if语句:
1.要求:c必须是数组;c["m"]不能是数字或数字字符串;c["m"]的值要大于2022
2.要求:c["n"]必须是数组;数组长度必须为2;c["n"][0]必须是数组
因为是json_decode()函数,大概知道$c的格式为
{"m":xxx,"n":xxx}
-
参数m的绕过条件:
!is_numeric(@$c["m"]) && $c["m"] > 2022
-
is_numeric("2023a")
→false
(字符串包含非数字字符) -
"2023a" > 2022
→true
(PHP 比较时将字符串转为2023
进行数值比较)'m' => '2023a'
-
-
参数n的绕过条件:
-
is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])
c["n"]
是数组,长度为 2,且第一个元素是数组
-
array_search("DGGJ", $c["n"])
返回非false,但遍历无"DGGJ"
array_search("DGGJ", [[1,2], 0])
→ 返回1
(因"DGGJ" == 0
为true
)- 遍历时元素为
[1,2]
和0
,均不等于"DGGJ"
(严格比较===
)
-
'n' => array(array(1,2), 0)
生成的JSON字符串:
{
"m":"2023a","n":[[1,2],0]}
编写的脚本:
<?php
$a = array('m' => '2023a', 'n' => array(array(1,2), 0));
var_dump($a);
echo json_encode($a);
?>
所以最后的payload:
?a=1e9&b=53724&c={"m":"2023a","n":[[1,2],0]}
flag:
cyberpeace{b1751d5bdeb068eadcd9b272f41c772e}
漏洞分析与利用原理
一、核心漏洞点深度解析
1. PHP 类型转换漏洞(参数 a)
-
原理:
intval()
对科学计数法的特殊处理
- PHP 在处理
intval("1e9")
时,先将字符串解析为浮点数1e9
(即 10⁹),再转换为整数 1000000000。 - 而
strlen("1e9")
仅计算字符串长度(3),导致数值和长度验证被绕过。
- PHP 在处理
-
利用条件:需同时满足数值 > 6000000 且字符串长度≤3,科学计数法是唯一解。
2. MD5 哈希截断攻击(参数 b)
-
原理
:仅验证 MD5 的最后 6 位字符(约 24 位熵)
- 攻击者可通过暴力破解或预计算彩虹表找到匹配的字符串。
- 计算复杂度:约 2²⁴次哈希运算(实际因生日悖论可能更低)。
-
防御弱点:哈希截断降低了碰撞难度,失去了哈希函数的安全性。
3. 数组比较逻辑矛盾(参数 c)
-
原理:
array_search
与严格比较的差异
array_search("DGGJ", [0])
因松散比较"DGGJ" == 0
返回索引 0。- 但
foreach
中使用严格比较0 === "DGGJ"
为 false,不触发 die ()。
-
利用模式:构造与目标值松散相等但类型不同的元素(如数值 0、空数组等)。
4. 字符串数值比较漏洞(参数 c [“m”])
-
原理
:PHP 在比较字符串和数值时的隐式转换
"2023a" > 2022
被解析为2023 > 2022
(字符串截取有效数字部分)。- 而
is_numeric("2023a")
为 false,因包含非数字字符。
二、漏洞利用方法论
1. 科学计数法利用流程
代码
graph TD
A[分析条件: 数值>6000000且长度≤3] --> B[常规数值无法满足]
B --> C[考虑科学计数法]
C --> D[选择1e9: intval=1000000000, strlen=3]
D --> E[验证通过]
2. 哈希碰撞攻击步骤
- 确定目标后缀:
8b184b
(24 位熵) - 选择攻击工具:
- 单机爆破:Python 脚本(适用于低熵值)
- 分布式计算:Hashcat + GPU 集群(适用于高熵值)
- 优化策略:
- 缩小字符集(如仅数字 + 小写字母)
- 预计算彩虹表(针对常见后缀)
3. 数组比较绕过技巧
-
常见绕过组合:
比较值 松散比较结果 严格比较结果 0 == "DGGJ"
true
false
[] == "Array"
true
false
NULL == "0"
true
false
-
选择标准:根据上下文选择不触发其他条件的组合(如本题选择数值 0)。
三、防御措施设计
1. 输入验证强化
// 替代方案:使用正则表达式严格验证数值格式
if(preg_match('/^\d+$/', $a) && intval($a) > 6000000) {
... }
// 或使用filter_var进行强类型验证
if(filter_var($a, FILTER_VALIDATE_INT) !== false && $a > 6000000) {
... }
2. 哈希验证优化
// 验证完整哈希值而非部分
if(md5($b) === "完整的哈希值") {
... }
// 或使用更安全的哈希算法
if(hash('sha256', $b) === "完整的SHA-256哈希值") {
... }
3. 数组比较安全写法
// 使用严格模式的array_search
if(array_search("DGGJ", $c["n"], true) !== false) {
die("直接拒绝任何可能的绕过");
}
// 或使用更安全的遍历方式
if(in_array("DGGJ", $c["n"], true)) {
die("严格检查存在性");
}
4. 安全配置建议
-
启用 PHP 的
strict_types
模式:declare(strict_types=1);
-
使用
json_decode
的associative
参数强制转换为数组:$c = json_decode($_GET['c'], true); // 第二个参数为true
解题策略与实战技巧
一、解题流程框架
1. 代码审计方法论
- 条件分解:将复杂条件拆解为独立验证点
- 漏洞定位:标记可能存在类型转换、弱比较的位置
- 边界测试:针对每个条件构造极端测试用例
- 组合验证:将各部分漏洞串联,验证整体逻辑
2. 快速验证技巧
-
分步测试参数:
# 测试参数a ?a=1e9&b=1&c=1 # 测试参数b(假设已找到碰撞值) ?a=1&b=碰撞值&c=1 # 测试参数c ?a=1&b=1&c={ "m":"2023a","n":[[1,2],0]}
-
利用 BurpSuite Repeater 进行快速迭代
二、关键技术突破点
1. 科学计数法的拓展应用
- 其他可能场景:
- 限制数值范围但允许科学计数法:
a=1e-5
(极小值) - 结合指数部分构造特殊值:
a=9e99
(极大值)
- 限制数值范围但允许科学计数法:
2. 哈希碰撞的优化策略
-
Python 多线程爆破脚本:
import hashlib import threading def crack_chunk(charset, length, start, end, target): for i in range(start, end): combo = charset[i // (len(charset) ** (length-1))] for j in range(length-1): combo += charset[(i // (len(charset) ** j)) % len(charset)] if hashlib.md5(combo.encode()).hexdigest()[-6:] == target: print(f"找到碰撞: { combo}") return combo # 启动多个线程并行搜索 threads = [] charset = 'abcdefghijklmnopqrstuvwxyz0123456789' length = 8 # 假设密码长度为8 chunk_size = (len(charset) ** length) // 10 # 分成10个任务 for i in range(10): t = threading.Thread( target=crack_chunk, args=(charset, length, i*chunk_size, (i+1)*chunk_size, '8b184b') ) threads.append(t) t.start() for t in threads: t.join()
3. 数组比较的进阶绕过
-
针对更复杂验证的构造:
// 若题目要求n的两个元素均为数组 c={ "n":[[0],"DGGJ"]} // array_search返回0,但遍历比较时"0"!==="DGGJ"
三、经验总结与拓展
1. 常见 PHP 安全陷阱清单
漏洞类型 | 典型函数 / 场景 | 绕过技巧 |
---|---|---|
弱类型比较漏洞 | == , array_search 默认模式 |
使用0 , "" , [] 等特殊值 |
哈希截断攻击 | substr(md5($data), -6, 6) |
暴力破解或彩虹表攻击 |
字符串数值转换漏洞 | $str > 1000 且 !is_numeric() |
使用"1001a" 等格式 |
JSON 解析类型转换 | json_decode 转数组 |
利用NULL , true 等特殊值 |
2. 防御者视角的安全编码规范
- 输入验证三原则:
- 白名单验证:只允许已知合法格式
- 强类型检查:使用
gettype()
,is_int()
等函数 - 长度限制:对所有字符串输入设置最大长度
- 避免危险函数组合:
- 不要同时使用
array_search
和严格比较 - 避免依赖隐式类型转换进行安全决策
- 不要同时使用
四、防御措施与安全建议
1. 代码层面的修复方案
// 修复参数a的验证
if(ctype_digit($a) && intval($a) > 6000000) {
... }
// 修复参数b的验证
if(hash_equals(hash('sha256', $b), "完整的SHA-256哈希值")) {
... }
// 修复参数c的验证
if(is_array($c) && isset($c["m"]) && is_string($c["m"]) &&
(int)$c["m"] > 2022 && $c["m"] === (string)(int)$c["m"] &&
is_array($c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0]) &&
!in_array("DGGJ", $c["n"], true)) {
... }
2. 安全配置强化
-
在
.htaccess
或 PHP 配置中添加:php_value magic_quotes_gpc 0 php_value register_globals 0 php_value display_errors 0
3. 自动化安全测试
-
使用工具进行漏洞扫描:
# 使用PHP_CodeSniffer检测安全问题 phpcs --standard=PSR12 --sniffs=Security . # 使用RIPS进行静态代码分析 rips-cli scan /path/to/code -o report.html
五、技术总结与未来趋势
1. 漏洞利用的技术演进
- 从简单类型转换到复杂逻辑链攻击
- 利用 AI 辅助生成更高效的哈希碰撞
- 针对特定 PHP 版本的 0day 漏洞利用
2. 防御技术的发展方向
- 基于机器学习的异常输入检测
- 语言层面的安全增强(如 PHP 的 JIT 编译器安全性优化)
- 零信任架构在 Web 应用中的落地
在攻防对抗的持续演进中,理解编程语言底层机制始终是核心竞争力。无论是 CTF 竞赛还是实际安全工作,对细节的把控和对新技术的敏感度都是制胜关键。