Last active
March 8, 2024 14:41
-
-
Save webstrand/4539759440b9ddec3592f770594ea385 to your computer and use it in GitHub Desktop.
SUID binary allowing unpriviliged users to enter permitted network namespaces
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <sched.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <stdint.h> | |
#include <inttypes.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
void autofree(char **str) { | |
if(str != NULL && *str != NULL) free(*str); | |
} | |
void autoclose(int *fd) { | |
if(fd != NULL && *fd >= 0) close(*fd); | |
} | |
int main(int argc, char *argv[]) { | |
if(argc < 3) { | |
fprintf(stderr, "Usage: %s netns program [...arguments]\n", argv[0]); | |
return 1; | |
} | |
if(strchr(argv[1], '/') != NULL) { | |
fprintf(stderr, "network namespace name must not contain slashes"); | |
return 1; | |
} | |
int netns_grant_fd __attribute__((cleanup(autoclose))) = openat(-1, "/run/netns-grant", O_PATH | O_DIRECTORY | O_CLOEXEC); | |
if(netns_grant_fd < 0) { | |
fprintf(stderr, "openat(-1, \"/run/netns-grant\"): %s\n", strerror(errno)); | |
return 1; | |
} | |
struct stat sb; | |
if(fstat(netns_grant_fd, &sb) < 0) { | |
perror("fstat"); | |
return 1; | |
} | |
if(sb.st_uid != 0 || sb.st_mode & ~0744 != 0) { | |
fprintf(stderr, "/run/netns-grant has invalid permissions: must be owned by root and writable only by root"); | |
return 1; | |
} | |
char *user_path __attribute__((cleanup(autofree))) = NULL; | |
if(asprintf(&user_path, "%" PRIdMAX, (intmax_t) geteuid()) < 0) { | |
user_path = NULL; // contents are undefined after asprintf | |
perror("asprintf"); | |
return 1; | |
} | |
// Open the "/run/netns-grant/{user-id}" directory so that we can safely lookup | |
// the requested netns name. | |
int netns_grant_user_fd __attribute__((cleanup(autoclose))) = openat(netns_grant_fd, user_path, O_PATH | O_DIRECTORY | O_CLOEXEC); | |
if(netns_grant_user_fd < 0) { | |
fprintf(stderr, "openat(%d, \"%s\"): %s\n", netns_grant_fd, user_path, strerror(errno)); | |
return 1; | |
} | |
if(fstat(netns_grant_user_fd, &sb) < 0) { | |
perror("fstat"); | |
return 1; | |
} | |
if(sb.st_uid != 0 || sb.st_mode & ~0744 != 0) { | |
fprintf(stderr, "/run/netns-grant/{user-id} has invalid permissions: must be owned by root and writable only by root"); | |
return 1; | |
} | |
// Open the requested "/run/netns-grand/{user-id}/{argv[1]}" netns if it exists. | |
int netns_fd __attribute__((cleanup(autoclose))) = openat(netns_grant_user_fd, argv[1], O_RDONLY | O_NOFOLLOW | O_CLOEXEC); | |
if(netns_fd < 0) { | |
fprintf(stderr, "openat(%d, \"%s\"): %s\n", netns_grant_user_fd, argv[1], strerror(errno)); | |
return 1; | |
} | |
// Switch into the netns, this will fail if the opened file is not a network namespace. | |
if(setns(netns_fd, CLONE_NEWNET) < 0) { | |
perror("setns"); | |
return 1; | |
} | |
execvp(argv[2], &argv[2]); | |
perror("execvp"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment