破壳漏洞(Shellshock)分析CVE-2014-6271


前段时间的破壳漏洞让各个公司忙的够呛,漏洞也过去一段时间了,大牛们的各种分析网上也是转来转去。等他们消停了,该我好好收集资料消化消化这个漏洞了。

漏洞简介

GNU Bash 4.3及之前版本在评估某些构造的环境变量时存在安全漏洞,向环境变量值内的函数定义后添加多余的字符串会触发此漏洞,攻击者可利用此漏洞改变或绕过环境限制,以执行Shell命令。某些服务和应用允许未经身份验证的远程攻击者提供环境变量以利用此漏洞。此漏洞源于在调用Bash Shell之前可以用构造的值创建环境变量。这些变量可以包含代码,在Shell被调用后会被立即执行。

破壳漏洞的严重性被定义为10级(最高),今年4月爆发的OpenSSL“心脏出血”漏洞才5级!

为什么这个漏洞如此受关注?

1、影响范围广,漏洞存在时间长。

Bash,Unix shell的一种。1989年发布第一个正式版本,原先是计划用在GNU操作系统上,但能运行于大多数类Unix系统的操作系统之上,包括Linux与Mac OS X v10.4都将它作为默认shell。它也被移植到Microsoft Windows上的Cygwin与MinGW,或是可以在MS-DOS上使用的DJGPP项目。在Novell NetWare与Android上也有移植。

所以这个漏洞存在于目前主流的Linux和MacOSX操作系统。该漏洞会影响到与bash交互的各种应用程序,如HTTP,FTP,DHCP等等。

出问题的bash代码已经存在20多年了。

漏洞原理

要理解这个漏洞首先要知道什么是bash环境变量,下面引用左耳朵耗子的文章

环境变量大家知道吧,这个不用我普及了吧。环境变量是操作系统运行shell中的变量,很多程序会通过环境变量改变自己的执行行为。在bash中要定义一个环境变量的语法很简单(注:=号的前后不能有空格):

$ var="hello world"

然后你就可以使用这个变量了,比如:echo $var什么的。但是,我们要知道,这个变量只是一个当前shell的“局部变量”,只在当前的shell进程中可以访问,这个shell进程fork出来的进程是访问不到的。

你可以做这样的测试:

$ var="hello coolshell"
$echo$var
hello coolshell
$bash
$echo$var

上面的测试中,第三个命令执行了一个bash,也就是开了一个bash的子进程,你就会发现var不能访问了。

为了要让shell的子进程可以访问,我们需要export一下:

$exportvar="hello coolshell"

这样,这个环境变量就会在其子进程中可见了。

如果你要查看一下有哪些环境变量可以在子进程中可见(也就是是否被export了),你可使用env命令。不过,env命令也可以用来定义export的环境变量。如下所示:

$envvar="hello haoel"

有了这些基础知识还不够,我们还要知道一个基础知识——shell的函数。

bash的函数
在bash下定义一个函数很简单,如下所示:

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

有了上面的环境变量的基础知识后,你一定会想试试这个函数是否可以在子进程中调用,答案当然是不行的。

$ foo(){echo"hello coolshell"; }
$ foo
hello coolshell
$bash
$ foo
bash: foo:commandnot found

你看,和环境变量是一样的,如果要在子进程中可以访问的话,那么,还是一样的,需要export,export有个参数 -f,意思是export一个函数。如:

$ foo(){echo"hello coolshell"; }
$ foo
hello coolshell
$export-f foo
$bash
$ foo
hello coolshell

Bash 漏洞的测试代码

在Bash Shell下执行以下代码:

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

this is a test

表示存在漏洞。打了补丁会输出以下错误:

bash: 警告: x: ignoring function definition attempt

bash: `x' 函数定义导入错误

this is a test

原理分析

Shell里可以定义变量,POC中定义了一个命名为x的变量,内容是一个字符串:
() { :;}; echo vulnerable
而根据漏洞信息得知,这个漏洞产生于Shell在处理函数定义时,执行了函数体之后的命令。但这里x的值是个字符串,它是怎么转变成函数的呢。

实际这个和Bash实现有关,在Bash中定义一个函数,格式为:
function function_name() {
body;
}
当Bash在初始化环境变量时,语法解析器发现小括号和大括号的时候,就认为它是一个函数定义:
[lu4nx@lx-pc ~]$ say_hello='() { echo hello world; }'
[lu4nx@lx-pc ~]$ export say_hello
[lu4nx@lx-pc ~]$ bash -c 'say_hello'
hello world
上面代码在新的Bash进程中,say_hello成了新环境中的一个函数,它的演变过程如下:

1、新的bash在初始时,扫描到环境变量say_hello出现小括号和大括号,认定它是一个函数定义

2、bash把say_hello作为函数名,其值作为函数体

typeset命令可以列出当前环境中所有变量和函数定义,我们用typeset看看这个字符串怎么变成函数的。继续上面定义的say_hello函数:
[lu4nx@lx-pc ~]$ bash -c 'typeset' | fgrep -A 10 say_hello
say_hello ()
{
echo hello world
}
这里新启动了个Bash进程,然后执行了typeset,typeset会返回当前环境(新的环境)中所有定义,这里清楚看到say_hello被变成函数了。

Gitlab-shell 受 Bash CVE-2014-6271 漏洞影响 

Linux再曝安全漏洞Bash 比心脏出血还严重

解决办法是升级 Bash,请参考这篇文章。

Bash远程解析命令执行漏洞测试方法

Bash漏洞最新补丁安装教程【附下载】 

破壳漏洞(Shellshock)修复详解

更多详情见请继续阅读下一页的精彩内容:

  • 1
  • 2
  • 下一页

相关内容