对CVE-2014-6271 [破壳漏洞] 的一次不太深入的跟踪
@firtst:有些事,该你遇到的始终会遇到!2013年,Struts2远程代码执行漏洞闹的满城风雨时,当时还对此一无所知;2014年4月,HeartBleed掀起波涛汹涌时,较快对此予以关注,晚上跑脚本测试那些使用了HTTPS的网站(以一个旁观者的身份);2014年9月25早上8点,一改平时早上不看微博的习惯,很碰巧地发现了GNU Bash <= 4.3环境变量远程代码执行的漏洞的信息,然后该关注的关注,该传播的传播,这一天虽然没有找出有效的解决方案,也算是打了一次鸡血!鸡血打完后,还有点余热,26号早早起来,打开电脑查阅新闻及相关资讯时,发现前一天下午的各种关注已经降温了。甲方的同学也许一晚彻夜未眠,忙着修复隐患和打补丁,尽管后来证明这一晚算是白忙活了,一个官方的patch足以让你的一切努力变得不再有效。阿里在此次危机中率先分析漏洞、提出临时的补丁方案,虽然依旧是incompletement, 可以bypass,但表现出了对安全问题的足够重视、高效的响应速度和背后强大的安全团队。最终,只有等到官方的补丁出来,厂商也好,用户也罢才好把心放下来。在漏洞曝光后一天多的时间,终于出了可靠的补丁,该打补丁的赶紧动手,黑产牛们早已在这段时间不知道圈了多少“鸡场”了。当然,在实际生产环境下应用补丁之前,还得对其可靠性和可用性做一个评估呢,littlehann同学good job!
网上关于此次CVE-2014-6271的分析文章在25号当天已经有好几篇了,基本上都是简要介绍该漏洞,给出验证方式之类的:freebuf上有两篇、知道创宇出了一篇(25号之后又有一篇)、乌云知识库、还有littlehann同学,可能还有其他的分析文章,但我所知道的的就这些。这几篇文章里面,littlehann同学的文章发布得比较早,后来下午又更新了一下,总的说来,内容更丰富。http://drops.wooyun.org/papers/3064一文发布时间最迟,总结了当前的利用方式,除了在bash中的验证,还提及了ssh和远程环境的测试。之所以说littlehann同学的文章更好,并不是看他文笔好,然后其实他长得也不帅,而是其他几篇文章谈到的都是验证漏洞和利用漏洞的方法,没有探究其根源问题。没错,bash解析环境变量没有对代码和数据进行边界识别,导致任意代码执行,它对会调用bash进行解析的cgi等程序造成影响,简言之:
在sshd配置中使用了ForceCommand用以限制远程用户执行命令,这个漏洞可以绕过限制去执行任何命令。一些Git和Subversion部署环境的限制Shell也会出现类似情况,OpenSSH通常用法没有问题。 Apache服务器使用mod_cgi或者mod_cgid,如果CGI脚本在BASH或者运行在子SHELL里都会受影响。子Shell中使用C的system/popen,Python中使用os.system/os.popen,PHP中使用system/exec(CGI模式)和Perl中使用open/system的情况都会受此漏洞影响。 PHP脚本执行在mod_php不会受影响。 DHCP客户端调用shell脚本接收远程恶意服务器的环境变量参数值的情况会被此漏洞利用。 守护进程和SUID程序在环境变量设置的环境下执行SHELL脚本也可能受到影响。 任何其他程序执行SHELL脚本时用BASH作为解释器都可能受影响。Shell脚本不导出的情况下不会受影响。 参考自:
http://drops.wooyun.org/papers/3064
https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack
但是,说了这么多,我们还是不知道漏洞到底是如何产生的,不知道bash如何处理环境变量的函数和代码,不明白补丁是如何生效的,不明白如果要防止此类问题和修复此类问题我们应该怎么做。既然问题发生在代码层面,要解决问题自然也是在这个层面了,这就是littlehann同学走的稍远的地方,他分析了之前官方给出的patch代码,并跟踪到了相关的文件和函数;先不论分析得怎样,至少研究更为深入,而且有攻也有防,懂技术的人不能老是把人们的目光牵引到攻击和利用上,你还得告诉人家当你的系统出了问题时应该怎么保护自己。毕竟,知道了存在漏洞,却不知道不合修补还是一件很让人难受的事!
结合官方的patch来看,当前阶段需要重点关心的有evalstring.c、variables.c,还有common.h和patchlevel.h,这几个文件是patch中进行了修改的地方。本人在看的时候,发现源码中还有一个eval.c、execute_cmd.c,如果要对此事继续跟踪下去,这两个文件应该也需要了解。
如littlehann所言,evalstring.c中如下代码未对传入的command做边界检查:
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;
但是应该对此负责的,我觉得variables.c更为妥当,该文件中有两个函数:initialize_shell_variables()和parse_and_excute(),一个进行初始化,一个用来解析和执行命令:
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 - ] == ')' && name[char_index - ] == '(')
name[char_index - ] = '\0'; if (temp_var = find_function (name))
{
VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = ;
}
else
report_error (_("error importing function definition for `%s'"), name); /* ( */
if (name[char_index - ] == ')' && name[char_index - ] == '\0')
name[char_index - ] = '('; /* ) */
}
其实应该是传入什么参数无关紧要,关键在于进行解析和执行之前要判断传入的参数是否合法。就像我在一个URL中输入一个'就提示“请不要进行非法攻击”之类的,用户想输入什么都是他的自由,提示输入非法的确有效,但总归不太友好。最好的做法本人觉得,不管输入参数是否合法,后台在进行解析和执行前先鉴定其合法性,然后闷不做声的干掉非法参数,返回一个正常的结果,让整个过程变得透明。比如XSS,过滤<>()"这些可以,但黑名单可以绕过,这样子是分批处理,还不如将输入输出放在一个可控制的范围,在解析和执行之前在一个集中的关卡进行转义,当然输出同样需要如是处理。
在最初的补丁中,variables.c中做出了点小改动:
--- , ---- bash-3.2-patches
strcpy (temp_string + char_index + , string); ! /* 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); if (temp_var = find_function (name))
*** , **** bash-4.3-patches
strcpy (temp_string + char_index + , string); ! if (posixly_correct == || 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 - ] == ')' && name[char_index - ] == '(')
! name[char_index - ] = '\0'; if (temp_var = find_function (name))
在parse_and_excute()函数之前引入了legal_identifier(name):
if (privmode == && read_but_dont_execute == && STREQN ("() {", string, ))
{
string_length = strlen (string);
temp_string = (char *)xmalloc ( + string_length + char_index); strcpy (temp_string, name);
temp_string[char_index] = ' ';
strcpy (temp_string + char_index + , string); ....
这里legal_identifer()用于判断name的内容是否合法,如果说打了补丁后,依旧可以用 env X='() { (a)=>\' sh -c "echo date"; cat echo 绕过,legal_identifer()是否需要对此负责呢?这个问题有待讨论
另外进行简单的跟踪后发现unwind_protect等函数或许也有关注的必要,总之是越扯越多了,但要想真正从根源上理解这个问题恐怕还需要了解更多
后来redhat上给出了一个简单粗暴的应对bypass的方案,将bash_ld_preload.c编译成so库文件,放到lib目录,源代码如下:
/*bash_ld_preload.c*/ static void __attribute__ ((constructor)) strip_env(void);
extern char **environ; static void strip_env()
{
char *p,*c;
int i = ;
for (p = environ[i]; p!=NULL;i++ ) {
c = strstr(p,"=() {");
if (c != NULL) {
*(c+) = '\0';
}
p = environ[i];
}
}
看到源代码就知道了,一目了然c = strstr(p,"=() {");
简单而粗暴,因此在给出这个方案后,redhat也进行了说明,这个东西is potentilaly dangerous,作为关键的临时应急措施尚可,非长久之计(影响bash的正常使用)
再然后,9月25号当天大家都是没辙了,甲方的同学赶紧出方案,乙方的同学也得想对策,进行一系列测试和风险评估后只能"求上天降幅于我",再等官方出补丁吧...
到了26号互联网上关于此事就冷却下来了嘛,其实这时候甲方的同学们,尤其运维人员其实也还是蛮着急的,只是没有媒体的推波助澜,表面上看起来平平稳稳,其实黑产牛们已经是开发出了各种利用方式(绕过方式呢?),从最初的user_agent到dhcp、邮件服务器,如果有人说这是yy,我可以告诉你tombkeeper(tk)在微博上贴了图证实了dhcp的利用可行,而邮件服务器则更是有直接root的,不多说(我只是个打酱油的)
26号下午(北京时间),看到官方终于出补丁,redhat上找了好久,结果还是mirror.centos.org上下载的rpm补丁。rpm包的补丁很方便,但是看不到source code,这对于想要分析源代码的liitlehann同学来说就不喜欢了。不过,大牛就是大牛,很快就弄到了change diff的文件,以bash-4.2为例:
xxx-0.patch是这样的:parse.y
@@ -, +, @@
FREE (word_desc_to_read);
word_desc_to_read = (WORD_DESC *)NULL; + eol_ungetc_lookahead = ;
+
last_read_token = '\n';
token_to_read = '\n';
}
xxx-1.patch是这样的:
--- ../bash-4.2-orig/variables.c -- ::59.313209541 +
+++ variables.c -- ::29.869420719 +
@@ -, +, @@
static void propagate_temp_var __P((PTR_T));
static void dispose_temporary_env __P((sh_free_func_t *)); -static inline char *mk_env_string __P((const char *, const char *));
+static inline char *mk_env_string __P((const char *, const char *, int));
static char **make_env_array_from_var_list __P((SHELL_VAR **));
static char **make_var_export_array __P((VAR_CONTEXT *));
static char **make_func_export_array __P((void));
@@ -, +, @@
#endif
} +/* Prefix and suffix for environment variable names which contain
+ shell functions. */
+#define FUNCDEF_PREFIX "BASH_FUNC_"
+#define FUNCDEF_PREFIX_LEN (strlen (FUNCDEF_PREFIX))
+#define FUNCDEF_SUFFIX "()"
+#define FUNCDEF_SUFFIX_LEN (strlen (FUNCDEF_SUFFIX))
+
+
/* Initialize the shell variables from the current environment.
If PRIVMODE is nonzero, don't import functions from ENV or
parse $SHELLOPTS. */
@@ -, +, @@ /* If exported function, define it now. Don't import functions from
the environment in privileged mode. */
- if (privmode == && read_but_dont_execute == && STREQN ("() {", string, ))
- {
- string_length = strlen (string);
- temp_string = (char *)xmalloc ( + string_length + char_index);
+ if (privmode == && read_but_dont_execute ==
+ && STREQN (FUNCDEF_PREFIX, name, FUNCDEF_PREFIX_LEN)
+ && STREQ (name + char_index - FUNCDEF_SUFFIX_LEN, FUNCDEF_SUFFIX)
+ && STREQN ("() {", string, ))
+ {
+ size_t name_length
+ = char_index - (FUNCDEF_PREFIX_LEN + FUNCDEF_SUFFIX_LEN);
+ char *temp_name = name + FUNCDEF_PREFIX_LEN;
+ /* Temporarily remove the suffix. */
+ temp_name[name_length] = '\0'; - strcpy (temp_string, name);
- temp_string[char_index] = ' ';
- strcpy (temp_string + char_index + , string);
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (name_length + + string_length + );
+ memcpy (temp_string, temp_name, name_length);
+ temp_string[name_length] = ' ';
+ memcpy (temp_string + name_length + , string, string_length + ); /* Don't import function names that are invalid identifiers from the
environment, though we still allow them to be defined as shell
variables. */
- if (legal_identifier (name))
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
+ if (legal_identifier (temp_name))
+ parse_and_execute (temp_string, temp_name,
+ SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); - if (temp_var = find_function (name))
+ if (temp_var = find_function (temp_name))
{
VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = ;
}
else
report_error (_("error importing function definition for `%s'"), name);
+
+ /* Restore the original suffix. */
+ temp_name[name_length] = FUNCDEF_SUFFIX[];
}
#if defined (ARRAY_VARS)
# if
@@ -, +, @@
var->context = variable_context; /* XXX */ INVALIDATE_EXPORTSTR (var);
- var->exportstr = mk_env_string (name, value);
+ var->exportstr = mk_env_string (name, value, ); array_needs_making = ; @@ -, +, @@
/* */
/* **************************************************************** */ +/* Returns the string NAME=VALUE if !FUNCTIONP or if VALUE == NULL (in
+ which case it is treated as empty). Otherwise, decorate NAME with
+ FUNCDEF_PREFIX and FUNCDEF_SUFFIX, and return a string of the form
+ FUNCDEF_PREFIX NAME FUNCDEF_SUFFIX = VALUE (without spaces). */
static inline char *
-mk_env_string (name, value)
+mk_env_string (name, value, functionp)
const char *name, *value;
+ int functionp;
{
- int name_len, value_len;
- char *p;
+ size_t name_len, value_len;
+ char *p, *q; name_len = strlen (name);
value_len = STRLEN (value);
- p = (char *)xmalloc ( + name_len + value_len);
- strcpy (p, name);
- p[name_len] = '=';
+ if (functionp && value != NULL)
+ {
+ p = (char *)xmalloc (FUNCDEF_PREFIX_LEN + name_len + FUNCDEF_SUFFIX_LEN
+ + + value_len + );
+ q = p;
+ memcpy (q, FUNCDEF_PREFIX, FUNCDEF_PREFIX_LEN);
+ q += FUNCDEF_PREFIX_LEN;
+ memcpy (q, name, name_len);
+ q += name_len;
+ memcpy (q, FUNCDEF_SUFFIX, FUNCDEF_SUFFIX_LEN);
+ q += FUNCDEF_SUFFIX_LEN;
+ }
+ else
+ {
+ p = (char *)xmalloc (name_len + + value_len + );
+ memcpy (p, name, name_len);
+ q = p + name_len;
+ }
+ q[] = '=';
if (value && *value)
- strcpy (p + name_len + , value);
+ memcpy (q + , value, value_len + );
else
- p[name_len + ] = '\0';
+ q[] = '\0';
return (p);
} @@ -, +, @@
/* Gee, I'd like to get away with not using savestring() if we're
using the cached exportstr... */
list[list_index] = USE_EXPORTSTR ? savestring (value)
- : mk_env_string (var->name, value);
+ : mk_env_string (var->name, value, function_p (var)); if (USE_EXPORTSTR == )
SAVE_EXPORTSTR (var, list[list_index]);
还有xxx-2.patch:
--- bash-3.2-orig/parse.y -- ::08.742924560 +
+++ bash-3.2/parse.y -- ::33.488769406 +
@@ -, +, @@ /* Variables to manage the task of reading here documents, because we need to
defer the reading until after a complete command has been collected. */
-static REDIRECT *redir_stack[];
+static REDIRECT **redir_stack;
int need_here_doc; +/* Pushes REDIR onto redir_stack, resizing it as needed. */
+static void
+push_redir_stack (REDIRECT *redir)
+{
+ /* Guard against oveflow. */
+ if (need_here_doc + > INT_MAX / sizeof (*redir_stack))
+ abort ();
+ redir_stack = xrealloc (redir_stack,
+ (need_here_doc + ) * sizeof (*redir_stack));
+ redir_stack[need_here_doc++] = redir;
+}
+
/* Where shell input comes from. History expansion is performed on each
line when the shell is interactive. */
static char *shell_input_line = (char *)NULL;
@@ -, +, @@
{
redir.filename = $;
$$ = make_redirection (, r_reading_until, redir);
- redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| NUMBER LESS_LESS WORD
{
redir.filename = $;
$$ = make_redirection ($, r_reading_until, redir);
- redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| LESS_LESS_LESS WORD
{
@@ -, +, @@
redir.filename = $;
$$ = make_redirection
(, r_deblank_reading_until, redir);
- redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| NUMBER LESS_LESS_MINUS WORD
{
redir.filename = $;
$$ = make_redirection
($, r_deblank_reading_until, redir);
- redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| GREATER_AND '-'
{
@@ -, +, @@
case CASE:
case SELECT:
case FOR:
- if (word_top < MAX_CASE_NEST)
+ if (word_top + < MAX_CASE_NEST)
word_top++;
word_lineno[word_top] = line_number;
break;
parse.y的用途本人还需要了解一下,源码中第一行注释:/* Yacc grammar for bash. */
在xxx-1.patch中,可以看到为了进行边界区分,这里定义了可能包含shell函数的环境变量的前缀和后缀:
+/* Prefix and suffix for environment variable names which contain
+ shell functions. */
+#define FUNCDEF_PREFIX "BASH_FUNC_"
+#define FUNCDEF_PREFIX_LEN (strlen (FUNCDEF_PREFIX))
+#define FUNCDEF_SUFFIX "()"
+#define FUNCDEF_SUFFIX_LEN (strlen (FUNCDEF_SUFFIX))
如果是要导出的函数,定义它,然后不要从特权模式下的环境中导入函数,我老是记得有个权限最小原则来着:
if (privmode == && read_but_dont_execute == && STREQN ("() {", string, ))
- {
- string_length = strlen (string);
- temp_string = (char *)xmalloc ( + string_length + char_index); /*撤掉原本对是否为特权模式和是否执行的判断,引入前面新增的特性:FUNCDEF_PREFIX,增强if语句条件为真时的约束条件*/ + if (privmode == && read_but_dont_execute ==
+ && STREQN (FUNCDEF_PREFIX, name, FUNCDEF_PREFIX_LEN)
+ && STREQ (name + char_index - FUNCDEF_SUFFIX_LEN, FUNCDEF_SUFFIX)
+ && STREQN ("() {", string, ))
+ { /*将前缀和后缀应用到实际代码中*/
+ size_t name_length
+ = char_index - (FUNCDEF_PREFIX_LEN + FUNCDEF_SUFFIX_LEN);
+ char *temp_name = name + FUNCDEF_PREFIX_LEN;
+ /* Temporarily remove the suffix. */
+ temp_name[name_length] = '\0'; - strcpy (temp_string, name);
- temp_string[char_index] = ' ';
- strcpy (temp_string + char_index + , string);
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (name_length + + string_length + ); /*新增memcpy(),多层次防御*/
+ memcpy (temp_string, temp_name, name_length);
+ temp_string[name_length] = ' ';
+ memcpy (temp_string + name_length + , string, string_length + );
不要从环境中导入非法标识符的函数名,虽然允许定义为shell变量,前面提到过legal_identifier注定要发挥作用的
- if (legal_identifier (name))
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
+ if (legal_identifier (temp_name))
+ parse_and_execute (temp_string, temp_name,
+ SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); - if (temp_var = find_function (name))
+ if (temp_var = find_function (temp_name))
{
VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = ;
}
else
report_error (_("error importing function definition for `%s'"), name)
后面的代码就先不一段段贴出来了,还没对此做深入研究
xxx-2.patch又回归到了parse.y,其中有一段:
/* Where shell input comes from. History expansion is performed on each
line when the shell is interactive. */
- static char *shell_input_line = (char *)NULL;
从change diff得到的这三个patch来看,显然核心在于variables.c和parse.y,前者之前已经提到过很多次,parse.y发挥的具体作用还需要我们去了解
这里识别代码的边界的问题采用了前缀、后缀的方式,这是比较可行的,我们无法根据一个变量或语句的起始是'或{ 来进行有效的判断,env X='() { (a)=>\' sh -c "echo date"; cat echo就是利用未闭合的{来进行代码注入的。提到这里,在shell中:
赋值语句 : var=value
变量解析 : ${var}
命令解析 : ${command}
双引号 " " : 变量内容,并做转义
单引号 ' ' : 变量内容,但不做转义
反单引号 ` ` : 同 $()
End Of File : "EOF" ; 指示多个命令序列化执行
& 指示该命令在后台异步执行
笔者由于手头工作的原因,这两天时间里只对此次Bash环境变量代码执行的漏洞进行了有限的关注和分析,可以说是一次浅尝辄止的跟踪与接触,对情况了解的并不是很深入,包括前面的一些分析也都只是大致看了几眼。诚如余弦所说,CVE-2014-0627的风险等级是10,而四月份的HeartBleed则为5,虽然因为利用方式相对比较复杂,媒体关注度不是很高,但后劲非常大。此外,针对此次漏洞的新的利用方式也将不断涌现,若不予以关注和警惕,届时将有无数服务器和网站沦为受害者。基于以上种种原因和笔者自己的兴趣,后续会依旧关注和研究这个问题一段时间。
目前的互联网看起来相对比较平静,没有波涛澎湃,但实际上暗潮涌动,我们所见的只为冰山一角。从HeartBleed被誉为世纪性大漏洞以来就已经注定了网络环境的不太平,要说世纪大漏洞此次的Bash漏洞才真的是世纪性的,Bash在互联网之前就已经存在了,而且这个漏洞潜伏了这么久。大家可以想象,这个两个所谓跨纪元的漏洞前后相隔不到半年,可以预测,在未来的几年里网络安全环境会更加跌宕起伏,不太平,作为信息安全工作者如果我们不做出更多的努力,就会有更多的用户成为受害者。
最后再插一句:Unix系统的发明者肯·汤普森在Unix诞生十年之后的一次演讲中曾经说过自己当时在系统中留了一个后面,而十年之后该后门没人发现,依旧可用。再回顾一下Bash,我们用了它二十几年,却全然无知其存在的隐患,我们最信赖的,结果却是伤我们最深的!
##补充
关于此次漏洞的讨论和分析可以参考如下地址:
https://bugzilla.redhat.com/show_bug.cgi?id=1141597#c23
https://www.reddit.com/r/netsec/comments/2hbxtc/cve20146271_remote_code_execution_through_bash/
算上时差,笔者感觉在前沿信息方面虽然有了互联网,但国内始终落后国外半拍,大家可以自己感受下
##updated in 2014-09-29
笔者没有想过26号的补丁出来之后就能一切都安安稳稳了,但是觉得起码在一段时间里,不管出现什么样的利用方式,漏洞应该算是可以防住了。但是上午看到一篇微博,提到第二次的补丁似乎有某种方法可以绕过(狗肉盖饭:http://weibo.com/mbqdpz?source=webim)
当时看到这张图不免有几分震惊,而且还看到GNU官方除了第三次补丁,以bash4.2为例:http://ftp.gnu.org/gnu/bash/bash-4.2-patches/bash42-050
9月27号(国外时间)更新,部分于28号又做了修改(http://ftp.gnu.org/gnu/bash/bash-4.2-patches/bash42-049),看完patch后,发现的确做了些变更
+ #define BASHFUNC_PREFIX "BASH_FUNC_"
+ #define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */
+ #define BASHFUNC_SUFFIX "%%"
+ #define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */
当然还有其他,补丁出来后首先自然想的是否公开了其他绕过技巧,但是因为在网上并没有看到,所以猜测要不是处于评估阶段,就是这第三次的补丁只是为了增强安全性
查找资料找到以下几个有参考价值的地址:
http://seclists.org/oss-sec/2014/q3/781
https://access.redhat.com/security/cve/CVE-2014-7187
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-7187
http://lcamtuf.blogspot.com/2014/09/bash-bug-apply-unofficial-patch-now.html
这里看到,上面的四个链接中两个提到的都是CVE-2014-7187,而不是最初的CVE-2014-6271,这是因为从最初的CVE-2014-6271到不完整的修复方案,中间又出现了CVE-2014-7169,CVE-2014-7186,直到目前的CVE-2014-7187,一下子冒出这么多来,让人怀疑官方的修复不够认真
根据redhat官方的披露,CVE-2014-7187的详情:
“An off-by-one error was discovered in the way Bash was handling deeply nested flow control constructs. Depending on the layout of the .bss segment, this could allow arbitrary execution of code that would not otherwise be executed by Bash.”
还有描述如下:
“Not vulnerable. The concrete .bss segment layout generated by GCC and the linker only allows overwriting a variable whose contents is already controlled by the attacker.”
结合seclists中的讨论,上次的补丁限制过于严格,影响了向后兼容性,破坏了Bash一些困难会用到的正常功能,最终CVSS的评分为4.6分
shellshock的网站上:https://shellshocker.net/ 公布了几个CVE相应的exploit,内容如下:
Exploit 1 (CVE-2014-6271)
There are a few different ways to test if your system is vulnerable to shellshock. Try running the following command in a shell.
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
If you see "vulnerable" you need to update bash. Otherwise, you should be good to go. Exploit 2 (CVE-2014-7169)
Even after upgrading bash you may still be vulnerable to this exploit. Try running the following code.
env X='() { (shellshocker.net)=>\' bash -c "echo date"; cat echo ; rm -f echo
If the above command outputs the current date (it may also show errors), you are still vulnerable. Exploit (???)
Here is another variation of the exploit. Please leave a comment below if you know the CVE of this exploit.
env -i X=' () { }; echo hello' bash -c 'date'
If the above command outputs "hello", you are vulnerable. Exploit 4 (CVE-2014-7186) bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' ||
echo "CVE-2014-7186 vulnerable, redir_stack" Exploit 5 (CVE-2014-7187)
(for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | bash ||
echo "CVE-2014-7187 vulnerable, word_lineno"
需要的同学可以使用上述的几个exploit,如果测试均无问题,那就可以安个心了
如果这几个exploit还不尽兴,可以看一下FireEye的 Shellshock in the Wild
##附:
其实很想跟到Bash的源代码中去探测个究竟,但是近7M的源代码,上千个文件,虽然只需要关注主要的几个源文件如:evalstring.c、variables.c,但由于C语言中没有类,函数调用使用非常多,因此给阅读源码造成了一定的困难,好在发现了sourcecodebrowser。这个网站做的事情就是帮助分析源代码,对函数、变量、结构体等等进行区分,可以同时查看源代码,还有一点很好的是给出一个很直观和形象的函数调用和被调用关系图,越是使用得多和重要的函数,其关系图越复杂,下图为parse_prologue调用的函数关系图:
/*此次事件虽然基本到此为止了,但笔者对此事有较大的关注热情,之后还会对此继续关注和跟踪一段时间*/
对CVE-2014-6271 [破壳漏洞] 的一次不太深入的跟踪的更多相关文章
- Shellshock 破壳漏洞 Writeup
破壳漏洞 CVE编号:CVE-2014-6271 题目URL:http://www.whalwl.site:8029/ 提示:flag在服务器根目录 ShellShock (CVE-2014-6271 ...
- 破壳漏洞利用payload—shellshock in the wild
FireEye关于破壳漏洞(shellshock)在现实中的利用有一篇文章: shellshock in the wild 原文较长,进行了对CGI利用的详细分析,笔者比较感兴趣的是Shellshoc ...
- CVE-2014-6271 Shellshock 破壳漏洞 复现
补坑. 什么是shellshock ShellShock是一个BashShell漏洞(据说不仅仅是Bash,其他shell也可能有这个漏洞). 一般情况来说,系统里面的Shell是有严格的权限控制的, ...
- Bash Shellshock(CVE-2014-6271)破壳漏洞测试
0x01 漏洞原理 Bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以"(){"开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行 ...
- 2014年武汉的IT行情好像不太好(续):20个月过后,再看当时面试过的几个公司--武汉财富基石-崩盘,辣妈萌宝-创业失败,朋友公司转交他人管理
2014年9月的时候,写过一篇面试的总结性质的文章,"2014年武汉的IT行情好像不太好". 原文地址:blog.csdn.net/fansunion/article/detai ...
- 破壳漏洞(CVE-2014-6271)分析
受影响版本:GNU Bash 4.3及之前版本 影响范围:主流的Linux和MacOSX操作系统,与bash交互的各种应用程序,如HTTP,FTP,DHCP等等. 漏洞原理及POC验证: 1.bash ...
- CVE爬虫抓取漏洞URL
String url1="http://www.cnnvd.org.cn/vulnerability/index/vulcode2/tomcat/vulcode/tomcat/cnnvdid ...
- 2014年武汉的IT行情好像不太好
本周,加入武汉一起好工作一周了,也就是说本次找工作彻底结束了. 总的来说,求职行情不太行,双方都匹配的工作好少呀. 1. 武汉财富基石,过了一面,第二面没有去. 钱太少,4K多,跳楼价. 2.武汉 ...
- Bash漏洞批量检测工具与修复方案
&amp;lt;img src="http://image.3001.net/images/20140928/14118931103311.jpg!small" t ...
随机推荐
- 斐讯Fir302b救砖教程
首先本人是路由器小白,不算是硬件改装高手,昨天收到了微信活动中的斐讯Fir302b,大概当时得奖的有300人,所以最近肯定很大一批朋友手里有这样的一款路由. 上网查了一下,此款路由可以刷基于tomat ...
- C指针(二)
原文链接:http://www.orlion.ga/924/ 一.指针与const限定符 const限定符与指针结合起来常见的情况有一下几种: const int *a; int const *a; ...
- JS preventDefault ,stopPropagation ,return false
所谓的事件有两种:监听事件和浏览器对特殊标签元素的默认行为事件.监听事件:在节点上被监听的事件操作,如 select节点的change事件,a节点的click事件.浏览器的默认事件:特定页面元素上带的 ...
- Android探索之HttpURLConnection网络请求
前言: 最近一直想着学习一下比较好的开源网络框架okhttp,想着学习之前还是先总结一下Android原生提供的网络请求.之前一直在使用HttpClient,但是android 6.0(api 23) ...
- Service Plugin / Agent - 每天5分钟玩转 OpenStack(73)
Core Plugin/Agent 负责管理核心实体:net, subnet 和 port.而对于更高级的网络服务,则由 Service Plugin/Agent 管理.Service Plugin ...
- ASP.NET MVC之分部视图和ChildAction(三)
前言 上节我们已经非常清晰并且明确的讲了@Html.ActionLink的作用,这一节我们开始讲讲分部视图以及孩子Action. 话题 在C#中我们知道继承的目的是为了代码的复用,在Web应用程序同样 ...
- EntityFramework之你不知道的那些事(七)
前言 前面一系列几乎都是循序渐进式的进行叙述,似乎脚步走得太快了,于是我开始歇一歇去追寻一些我所不太了解的细枝末节,在此过程中也屡次碰壁,但是唯有如此才能更好的成长,不是吗!希望此文对你亦有帮助. 属 ...
- 从零开始编写自己的C#框架(4)——文档编写说明
在写本系列的过程中,了解得越多越不知道从哪里做为切入点来写,几乎每个知识点展开来说都可以写成一本书.而自己在写作与文档编写方面来说,还是一个初鸟级别,所以只能从大方面说说,在本框架开发所需的范围内来讲 ...
- ES6 - Note1:块级作用域与常量
在ES6以前,ES不支持块级作用域,只有全局作用域和函数作用域,所有变量的声明都存在变量声明提升. 1.let 关键字 声明一个块级变量,只在一个代码块中有效,如果在块外面访问便会报错,如下所示: { ...
- iOS开发之微信聊天页面实现
在上篇博客(iOS开发之微信聊天工具栏的封装)中对微信聊天页面下方的工具栏进行了封装,本篇博客中就使用之前封装的工具栏来进行聊天页面的编写.在聊天页面中主要用到了TableView的知识,还有如何在俩 ...