Skip to content
Kurisu
Go back

无字母数字 RCE:16 字符限制下的命令执行全解

审计时发现这篇课堂纪要被我漏掉了——欣课上讲的”有限字符 RCE”专题,是整期课里最 CTF 的一讲。补记如下。

0x01 题目模型:16 个字符怎么执行命令

一类经典 CTF 题:代码只执行 eval($_GET[1]) 之类,但你的命令总长度被限制在 16 字符以内(或者干脆禁用字母数字下划线)——system('ls') 都塞不下,怎么办。课堂给了三条路,按直接程度排序:

① 反引号执行:最直接。反引号在 shell 语义里就是命令替换,16 字符够写短命令:

`ls`;`nl *`;      # 配合通配符缩短

② file_put_contents + 伪协议包含:第一条塞不下完整攻击时,用有限的字符写一个新文件,再用包含类漏洞执行它——把”一次短命令”拆成”写入 + 触发”两步,绕开长度限制:

file_put_contents('x.php','<?php eval($_GET[0]);');   # 拆成多次 GET 分批写

③ 临时文件 + 问号通配符:最优雅的一路,见 0x02。

0x02 通配符匹配临时文件:让 PHP 替你落地 webshell

核心认知:PHP 处理文件上传时,无论业务代码写什么,文件都会先落到临时目录/tmp/phpXXXXXX,6 位随机字符),请求结束后才删除。

在”禁用字母数字下划线美元符”的极端限制下,用 ?(匹配任意单字符)构造 pattern 让 shell 帮我们找到并执行它:

. /???/??????????>    # /tmp/phpXXXXXX = 8 个问号匹配 php+6位随机

两个技巧点(笔记原话):利用时间排序ls -t 思路,最新的临时文件排最前)和通配符精准匹配? 数量必须卡准 php + 6 位长度的总位数,用 [@-[] 一类字符区间排除干扰文件)。请求发出后立刻用第二个连接执行,抢在 PHP 清理临时文件之前——这就是经典的”条件竞速”。

0x03 preg_replace /e:\1 分组即代码执行

一个高危正则参数的历史漏洞:PHP 7.3 之前,preg_replace/e 修饰符会把替换串当 PHP 代码执行,而替换串里的 \1 引用第一个捕获分组——如果分组内容是用户可控的,\1 被展开后就在执行用户的代码

preg_replace('/(.*)/e', '\\1', $_GET['x']);   // x=<?php system('whoami')?> RCE

payload 里特意构造 \1 就是为了吃掉分组内容。Windows/Linux 环境在这个洞上没有本质区别,本地测一次即可。7.3+ 该修饰符已移除,但老系统(又是老系统)仍会遇到。

0x04 冷知识:GET 参数里的点号会变成下划线

PHP 处理 GET 请求时,参数名里的 . 和空格会被自动转成 _——?a.b=1 到后端是 $_GET['a_b']。写扫描器/利用脚本时如果参数名带点,按原名传永远收不到,这是查半天”参数没生效”的经典原因。

0x05 环境杂记

课堂顺带的两条运维经验:Ubuntu 长时间运行后 Docker/SSH 偶发失联,重启解决(运行时长导致的系统怪病,别死磕配置);preg_replace 这类历史特性测试时用 php -S 0.0.0.0:7788 起本地服务最快,注意目录下没有 index 文件会 404。

相关旧文


Share this post:

Previous Post
SQL 注入绕过手册:从空格逗号到雷池 WAF 的分层思路
Next Post
PHP 反序列化进阶:Session 引擎不一致、phar 与 ThinkPHP 链的实战拼装