shell shock
基本概念
- 普通 shell 变量(shell variable)
VAR=value定义的变量只在当前 shell 可见。子进程不会继承(例如,从当前 shellfork出来的另一个进程拿不到它,除非export)。 - 环境变量(environment variable)
export VAR=value把变量导出成环境变量。环境变量会被子进程继承(这就是为什么 web 服务器、CGI 程序等会收到父进程的环境变量)。 - shell 函数(function)
在交互 shell 或脚本里写的函数不会被子进程直接继承(普通函数变量不继承)。 - 环境变量版的函数(function exported via environment)
Bash 有个历史特性:可以把函数导出到环境里,然后子进程的 bash 会把它“还原”为函数。也就是说,导出的函数会以特殊字符串形式存在于环境变量里,子 bash 启动时会解析这些字符串并重新定义函数。
-
- 正常情况下,这个导出/重建机制是合理的(方便导出函数给子 shell 使用)。
- 字符串版的函数环境变量(漏洞点)
问题来了:如果父进程把一个看起来像函数定义的字符串放进环境变量(而不是实际的函数导出),旧版 Bash 在启动时会尝试把这个字符串解析为函数,而解析过程有缺陷:在“函数定义结束”后面的内容没有被正确界定或截断,导致后续的字符串(本应只是普通文本)被当作 shell 代码执行。 - 一次性通过 env 设置并启动 bash 的场景
攻击者常用env x='() { :; };这种形式把恶意字符串放到环境里并直接启动 bash。漏洞的后果是:在 bash 启动并解析环境变量时,就会先执行' bash -c "..." ,然后才执行-c指令指定的命令——等于是提前执行了攻击者放在环境里的代码。 - 为什么父进程能“注入”到子进程?
因为环境变量本来就是用来传递“运行时信息”给子进程的。Bash 的解析代码没有把“函数体”和“后面跟着的命令”严格分离/验证,导致后面跟着的命令也被当成需要执行的 shell 代码。
payload 举例
env x='() { :; }; echo 1' bash -c "echo 1"
解释流程(概念化):
env把环境变量x设为() { :; }; echo 1(这是个看起来像“函数定义 + 额外命令”的字符串)。- vulnerable bash 启动时扫描环境变量,看到
x的值像() { ... },就把() { :; };解析成“函数定义”的开始/结束。 - 解析实现有 bug,错误地把
echo 1当作要执行的代码(而不是普通文本或无害尾部),因此在 bash 启动阶段就执行了一次echo 1。 - 然后
bash -c "echo 1"本身也会执行echo 1,所以会看到两次输出(脆弱系统上会出现“额外的东西”在 bash 启动时被执行)。这正是 Shellshock 的标志行为。
关键点:漏洞在于解析函数定义时没有正确识别函数体的边界,导致后续字符串被当成代码执行。
参考链接






