
php
$cmd = $_GET['cmd'];
if (!preg_match('/flag/i', $cmd)) { // ① 输入里不能出现 flag(忽略大小写)
$cmd = escapeshellarg($cmd); // ② 整段被单引号锁成"一个参数"
system('cat ' . $cmd); // ③ 命令固定是 cat,只能控制它读哪个文件
}
管理员用浏览器访问过真 flag 文件,这个请求会被记进 nginx 访问日志 。日志路径不含 flag,能直接 cat:
php
?cmd=/var/log/nginx/access.log

真文件就叫 this_is_final_flag_e2a457126032b42d.php ,在网站根目录 /var/www/html/。但名字里有 flag,直接读会被正则拦。
escapeshellarg 在 Linux 上内部用 php_mblen() 判断多字节字符,PHP 7.3 的源码逻辑大致是:
cs
for (每个字节) {
int mb_len = php_mblen(当前位置, 剩余长度);
if (mb_len < 0) {
continue; // ★ 判定为"非法多字节字符" → 直接丢弃这个字节!
} else if (mb_len > 1) {
整段多字节字符原样复制; // 合法的多字节字符(如 UTF-8 汉字)保留
} else {
正常处理(单引号转义);
}
}
而 0x80 是一个孤立的 UTF-8"尾字节" (continuation byte),单独出现时构不成合法字符,php_mblen 返回 -1,于是这个字节被静默删除 。所以我们可以把 %80 塞进 flag 中间,变成 fla + 0x80 + g。两个函数对这同一个字节的处理不一样,形成绕过。

