return system(argv[1]);
}
编译,并赋予其suid权限:
root@linux:/tmp# gcc suid.c -o suid
root@linux:/tmp# chmod +s suid
接着我尝试在不同系统中,用www-data用户运行./suid id:
【黑客大神谈一谈Linux与suid提权】Linux发行版
Ubuntu 14.04
Ubuntu 16.04
Ubuntu 18.04
centos 6
CentOS 8
Debian 6
Debian 8
Kali 2019
可见,有些系统是root权限,有些系统仍然是原本用户权限 。那么上面nmap提权失败的原因,就可以排除nmap的原因了 。
同样,CentOS 6和Debian 6都是较老的发行版,但CentOS 6的表现却和新版Ubuntu类似,经过网上的询问和翻文档,得到了bash中的这么一段说明:
If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they Appear in the environment, are ignored, and the effective user id is set to the real user id. If the -p option is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.
如果启动bash时的Effective UID与Real UID不相同,而且没有使用-p参数,则bash会将Effective UID还原成Real UID 。
我们知道,Linux的system()函数实际上是执行的/bin/sh -c,而CentOS的/bin/sh是指向了/bin/bash:
[root@localhost tmp]# ls -al /bin/sh
lrwxrwxrwx. 1 root root 4 Apr 10 2017 /bin/sh -> bash
这就解释了为什么CentOS中suid程序执行id获得的结果仍然是www-data 。假设我们此时将sh修改成dash,看看结果是什么:
[root@localhost tmp]# su -s /bin/bash nobody
bash-4.1$ ls -al /bin/sh
lrwxrwxrwx. 1 root root 9 Feb 19 00:21 /bin/sh -> /bin/dash
bash-4.1$ ./suid id
uid=99(nobody) gid=99(nobody) euid=0(root) egid=0(root) groups=0(root),99(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
dash并没有限制Effective UID,这里可以看到成功获取了root权限 。
Ubuntu的特殊处理但是,我们来看看Ubuntu 16.04,其/bin/sh指向的同样是dash:
$ ls -al /bin/sh
lrwxrwxrwx 1 root root 4 9月 18 2016 /bin/sh -> dash
$ ls -al /bin/dash
-rwxr-xr-x 1 root root 154072 2月 17 2016 /bin/dash
为什么仍然会出现无法提权的情况?
此时我们又需要了解另一个知识了 。通常来说,类似Ubuntu这样的发行版都会对一些程序进行修改,比如我们平时在查看php版本的时候,经常会看到这样的banner:PHP 7.0.33-0ubuntu0.16.04.11,在官方的版本号后会带上Ubuntu的一些版本号,这是因为Ubuntu发行版在打包这些软件时会增加一些自己的代码 。
那么我们可以来看看Ubuntu 16.04源中dash目录:
文章插图
下载其中的dash_0.5.8.orig.tar.gz和dash_0.5.8-2.1ubuntu2.diff.gz并分别解压,我们可以看到dash 0.5.8的原始代码,和Ubuntu对其做的patch 。
我们对原始代码进行patch后,会发现多了一个setprivileged函数:
void setprivileged(int on)
{
static int is_privileged = 1;
if (is_privileged == on)
return;
is_privileged = on;
/*
* To limit bogus system(3) or popen(3) calls in setuid binaries, require
* -p flag to work in this situation.
*/
if (!on && (uid != geteuid() || gid != getegid())) {
setuid(uid);
setgid(gid);
/* PS1 might need to be changed accordingly. */
choose_ps1();
}
}
on的取值取决于用户是否传入了-p参数,而uid和gid就是当前进程的Real UID(GID) 。可见,在on为false,且Real UID 不等于Effective UID的情况下,这里重新设置了进程的UID:
setuid(uid)
setuid函数用于设置当前进程的Effective UID,如果当前进程是root权限或拥有CAP_SETUID权限,则Real UID和Saved UID将被一起设置 。
所以,可以看出,Ubuntu发行版官方对dash进行了修改:当dash以suid权限运行、且没有指定-p选项时,将会丢弃suid权限,恢复当前用户权限 。
这样一来,dash在suid的表现上就和bash相同了,这也就解释了为什么在Ubuntu 16.04以后,我们无法直接使用SUID+system()的方式来提权 。
如何突破限制?同样的,你下载Debian 10最新的dash,也可以看到类似代码 。那么,为什么各大发行版分分在sh中增加了这个限制呢?
我们可以将其理解为是Linux针对suid提权方式的一种遏制 。因为通常来说,很多命令注入漏洞都是发生在system()和popen()函数中的,而这些函数依赖于系统的/bin/sh 。相比CentOS来说,Ubuntu和Debian中的sh一直都是dash,也就一直受到suid提权漏洞的影响 。
推荐阅读
- 男生不会谈恋爱有哪些表现?
- 简谈普洱茶的醒茶方法,普洱茶醒茶的前提
- 简谈普洱茶的存放,存放普洱茶的几个简单条件
- 43岁的男人性功能
- 数仓、数据平台和中台还分不清楚?老板的多年经验之谈,看完懂了
- 黑客大神的Weblogic 远程命令执行漏洞分析
- 河姆渡茶文化遗存琐谈,唐代瓯窑生产的茶具介绍
- 浅谈Linux 中的进程栈、线程栈、内核栈、中断栈
- 黑客大神浅谈LDAP注入攻击
- 浅谈消炎药饭前还是饭后吃