FUSE - Filesystem in Userspace

#race-conditions#toctou#fuse#uaf
Vuln Classestoctou
Givesdeterministic-race-win

This is another great technique to stall a kernel thread, similar to what we did with userfaultfd. However, unlike userfaultfd, which has been hardened and cannot be used in modern kernels, this is still an exploitable technique.


What is it?

Normally, when we open a file, the kernel handles everything:

VFS → ext4 driver (or whatever filesystem) → disk

The filesystem driver lives inside the kernel.

FUSE flips this.

It lets a userspace program act as the filesystem driver. The kernel asks your program, and your program replies with whatever it wants.


Why is it needed?

  • sshfs — mount a remote server over SSH as a local directory. Used by millions of developers daily.
  • rclone mount — mount Google Drive, S3, Dropbox, OneDrive as local directories. Huge in cloud workflows.
  • And many more.

So now it is clear why FUSE cannot simply be removed like we did with userfaultfd.


How to?

  1. Create a userspace filesystem handler
    1. Don’t worry, researchers already have it for us.
    2. https://github.com/LukeGix/FUSEFs_exploitation/blob/main/fusefs.c
  2. Compile the code
    1. apt install libfuse3-dev fuse3
    2. gcc fuse.c -o fusefs $(pkg-config fuse –cflags –libs)
    3. ./fusefs /tmp/fusemount/
  3. Our userspace FUSE daemon is now successfully running and can handle read and write requests to files inside this filesystem.

In the exploit, open a file inside the FUSE filesystem and mmap() it. The resulting memory mapping can then be used as a userspace address involved in kernel operations such as copy_from_user() or copy_to_user(). This is conceptually similar to the earlier userfaultfd technique, where an anonymous page was mmap()‘d and access to that page could cause the kernel thread to block while waiting for userspace-controlled handling. With FUSE, accessing pages backed by a FUSE filesystem can similarly cause the kernel to communicate with the userspace filesystem daemon, giving the userspace handler control over when the corresponding filesystem request is serviced.

//EXAMPLE
int main(){
	int fd = open("/tmp/fusemount/lol", O_RDWR);
	void *addr = mmap(0x1000, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
	printf("Triggering read from FUSE\n");
	//THIS will trigger the call to FUSE read (custom handler)
	printf("%s\n", (char *)addr);
}

Referencess