夜间模式暗黑模式
字体
阴影
滤镜
圆角
CVE-2014-6271漏洞分析

0x01 Intro

虽然是比较久远的破壳漏洞了,但是最近刷pwnablekr的时候碰到了这个知识点,就稍微整理一下。这个CVE影响范围为GNU Bash 4.3之前,Bash在读取某些刻意构造的环境变量时会存在安全漏洞。因为某些服务和应用允许未经身份验证的攻击者提供精心构造的环境变量,攻击者利用这个漏洞可以实现绕过环境限制执行Shell命令。

0x02 环境变量

什么是环境变量呢,通过下面的方法就可以定义一个简单的环境变量:

$ foo="hello world"

然后我们就可以使用这个变量,比如echo $foo。但是这个变量只能在当前的shell进程才能访问,比如在shell中fork出来的进程就访问不到$foo。如果需要在shell的子进程中访问,需要在前面添加export:

$ export bar="hello world"

如果需要查看一下当前shell下有哪些环境变量可以在子进程中可见,使用env命令。另外,env也可以用来定义export的环境变量:

$ env var="hello endcat"

同样,shell下不仅可以定义环境变量,也可以定义函数。同样的,如果想要在子进程中访问,也需要在前面加上export

$ foo(){ echo "hello world"; }

0x03 漏洞成因

先来看一下CVE-2014-6271的poc:

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

前面说到,Shell里面可以定义变量,在poc中定义了一个名称为x的变量,内容是一个字符串() { :;}; echo vulnerable,根据漏洞信息知道,这个漏洞产生在shell处理函数定义的时候,执行了函数体后面的命令。在这个例子中,就是echo vulnerable。你可能会感到奇怪,x明明是一个字符串,为什么会变成函数?

来看一下GNU Bash 4.3之前的函数实现,一般来说定义一个函数的格式是这样的:

function function_name(){
body;
}

当Bash在初始化环境变量的时候,语法解析器发现小括号和大括号的时候,就认为它是一个函数定义。比如foo='(){ echo endcat }',bash就会认为这是一个函数定义,并把foo作为其值的函数名,其值就是函数体。通过typeset可以查看当前环境的所有变量和函数定义。

至于调用函数定义后面的语句的原因,是因为bash在初始化的时候调用了builtins/evalstring.c中的parse_and_execute函数,这个函数负责了解析字符串输入并执行的工作。在源码中有一行来判定命令是否为全局的:

else if (command = global_command){
   struct fd_bitmap *bitmap;
  ...
}

事实上在bash进程启动后,会把poc中的x函数解析成解析全局函数。通过typeset -f查看在定义的x后面会有一句declare -fx say_hellodeclare是bash内置命令,用于限定变量的属性。-f表征变量为函数,-x表征变量被export成一个全局函数。其实bash在整个解析初始环境变量的过程中,采用的是foo=bar的赋值语句去用eval执行一遍;如果出现了函数定义就把它转变为函数,在转变的过程中没有严格检查,连带执行了后面的命令。为了避免这个问题,加上了这样一条补丁:

#define SEVAL_FUNCDEF 0x080       
/* only allow function definitions */
#define SEVAL_ONECMD 0x100      
/* only allow a single command */
if ((flags & SEVAL_FUNCDEF)  && (command->type != cm_function_def)
{
   break;
}
if (flags & SEVAL_ONECMD) break;

但是,还是被惨遭bypass:

$ env X='() { (x)=>\' sh -c "echo date"; cat echo

补丁思路的意思是,如果不是函数定义/超过一个命令就认为不合法。但实际上在poc中,函数体为(){,同时又因为没有;分号,它也是一个单命令。Bash shell在执行eval的时候会遇到语法问题,从而把(x)=忽略掉,然后缓冲区就剩下了>\两个字符。然后bash就执行了这个指令:

$ >\
my echo hello

来回忆一下bash的语法,首先>为重定位字符,可以理解为把字符前面的东西写入到字符后面里面。比如A > B就意味着把进程A的输出写入到文件B中,实际上和> B A是等价的。具体来说,重定向的语法定义是这样的:

redirection: '>' WORD
{
redir.filename = $2;
$$ = make_redirection (1, r_output_direction, redir);
}

\是转移字符,保留后面的文本。只在终端输入\并回车后bash进程会被阻塞并等待用户输入。

综上所述,poc在执行的时候相当于执行了>\my echo hello,效果为在路径下面生成了my文件,内容为hello。

暂无评论

发送评论 编辑评论


				
上一篇
下一篇