源代码中的 Bash Shellshock 漏洞在哪里?

信息安全 炮击 重击
2021-08-17 06:55:55

我从昨天开始就听说了 Bash Shellshock问题,我很想知道这个问题出现在源代码的什么地方。我已经从这里下载了 Bash 4.2 的源代码。

我应该在 Bash 4.2 的源代码中究竟在哪里寻找 Shellshock?

我能够找到4.2 的这个补丁信息(来自这个页面),但如果有人能清楚地解释 Shellshock 发生在哪里,那将会很有帮助。

1个回答

CVE-2014-6271

CVE-2014-6271是第一个被发现的漏洞。可以在这里找到补丁。

来自维基百科

通过在环境变量列表中将函数定义编码为变量,这些变量的值以括号 ("()") 开头,后跟函数定义,从而导出函数定义。Bash 的新实例在启动时会扫描其环境变量列表以查找这种格式的值,并将它们转换回内部函数。

Bash 通过创建定义函数的代码片段并执行它来执行这种转换,但它不会验证该片段仅仅是一个函数定义因此,任何可以使 Bash 在其环境中使用特定名称/值对执行的人,也可以通过将这些命令附加到导出的函数定义来执行任意命令。

在源代码中,我们可以看到函数变量的导入variables.c

/* 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;
{
  [...]
  for (string_index = 0; string = env[string_index++]; )
    {

      [...]
      /* 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))
      {
        [...]
        parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
        [...]
      }
}

我们可以看到一个针对赋予函数的所有环境变量的 for 循环,然后是一个关于我们是否处于特权模式的 if,但大多数情况下这是禁用的。“不验证片段只是一个函数定义”部分parse_and_execute在行中。功能描述来自builtins/evalstring.c

/* Parse and execute the commands in STRING.  Returns whatever
   execute_command () returns.  This frees STRING.  FLAGS is a
   flags word; look in common.h for the possible values.  Actions
   are:
    (flags & SEVAL_NONINT) -> interactive = 0;
    (flags & SEVAL_INTERACT) -> interactive = 1;
    (flags & SEVAL_NOHIST) -> call bash_history_disable ()
    (flags & SEVAL_NOFREE) -> don't free STRING when finished
    (flags & SEVAL_RESETLINE) -> reset line_number to 1
*/
int
parse_and_execute (string, from_file, flags)
     char *string;
     const char *from_file;
     int flags;
{

因此,传递给函数的所有内容都会像普通的 bash 命令一样执行。标志SEVAL_NONINTSEVAL_NOHIST是不言自明的(交互性的解释,NOHIST不会将定义添加到您的 bash 历史记录中)不会阻止传递函数定义以外的其他内容。该补丁引入了标志SEVAL_FUNCDEFSEVAL_ONECMD可以在标志字段中传递给parse_and_execute

+ #define SEVAL_FUNCDEF 0x080       /* only allow function definitions */
+ #define SEVAL_ONECMD  0x100       /* only allow a single command */

该补丁还添加了parse_and_execute符合这些新标志的功能,并更改了parse_and_execute传递这些标志的调用:

-     parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+     /* Don't import function names that are invalid identifiers from the
+        environment. */
+     if (legal_identifier (name))
+       parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);

CVE-2014-7169

CVE-2014-7169 基于Tavis Ormandy指出的函数解析问题。修复parse.y看起来很简单,但比 CVE-2014-6271 更棘手:

/* Called from shell.c when Control-C is typed at top level.  Or
   by the error rule at top level. */
void
reset_parser ()
  [...]
  FREE (word_desc_to_read);
  word_desc_to_read = (WORD_DESC *)NULL;
+ eol_ungetc_lookahead = 0;
+
  current_token = '\n'; /* XXX */
  last_read_token = '\n';
  token_to_read = '\n';

eol_ungetc_lookahead变量在其定义中进行了解释:

/* This implements one-character lookahead/lookbehind across physical input
   lines, to avoid something being lost because it's pushed back with
   shell_ungetc when we're at the start of a line. */
static int eol_ungetc_lookahead = 0;

它在shell_getc函数内部读取,如果设置,则读取其(一个字符)内容。

该命令rm echo; env -i X='() { function a .>\' bash -c 'echo date'; cat echo首先使用该字符创建一个语法错误.(您也可以在此处使用其他字符,例如a=),然后使用函数中对eol_ungetc_lookahead变量的不充分清理将字符注入也提供给的“回显日期”字符串中重击。它相当于.reset_parser>rm echo; bash -c '> echo date'; cat echo

oss-sec 邮件列表上的更多资源