命令执行漏洞原理
一个攻击者往搜索框里输入 '; DROP TABLE users; --,命令网站瞬间崩了。执行这当然是漏洞个老段子,但命令执行漏洞比这更狠——它直接让攻击者拿到服务器的原理“手”,想敲什么命令就敲什么命令。命令
命令执行漏洞的执行本质,是漏洞程序把用户的输入当成了系统命令的一部分。开发者的原理本意可能是想调用系统工具处理一下文件,比如把上传的命令图片压缩一下,或者把用户提交的执行文本转成PDF。代码写成了 system("convert " . $filename),漏洞结果 $filename 是原理用户传来的。用户传一个 foo.jpg; cat /etc/passwd,命令分号一结束前面的执行命令,后面的漏洞 cat 就跟着执行了。就这么简单。
这类漏洞常见于那些需要调用外部程序的功能点。PHP 的 system()、exec()、shell_exec(),Python 的 os.system()、subprocess.call(),Java 的 Runtime.getRuntime().exec()——只要参数里有用户可控的内容,没做过滤,基本就中招了。
我见过一个真实案例。某企业内部工具,功能是让管理员输入一个IP地址,然后后端 ping 一下看通不通。代码大概是 system("ping -c 3 " . $ip)。看起来没什么问题,对吧?直到有人输入了 127.0.0.1 && whoami。服务器返回了 www-data。再输入 127.0.0.1 && cat /etc/shadow,密码哈希直接出来了。整个内网沦陷只花了五分钟。
问题出在哪?开发者以为 IP 地址是安全的输入,因为它是“数字加点的格式”。但代码里没有任何校验,用户传什么就拼什么。就算加了正则过滤,也可能被绕过——比如用反引号、$()、换行符、管道符……黑名单永远防不住想象力。
修复方案其实不复杂。最核心的一条:永远不要用字符串拼接去构造系统命令。用 escapeshellarg() 或 escapeshellcmd() 转义参数,或者干脆禁用 system 类函数。更推荐的做法是,把需要调用的系统功能封装成固定参数的接口,用户只能传“选项”,不能传“命令”。比如上面那个 ping 的例子,直接改成用 PHP 的 fsockopen() 发 ICMP 包,或者用现成的库,彻底摆脱系统命令。
还有一点容易忽略:就算输入过滤得再严,如果系统命令本身有漏洞,比如 ImageMagick 的某些历史版本,攻击者照样能通过构造特殊图片触发命令执行。所以能不用系统命令就不用,这是底线。
这类漏洞在 2024 年的漏洞报告中依然高居前列。为什么?因为很多开发者习惯了快捷方式——调系统命令比写原生代码省事,拼个字符串比做参数化查询简单。但省事的代价是,你把服务器的控制权拱手送人。
下次写代码前,先问自己一句:这个功能真的需要调用系统命令吗?如果必须调用,用户输入能进到命令里吗?把这两个问题想清楚,命令执行漏洞基本就堵死了。
(责任编辑:焦点)














