摘要

  • CGI 可把远端 HTTP 请求头转换为 Unix 环境变量;存在漏洞的 Bash 会识别形似函数定义的值,并继续执行函数结尾后的命令。
  • 远程暴露取决于完整调用链,而不是“装了 Bash”这一项事实:外部数据必须进入环境,服务还要直接或间接启动 Bash。
  • 首轮补丁并未覆盖全部行为;完整收口还包括部署后续修复、缩窄环境继承、取消不必要的 shell 跳转,并证明旧进程状态已被替换。

HTTP 请求头本来描述一次请求。Unix 环境变量本来描述一个进程的上下文。函数定义则是代码。Shellshock 的关键,是这三种本应分离的语义在一条调用链上悄然合并。

第一段链路来自公开规范。RFC 3875 规定,CGI 可以把 HTTP 请求头变成以 HTTP_ 开头的元变量;在 Unix 上,这些元变量以同名环境变量交给脚本。这里发生的是数据搬运,本身并不等于执行。

第二段来自 Bash 的函数导出机制。为了让新启动的 Bash 继承函数,旧机制把函数定义编码进环境变量。存在漏洞的版本在启动时识别这种形态,却没有可靠地在函数定义结束处停止解析。跟在后面的字符串便可能成为命令。

完整路径因此非常具体:远端请求头进入 CGI 元变量,再进入进程环境,随后某个程序启动 Bash,Bash 导入函数并执行尾随内容。任何一段不存在,远程利用路径就不成立。所以“所有安装 Bash 的主机都能被远程攻破”并不是可靠判断。CERT/CC 与 Red Hat 列出的实例包括 CGI、受限命令 SSH 配置和部分 DHCP 客户端流程;相同软件包在不同调用图里,风险可以完全不同。

时间线也说明“已修补”不是一个瞬间完成的状态。CVE-2014-6271 于 2014 年 9 月 24 日公开,CERT/CC 次日发布漏洞说明并记录到主动利用。9 月 26 日,Red Hat 明确指出首个修复并不完整,剩余行为由 CVE-2014-7169 描述,随后还有相关解析问题获得独立编号。那几天里,不附带精确包版本和路径测试的“已修复”结论没有足够证据。

更有意义的改动不是屏蔽一条已知字符串,而是重写导入契约。更新后的 Bash 将导出函数放入类似 BASH_FUNC_名称() 的可识别命名空间,使普通环境数据与可执行定义更容易区分。兼容性成本确实存在,但它换回了一条可以解释和验证的边界。

写入磁盘的新包仍不等于运行状态已经更新。Red Hat 建议重启使用导出函数的相关服务;用户可能需要重新登录,screen 或 tmux 会话也可能保留旧定义。资产清单只能回答“安装了什么”,不能单独回答“当前请求最终调用了什么”。

有效处置应从调用图开始:哪个公网服务构造环境?哪个脚本或辅助程序启动 Bash?命令以什么账户运行?哪个长寿命父进程仍携带旧契约?在补丁部署期间,本地责任人应能停用暴露的 CGI 处理器、清洗跨越信任边界的环境,或用直接执行替代不必要的 shell 包装。

最终证据也应留在机器附近。让无害测试沿同一路径进入,观察真正启动的可执行文件、版本与父子关系,确认函数形态的输入被拒绝,记录服务重启,并证明暴露图上的那条边已经消失。

Shellshock 不是“环境变量皆不可信”的口号。它留下的更严格问题是:一段数据在何处获得了新的动词?若答案只是“因为它长得像代码”,执行权就藏得太深了。

来源