【攻防世界】web | easyphp 详细题解WP

Source

【攻防世界】web | easyphp 详解WP

payload
?a=1e9&b=53724&c={"m":"2023a","n":[[1,2],0]}

在这里插入图片描述

打开环境是PHP的代码审计题目,通过构造特殊输入绕过多重条件检查。解题的关键在于利用 PHP 语言的弱类型特性函数行为差异逻辑矛盾点

大概审计完全部的代码后,知道key1key2都为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 能满足条件,核心原因是:

  1. 作为字符串时长度为 3:“1e9” 由字符 1e9 组成,长度 = 3,满足 strlen(a) ≤ 3
  2. 转换为整数后数值足够大: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" > 2022true(PHP 比较时将字符串转为2023进行数值比较)

      'm' => '2023a'
      
  • 参数n的绕过条件:

    1. is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])
      
      • c["n"] 是数组,长度为 2,且第一个元素是数组
    2. array_search("DGGJ", $c["n"])
      

      返回非false,但遍历无"DGGJ"

      • array_search("DGGJ", [[1,2], 0]) → 返回1(因"DGGJ" == 0true
      • 遍历时元素为[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),导致数值和长度验证被绕过。
  • 利用条件:需同时满足数值 > 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. 哈希碰撞攻击步骤

  1. 确定目标后缀8b184b(24 位熵)
  2. 选择攻击工具:
    • 单机爆破:Python 脚本(适用于低熵值)
    • 分布式计算:Hashcat + GPU 集群(适用于高熵值)
  3. 优化策略:
    • 缩小字符集(如仅数字 + 小写字母)
    • 预计算彩虹表(针对常见后缀)

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_decodeassociative参数强制转换为数组:

    $c = json_decode($_GET['c'], true);  // 第二个参数为true
    

解题策略与实战技巧

一、解题流程框架

1. 代码审计方法论

  1. 条件分解:将复杂条件拆解为独立验证点
  2. 漏洞定位:标记可能存在类型转换、弱比较的位置
  3. 边界测试:针对每个条件构造极端测试用例
  4. 组合验证:将各部分漏洞串联,验证整体逻辑

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. 防御者视角的安全编码规范

  1. 输入验证三原则:
    • 白名单验证:只允许已知合法格式
    • 强类型检查:使用gettype(), is_int()等函数
    • 长度限制:对所有字符串输入设置最大长度
  2. 避免危险函数组合:
    • 不要同时使用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 竞赛还是实际安全工作,对细节的把控和对新技术的敏感度都是制胜关键。