Bash 3.0~4.3 命令执行漏洞补丁 Bypass 漏洞分析
白天忙着漏洞响应、服务器测试和修复、漏洞影响范围统计等事,直到晚上终于有时间来分析了。
官方提供的第一个补丁主要修改了:
1、参数类型和个数的限制,从注释中即可看出:
#define SEVAL_FUNCDEF 0x080 /* only allow function definitions */ #define SEVAL_ONECMD 0x100 /* only allow a single command #*/
2、给 builtins/evalstring.c 文件里的 parse_and_execute 加入了类型判断:
if ((flags & SEVAL_FUNCDEF) && command->type != cm_function_def) { 不合法,不是函数定义 break; } ... // 逻辑为真就表明参数不合法 if (flags & SEVAL_ONECMD) break;
从上面即可看出补丁思路:如果不是函数定义、命令(command)超过一个就判为不合法。什么才算合法呢,Bypass POC 给出了答案:
env X='() { (x)=>\' ./bash -c 'my echo hello'
只要函数体满足() {打头就行了。并且这条POC也满足单个命令(command),因为没出现“;”。
Bash 在 eval 的时候遇到语法问题 (x)= 被忽略了。接着就来到重点了,新的 bash 进程执行了这条命令:
>\my echo hello
然后在路径下生成了 my 文件,内容为 hello。
Bash 语法极其怪异,让我们逐一分析。
字符 \ 是个转移字符,当会保留后面跟的文本,\my 实际等于字符串 my,如果没有 \,新的 bash 进程会把 my 当作是命令。因为如果你在终端只输入 \ 并回车,当前 bash 进程会阻塞等待你输入,在 POC 里,“输入”的就是 my。
字符 > 就是传说中的重定向,假设要把进程 A 的输出写入到文件 B 中,就写成如下:
A > B
其实你写成 > B A 形式也可以,不信试试:
$ > hi date $ cat hi 2014年 09月 27日 星期六 01:06:06 CST
这种前缀写法我也是头一次见到,这次分析 shell 源码,看得出它的设计极其像一个 Lisp 解析器,我以为这种写法是照顾 Lisper,因为 Bash 结构基本上就是一个交互式(REPL)和 eval,而 Lisp 解析器的核心就是 eval,直到我看了 shell 的 Yacc 语法分析(parse.y)后,我才恍然大悟。重定向的语法定义如下:
redirection: '>' WORD { redir.filename = $2; $$ = make_redirection (1, r_output_direction, redir); }
这里表示,输出的文件是取自 $2,$2在这段表示参数 WORD,如果输入的语句是 > A B,那么 WORD 的实参就是 A;如果输入的语句是 A > B,那么 WORD 的实参就是 B。
所以 POC 的思路就是定义一个语法不合法的函数体,绕过函数定义的检测代码,然后执行了后面的命令,最终让 Bash 在初始化的时候执行了 >\my echo hello。