5

For the purpose of this question, let us assume that there is a file (/path/to/file) in the filesystem with a certain content like this:

$> cat /path/to/file
this is the content of the
file /path/to/file 

I am happy with almost all processes running (and having reading access to this file), reading this content.
There should however (and this is the core of the question) be a specific process, which when reading from /path/to/file should be provided with different file content.

$> cat /path/to/file
this is the DIFFERNT content of the
file /path/to/file 

How can I somehow spoof the file content for a specific process?

Some guesses of mine, which road to take would be solitions related to :

  • symlinking trick
  • (linux) namespaces (filesystem namespace) trick
  • chroot trick
  • $LD_PRELOAD hooking trick
  • overlayfs

My platform is GNU/linux, but if there is a POSIX way to achieve it that would be even better :)

Update

What would be a good solution/answer?

A criterium for a good solution/answer would be that it is possible to achieve "different file content for specific process", without needing interaction of the root user, eventhough the ideally the spoofed file should not be user-writeable in the first place.

Another good criterium would be that the modification of the file process shown to the process is specific, and ideally without sort of race-condition.

Background

Mozilla firefox uses 2 zip archives /usr/lib/firefox/omni.ja and /usr/lib/firefox/browser/omni.ja, which contain a fair deal of firefox' code (mostly the stuff writen in Javascript), by spoofing the file I would be able to modify my version of firefox (include some features I cannot anymore implement as a extension, provided dropped XPCOM support and disliked addon-siging coercion)

2 Answers2

7

Yes, mount namespaces is one approach:

$ cat file
foo
$ cat other-file
bar
$ sudo unshare -m zsh -c 'mount --bind other-file file; USERNAME=$SUDO_USER; cat file'
bar
$ cat file
foo

Above using zsh to restore the uid/gids of the original user for the hijacked cat command.

  • Thank you. Still some question left: I remember that - some time ago - using the unshare for stuff related to user_namespaces(7) was not accessible for non-root-priveledged users, yet here we we use unshare only for the mount_namespaces(7). Can we drop the sudo somehow? – humanityANDpeace Apr 25 '17 at 14:33
  • @humanityANDpeace, you could do unshare -Urm sh -c 'mount --bind other-file file; cat file' but then cat runs as the root user of that new user namespace. You may be able to go back to the original uid, but probably not with the unshare utility. – Stéphane Chazelas Apr 26 '17 at 08:29
  • thanks Stéphane Chazelas, thanks for responding, I see that to unshare as non-root implies using the Usernamespace, something which on debian, ubuntu and arch seems either not enabled in kernel or needing a kernel knob, hence being farther away than a /etc/sudoers exception. Guess I have to wait till user_namespace gains further traction, thanks – humanityANDpeace Apr 26 '17 at 16:31
4

Here's a way to do it with preloading to wedge some code between the program and the system library, by a small piece of code. This assumes that the program is a dynamically linked binary, or a script executed by a dynamically linked binary (i.e. it isn't statically linked). Write the following code to a file override_fopen.c:

#include <dlfcn.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef FROM
#error "Define FROM to the path to override in double quotes, e.g. -DFROM='\"/bad\"'"
#endif
#ifndef TO
#error "Define TO to the path to use instead in double quotes, e.g. -DFROM='\"/good\"'"
#endif
FILE *fopen(const char *path, const char *mode) {
    void *(*original_fopen)(const char *, const char *) = dlsym(RTLD_NEXT, "fopen");
    if (!strcmp(path, FROM)) {
        path = TO;
    }
    return original_fopen(path, mode);
}
int open(const char *path, int oflag, ...) {
    int (*original_open)(const char *, int, ...) = dlsym(RTLD_NEXT, "open");
    int ret;
    va_list args;
    if (!strcmp(path, FROM)) {
        path = TO;
    }
    va_start(args, oflag);
    if (oflag & O_CREAT) {
        ret = original_open(path, oflag, (mode_t)va_arg(args, mode_t));
    } else {
        ret = original_open(path, oflag);
    }
    va_end(args);
    return ret;
}

Compile with the following command (that's for Linux, other Unix variants may require different options). Note the quotes around the path you want to override.

gcc -DFROM='"/path/to/file"' -DTO='"/path/to/alternate/content"' -D_GNU_SOURCE -O -Wall -fPIC -shared -o override_fopen.so override_fopen.c -ldl

Run the program as follows (on OSX, use DYLD_PRELOAD instead of LD_PRELOAD):

LD_PRELOAD=./override_fopen.so ./myexe

This only works if the program is calling the fopen or open library function. If it calls some other function, you'll need to override that one. You can use ltrace to see what library calls the program makes.

  • I guess with regards to what I hoped for, i.e. an answer that does work without root priveledge involvement, the preload solution seems preferable, yet it will only work if the open syscall is issued via using dynamic linking feature. I will accept this answer once I made sure the namespace mount option of @Stéphane Chazelas' answer is impossible without root access of sorts. both answers have their long- and shortcomings, thanks – humanityANDpeace Apr 26 '17 at 05:18