Shellshock错误的奇怪Bash函数导出

时间:2014-09-24 19:59:38

标签: bash shellshock-bash-bug

为什么代码

date
bash -c "date"
declare -x date='() { echo today; }' #aka export date='() { echo today; }'
date
bash -c "date"

打印

Wed Sep 24 22:01:50 CEST 2014
Wed Sep 24 22:01:50 CEST 2014
Wed Sep 24 22:01:50 CEST 2014
today

评估的地点(和原因)

 date$date

发生并获得

 date() {echo today; }

广告:@Etan Reisner

  1. 我导出变量 - 不是函数。 Bash从中发挥作用。在
  2. export date='someting'
    

    仍然是一个变量,无论其内容如何。那么,为什么

    export date='() { echo something; }' #Note, it is a variable, not function.
    

    转换为函数?

    1. 上述安全公告谈到在变量之后执行命令,例如,
    2. x='() { echo I do nothing; }; echo vulnerable' bash -c ':'
                                    ^^^^^^^^^^^^^^^
                                    This is executed - this vunerability is CLOSED in version 4.3.25(1).
      

      env-definition之后的命令不会在最新的Bash中执行。

      但问题仍然存在 - 为什么 Bash会将导出的变量转换为函数?

      这是 错误 ;)完整演示,基于@Chepner的回答:

      #Define three variables
      foo='() { echo variable foo; }'    # ()crafted
      qux='() { echo variable qux; }'    # ()crafted
      bar='variable bar'                 # Normal
      export foo qux bar                 # Export
      
      #Define the same name functions (but not qux!)
      foo() { echo "function foo"; }
      bar() { echo "function bar"; }
      declare -fx foo bar                 #Export
      
      #printouts
      echo "current shell foo variable:=$foo="
      echo "current shell foo function:=$(foo)="
      echo "current shell bar variable:=$bar="
      echo "current shell bar function:=$(bar)="
      echo "current shell qux variable:=$qux="
      echo "current shell qux function:=$(qux)="
      
      #subshell
      bash -c 'echo subshell foo variable:=$foo='
      bash -c 'echo subshell foo command :=$(foo)='
      bash -c 'echo subshell bar variable:=$bar='
      bash -c 'echo subshell bar command :=$(bar)='
      bash -c 'echo subshell qux variable:=$qux='
      bash -c 'echo subshell qux command :=$(qux)='
      

      打印

      current shell foo variable:=() { echo variable foo; }=
      current shell foo function:=function foo=
      current shell bar variable:=variable bar=
      current shell bar function:=function bar=
      current shell qux variable:=() { echo variable qux; }=
      tt: line 20: qux: command not found
      current shell qux function:==
      subshell foo variable:==                   #<-- LOST the exported foo variable
      subshell foo command :=function foo=
      subshell bar variable:=variable bar=
      subshell bar command :=function bar=
      subshell qux variable:==                   #<-- And the variable qux got converted to
      subshell qux command :=variable qux=       #<-- function qux in the subshell (!!!).
      

      避免长评论,这是来自Bash来源的代码:

       if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
                                                                 ^^^^^^^^ THE PROBLEM
          {
            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);
      
            /* Ancient backwards compatibility.  Old versions of bash exported
               functions like name()=() {...} */
      

      “古代”(似乎)更好......:)

            if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
              name[char_index - 2] = '\0';
      

3 个答案:

答案 0 :(得分:4)

要记住的关键点是

foo='() { echo 5; }'

只定义一个字符串参数,其字符串看起来很像函数。它仍然是一个常规字符串:

$ echo $foo
() { echo 5; }

而不是功能:

$ foo
bash: foo: command not found

foo标记为导出后,

$ export foo

任何子Bash都会在其环境中看到以下字符串:

foo=() { echo 5; }

通常,此类字符串将成为shell变量,使用=之前的部分作为名称,并使用值之后的部分。但是,Bash特别通过定义函数来处理这些字符串:

$ echo $foo

$ foo
5

您可以通过使用Bash以外的其他内容检查环境本身,而不会更改环境:

$ perl -e 'print $ENV{foo}\n"'
() { echo 5
}

(显然,在创建子环境时,父Bash用换行符替换分号)。它只是子Bash从这样的字符串创建函数而不是shell变量。

foo可以是同一个shell中的参数和函数的事实;

$ foo=5
$ foo () { echo 9; }
$ echo $foo
5
$ foo
9

解释了-f需要export的原因。 export foo会导致字符串foo=5被添加到孩子的环境中; export -f foo用于添加字符串foo=() { echo 9; }

答案 1 :(得分:0)

您基本上是手动导出名为date的函数。 (因为这是bash在内部用于导出函数的格式.Barmar在他的回答中提出了这种格式。这个机制至少提到了here。)

然后当你运行bash时,它会看到导出的函数,并在你告诉它运行date时使用它。

问题是那个机制指定在哪里?我的猜测是它不是因为它是一个内部细节。

这应该显示行为的合并,如果这有助于任何事情。

$ bar() { echo automatic; }; export -f bar
$ declare -x foo='() { echo manual; }'
$ declare -p foo bar
declare -x foo="() { echo manual; }"
-bash: declare: bar: not found
$ type foo bar
-bash: type: foo: not found
bar is a function
bar ()
{
    echo automatic
}
$ bash -c 'type foo bar'
foo is a function
foo ()
{
    echo manual
}
bar is a function
bar ()
{
    echo automatic
}

答案 2 :(得分:-1)

您的问题的答案直接来自man bash

  

exportdeclare -x命令允许参数和函数   要添加到环境中以及从环境中删除。如果是a的值   修改环境中的参数,新值成为其中的一部分   环境,取代旧的。

因此

declare -x date='() { echo today; }'

替换环境中的date。对date的下一次立即调用会给出date,因为它存在于脚本中(未更改)。对bash -c "date"的调用会创建一个新shell并执行declare -x定义的日期。