我从昨天开始就听说了 Bash Shellshock问题,我很想知道这个问题出现在源代码的什么地方。我已经从这里下载了 Bash 4.2 的源代码。
我应该在 Bash 4.2 的源代码中究竟在哪里寻找 Shellshock?
我能够找到4.2 的这个补丁信息(来自这个页面),但如果有人能清楚地解释 Shellshock 发生在哪里,那将会很有帮助。
我从昨天开始就听说了 Bash Shellshock问题,我很想知道这个问题出现在源代码的什么地方。我已经从这里下载了 Bash 4.2 的源代码。
我应该在 Bash 4.2 的源代码中究竟在哪里寻找 Shellshock?
我能够找到4.2 的这个补丁信息(来自这个页面),但如果有人能清楚地解释 Shellshock 发生在哪里,那将会很有帮助。
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_NONINT
和SEVAL_NOHIST
是不言自明的(交互性的解释,NOHIST
不会将定义添加到您的 bash 历史记录中)不会阻止传递函数定义以外的其他内容。该补丁引入了标志SEVAL_FUNCDEF
,SEVAL_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 基于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 邮件列表上的更多资源。