Libc Realpath缓冲区下溢漏洞分析

0x00漏洞背景

2018年1月11日由Jakub Wilk发现并公布该漏洞,CVE编号为CVE-2018- 1000001,利用该漏洞可以实现Linux系统本地提权。

CVE-2018-1000001是一个Libc Realpath缓冲区下溢漏洞,漏洞的产生是由于GNU C库没有正确处理getcwd()系统调用返回的相对路径,其他库也很可能受此影响。 在受影响的系统中,通过SUID binary可以获得root权限

0x01漏洞分析

该漏洞涉及到两个方面:(1)kernel的getcwd系统调用(2)glibc的realpath函数

(1)内核方面:

getcwd()会返回当前工作目录的绝对路径,如果当前目录不属于当前进程的根目录(例如:该进程使用chroot设置了一个新的文件系统根目录,但是没有将当前目录的根目录替换成新的)。从linux 2.6.36开始,getcwd会返回“(unreachable)”。通过改变当前目录到另一个挂载的用户空间,普通用户可以完成上述的行为。所以当处理不可信来源的路径时,应该检查返回的路径是否以”/”或”(“开头,避免返回一个不可达地址,被认为是相对地址。

getcwd会返回“(unreachable)”的实现代码:

https://github.com/torvalds/linux/commit/8df9d1a4142311c084ffeeacb67cd34d190eff74

所以getcwd()返回值不应该不含”.”、”..”或符号链接

(2)glibc方面

realpath函数原型:char *realpath(const char *path, char *resolved_path) 头文件:#include

返回值: 成功则返回指向resolved_path的指针,失败返回NULL,错误代码存于errno

realpath是用来将参数path所指的相对路径转换成绝对路径,然后存于参数resolved_path所指的字符串数组或指针中的一个函数。

glibc 仍然认为getcwd()返回的是绝对地址。

漏洞发生处:glibc stdlib/canonicalize.c 的__realpath函数:

如果解析的是一个相对路径,不是以’/’开头的路径时,就会调用getcwd系统调用

 if (name[0] != '/')
    {
      if (!__getcwd (rpath, path_max))
	{
	  rpath[0] = '\0';
	  goto error;
	}
      dest = __rawmemchr (rpath, '\0');
    }
  else
    {
      rpath[0] = '/';
      dest = rpath + 1;
    }

如果getcwd此时返回的是”(unreachable)”,则在接下来在解析路径时,发现路径开头并不包含’/’,会在while循环中不断读取dest之前的地址,产生缓冲区下溢。

  else if (end - start == 2 && start[0] == '.' && start[1] == '.')
	{
	  /* Back up to previous component, ignore if at root already.  */
	  if (dest > rpath + 1)
	    while ((--dest)[-1] != '/');
	}

之后操作的dest地址就是溢出的地址。

0x02 调试过程

调试前需要将poll的等待时间改成-1,设置跟踪子进程,就能调试到漏洞触发点。

exp中是通过调用umount来执行realpath函数,触发漏洞。

当处理”down”路径时,会调用__getcwd函数,此时返回”(unreachable)”

通过readlink获取新的rpath:

将name的值变成预先设好的字符串,包含”..”

执行到漏洞点,此时rpath为”(unreachable)/tmp/down”,dest为”down”,name为”0x7fffe41fac70 “../x/../../”, ‘A’ <repeats 57 times>, “/”, ‘A’ <repeats 53 times>, “/A” “

根据name来处理路径,当dest处理到(unreachable)时,开头没有’/’,while会一直循环,直到找到’/’为止。

漏洞攻击效果图:

0x03 补丁分析

halfdog网站提供的补丁:

************************************************************
--- stdlib/canonicalize.c       2018-01-05 07:28:38.000000000 +0000
+++ stdlib/canonicalize.c       2018-01-05 14:06:22.000000000 +0000
@@ -91,6 +91,11 @@
          goto error;
        }
       dest = __rawmemchr (rpath, '\0');
+/* If path is empty, kernel failed in some ugly way. Realpath
+has no error code for that, so die here. Otherwise search later
+on would cause an underrun when getcwd() returns an empty string.
+Thanks Willy Tarreau for pointing that out. */
+      assert (dest != rpath);
     }
   else
     {
@@ -118,8 +123,17 @@
       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
        {
          /* Back up to previous component, ignore if at root already.  */
-         if (dest > rpath + 1)
-           while ((--dest)[-1] != '/');
+         dest--;
+         while ((dest != rpath) && (*--dest != '/'));
+         if ((dest == rpath) && (*dest != '/') {
+           /* Return EACCES to stay compliant to current documentation:
+           "Read or search permission was denied for a component of the
+           path prefix." Unreachable root directories should not be
+           accessed, see https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ */
+           __set_errno (EACCES);
+           goto error;
+         }
+         dest++;
        }
       else
        {
************************************************************

补丁对产生溢出的地方加了一个判断,一旦发现路径不是以’/’开头,便产生报错。

但是glibc官方并没有采用上述的补丁:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=52a713fdd0a30e1bd79818e2e3c4ab44ddca1a94

思想是一样的,只是将对getcwd返回的内容的判断移到了函数内部。

0x05参考链接

https://mp.weixin.qq.com/s/x69eDc8ke0wcUcwRdhsk4Q

https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/

https://cert.360.cn/warning/detail?id=f28c70a8e4905ec0c912f5cfa02ad198

exp地址:https://github.com/5H311-1NJ3C706/local-root-exploits/tree/master/linux/CVE-2018-1000001

http://www.freebuf.com/column/162202.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏TheOneGIS空间站

使用sudo时user is not in sudoers file的解决

在Ubuntu下安装系统以后第一个创建的用户默认是在sudo用户组的,所以以sudo执行命令没有问题。

72620
来自专栏TheOneGIS空间站

时期和ANSI Date之间的转换

一个具体日期的ANSI Date指的是该日期到1600年12月31日经过的天数。 1601年1月1日的ANSI Date为1。 在Linux中使用如下命...

12730
来自专栏TheOneGIS空间站

PostGIS导入导出ESRI Shapefile数据

PostGIS作为PostgreSQL数据库的空间扩展,提供了对空间数据管理的支持。对于空间矢量数据,PostGIS提供了Geometry和Geography俩...

45210
来自专栏TheOneGIS空间站

安装Windows和Linux双系统需要注意的一些问题

1。 如果想在一台电脑上同时体验Windows和Linux双系统,进行安装的时候,最好先安装Windows,留一块分区,然后再安装Linux。因为Linux可以...

76330
来自专栏TheOneGIS空间站

LINUX下gdb无法debug,提示ImportError: No module named 'libstdcxx'

Ubuntu下使用gdb调试C++程序,提示:ImportError: No module named ‘libstdcxx’。貌似CentOS没有这样的问题。

25850
来自专栏TheOneGIS空间站

VMware扩展磁盘大小

1.2K10
来自专栏TheOneGIS空间站

Shell脚本循环读取文件中的每一行

While循环中read命令从标准输入中读取一行,并将内容保存到变量line中。在这里,-r选项保证读入的内容是原始的内容,意味着反斜杠转义的行为不会发生。输入...

12520
来自专栏TheOneGIS空间站

Linux下Boot的编译和使用

其实Linux下的编译安装过程和Window下的是差不多的(Windows下Boot的编译和使用) 首先在官网下载安装包进行解压,然后执行bootstrap...

12820
来自专栏TheOneGIS空间站

Ubuntu下Python版的GDAL安装以及使用

这里使用 ubuntugis提供的gdal进行安装。 首先更新一下ubuntugis的源:

24130
来自专栏TheOneGIS空间站

Ubuntu安装MySQL密码初始化问题

在Ubuntu上使用sudo apt-get install mysql-server mysql-common 命令安装MySQL以后,安装过程中没有提示输入...

40710

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励