GNU tar解压路径绕过漏洞分析(CVE-2016-6321)

GNU tar解压路径绕过漏洞分析(CVE-2016-6321)

0x00 摘要


GNU tar文档管理命令是linux系统下常用的一个打包、压缩的命令,CSS(FSC1V Cyber Security Services)团队的研究员Harry Sintonen发现,tar命令在解压时存在一个路径名绕过漏洞,在一些特定的场景下,利用此漏洞甚至可导致远程代码执行。该漏洞获得CVE编号CVE-2016-6321

0x01 细节


如果攻击者利用该漏洞,精心构造一个特殊的打包文档,受害者在使用tar命令对其进行解压操作时,不论他在命令行指定的目标路径是什么,都可被攻击者绕过,并将提交准备好的文件与目录解压到攻击者指定的地方。

通过这种方式,可以对特定文件进行覆盖与重写,如果解压操作是以高权限身份进行的,例如root账户,那么可以覆写的文件内容就更多也更重要,在最坏的场景下,攻击者可以借助此漏洞对系统的完全控制(root身份下的远程代码执行)。

这个漏洞的影响范围从GNU tar 1.14 to 1.29 (包含1.29) ,影响包括Red Hat,Alphine Linux,Red Star OS以及其他所有使用GNU tar的Linux系统。接下来我们从tar的官网上下载1.29的版本以进行源代码审计,[下载传送门]

在lib/paxnames.c文件中,有一个safer_name_suffix()函数,如果absolute_names变量为0,则将文件名中文件系统的前缀给去掉,并且会对 file_name 进行了一些安全检查 。

if ( absolute_names )
 p = file_name;
 else{
 /* Skip file system prefixes, leading file name components that contain
 * "..", and leading slashes. */
 size_t prefix_len = FILE_SYSTEM_PREFIX_LEN( file_name );
 for ( p = file_name + prefix_len; *p; )
 {
 if ( p[0] == '.' && p[1] == '.' && (ISSLASH( p[2] ) || !p[2]) )
 prefix_len = p + 2 - file_name;
 do
 {
 char c = *p++;
 if ( ISSLASH( c ) )
 break;
 }
 while ( *p );
 }
 for ( p = file_name + prefix_len; ISSLASH( *p ); p++ )
 continue;
 prefix_len = p - file_name;
 if ( prefix_len )
 {
 const char *prefix;
 if ( hash_string_insert_prefix( &prefix_table[link_target], file_name,
 prefix_len, &prefix ) )
 {
 static char const *const diagnostic[] =
 {
 N_( "Removing leading `%s' from member names" ),
 N_( "Removing leading `%s' from hard link targets" )
 };
 WARN( (0, 0, _( diagnostic[link_target] ), prefix) );
 }
 }
 }
 //...
 return p ;

从上述代码中可以读出,经过safer_name_suffix 操作之后,不论在命令行中指定的目标路径是什么,只要文件名中包含"../",那么“../”序列之后部分的文件名与你的当前工作目录就变成了相对关系。

这个漏洞的历史情况有些复杂,

1. 在13.12.1999 commit 之前,压缩文件条目可以通过“../”字符串直接绕过解压路径问题,并将文件写到任意位置。

2. 在13.12.1999 commit之后,代码进行了一些修复,会警告压缩文件文件名中存在..字符串,并且会跳过不去处理这些文件。

3. 然而,在05.07.2003 commit 时,tar引入safer_name_suffix函数,不再使用自己的检查机制。

在05.07.2003 提交的变动中,safer_name_suffix 不再跳过文件名中包含“../”字符串的恶意成员文件,而是想办法去让纠正恶意的文件名,为了让文件名变得安全,该函数做了一些措施,删除所有存在攻击性的成分如"../"及其之前的部分,强行将它们与解压目标路径变成相对关系,看起来这的确更加安全了,但不幸的是,这些操作会带来一些副作用。

例如,test.tar包中有一个文件项,其路径为 etc/motd/../etc/shadow,你在根目录/下,你想使用tar xvf test.tar etc/motd/ 将tar包中打包的motd解压恢复到本机的motd目录中,然而在safer_name_suffix函数中经过一系列的安全操作后,已经将文件项“../”前面的内容都去掉了,路径文件名只剩下etc/shadow,这时你的/etc/shadow就被攻击者覆写了。

0x02 具体攻击场景


1. 攻击者可以用这种手段诱使用户替换一些重要的文件,例如 .ssh/authorized_keys , .bashrc , .bash_logout , .profile , .subversion 或 .anyconnect。示例代码如下:

user@host:~$ dpkg --fsys-tarfile evil.deb | tar -xf - \
 --wildcards 'blurf*'
 tar: Removing leading `blurf/../' from member names
 user@host:~$ cat .ssh/authorized_keys
 ssh-rsa AAAAB3...nU= mrrobot@fsociety
 user@host:~$

2. 有一些从web应用或者其它类似来源自动解压文件的脚本,这些脚本一般会以setuid root权限执行,通常这类脚本的解压命令如下:

#tar -C / -zxf /tmp/tmp.tgz etc/application var/chroot/application/etc

在这种情况下,攻击者可以重写 /var/spoon/cron/crontabs/root 以获取root身份的代码执行能力;也可以将可能被root身份执行的二进制文件替换成一个有后门的版本;或者投放一个setuid root的二进制文件,等待被管理员执行,使攻击者有机会获取root权限。在现实生活中,这种场景下的漏洞利用曾有过好几例成功的案例。

 

3. 以root身份执行解压命令也可能被攻击 。例如上文中提到覆写/etc/shadow的例子

# tar -C / -xvf archive.tar etc/motd
 tar: Removing leading `etc/motd/../' from member names
 etc/motd/../etc/shadow
#

如果--exclude规则与--anchored选项同时使用,那么即使手动加了--exclude规则也没有用,例如:

#tar -C / -xvf archive.tar --anchored --exclude etc/shadow 
tar: Removing leading `etc/motd/../' from member names 
etc/motd/../etc/shadow

在两种情况下,攻击者都成功地把/etc/shadow替换成了任意内容。

 

不过,在实际利用这个漏洞时,攻击者需要首先知道一些特定的前导信息,例如解压命令执行时实际在命令行下指定的路径名,毕竟在构造攻击tar包时“../”序列之前的路径前缀需要符合tar命令中所输入的路径,攻击才能奏效。

0x03 漏洞修复补丁


漏洞作者提出了一些解决方法,并给出了补丁文件,他的想法是直接在safer_name_suffix函数中添加逻辑,如果检测到../字符串,直接报FATAL_ERROR,然后退出程序。补丁内容如下:

--- lib/paxnames.c.orig 2016-04-06 00:04:47.314860045 +0300
+++ lib/paxnames.c 2016-04-06 02:08:44.962297881 +0300
@@ -18,6 +18,7 @@
#include <system.h>
#include <hash.h>
#include <paxlib.h>
+#include <quotearg.h>
/* Hash tables of strings. */
@@ -114,7 +115,15 @@
 for (p = file_name + prefix_len; *p; )
 {
 if (p[0] == '.' && p[1] == '.' && (ISSLASH (p[2]) || !p[2]))
- prefix_len = p + 2 - file_name;
+ {
+ static char const *const diagnostic[] =
+ {
+ N_("%s: Member name contains '..'"),
+ N_("%s: Hard link target contains '..'")
+ };
+ FATAL_ERROR ((0, 0, _(diagnostic[link_target]),
+ quotearg_colon (file_name)));
+ }
 do
 {

虽然这种做法简单粗暴,的确把漏洞给补了,但是并不是完美的补丁,毕竟在恢复网站或者配置的时候,还原到一半就停下来了,这体验肯定是tar开发者自身都不能容忍的。后来,开发者在scr/extract.c代码中做了修改,当检测到文件名中存在../时,直接跳过该文件,时间仿佛回到了十七年前那个冬天的13.12.1999 commit。

diff --git a/src/extract.c b/src/extract.c
index f982433..7904148 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -1629,12 +1629,20 @@ extract_archive (void)
 {
 char typeflag;
 tar_extractor_t fun;
+ bool skip_dotdot_name;
 fatal_exit_hook = extract_finish;
 set_next_block_after (current_header);
+ skip_dotdot_name = (!absolute_names_option
+ && contains_dot_dot (current_stat_info.orig_file_name));
+ if (skip_dotdot_name)
+ ERROR ((0, 0, _("%s: Member name contains '..'"),
+ quotearg_colon (current_stat_info.orig_file_name)));
+
 if (!current_stat_info.file_name[0]
+ || skip_dotdot_name
 || (interactive_option
 && !confirm ("extract", current_stat_info.file_name)))
 {

0x04 最后


虽然有些攻击看起来条件很难满足,但是在实际的攻击场景中,这些看起来很苛刻的洞可能正有奇效。

以下是漏洞作者给出的PoC,用户可用其自检,目前正是漏洞生存周期0day与1day交替阶段,基本都可成功。

$ curl https://sintonen.fi/advisories/tar-poc.tar | tar xv etc/motd
$ cat etc/shadow

 

0x05 参考链接


[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842339

[2] https://sintonen.fi/advisories/tar-extract-pathname-bypass.proper.txt

[3] https://sintonen.fi/advisories/tar-extract-pathname-bypass.patch

[4] https://www.gnu.org/software/tar/

 

 

发表评论

您必须登录才能发表评论!