7

If I want to disable execution of programs on a filesystem, I can use the noexec mount option.

However, this doesn’t works with dynamic libraries loaded throughdlopen(). So what is the way to do it with nacl or seccomp ? (this is for untrusted bytecode sandboxing, I don’t want users being able to execute shared libraries they uploaded)

In the same time, libraries like gconv needs to be able to execute their.so modules.
fork()ing or exec()uting other programs programmatically is already disabled.

10
  • I know such way exists since Google app engine uses ɴaᴄʟ in order to prevent execution of shared object files that can be uploaded by it’s users. Commented Aug 14, 2016 at 17:17
  • NaCl requires static linking, it's not what you want. Commented Aug 18, 2016 at 16:40
  • @SteveDL : do not confuse static lining and dynamic linking at startup. ɴaᴄʟ does not preventdlopen()at runtime. And in fact, the gconv modules (which are loaded with theGCONV_PATHafter the program started) of Google app engine are linked against ɴaᴄʟ. Commented Aug 18, 2016 at 16:43
  • I would say a proper answer would depend on what exactly you control and don't control in the execution environment. You can simply override dlopen and implement your own approach to limit the paths that can be given to it. SELinux and other LSMs may also be able to intercept dlopen(). Seccomp might, but afair it's not easy to use seccomp to process string arguments. Otherwise build a loader a la libdetox to rewrite calls to whatever it is dlopen() does prior to executing the process. Commented Aug 18, 2016 at 16:44
  • oops, correct. They're declared in a manifest or bundled with the app, though. Commented Aug 18, 2016 at 16:46

1 Answer 1

1

However, this doesn’t works with dynamic libraries.

It does work for dynamic libraries. If a shared object is located on a partition which is mounted with the noexec option, it cannot be loaded, otherwise noexec would be quite useless.

$ grep -E '/tmp |ext4' /proc/mounts
/dev/mapper/root_crypt / ext4 rw,nodev,noatime,data=ordered 0 0
tmpfs /tmp tmpfs rw,nosuid,nodev,noexec,noatime,size=2031200k 0 0
$ cat > main.c
#include <stdio.h>

void main(void)
{
        printf("%d\n", getpid());
}
$ gcc -o main main.c
$ ./main
4950
$ cat > shared.c
int getpid(void)        
{
        return 1;
}
$ gcc -shared -fPIC -o shared.so shared.c
$ LD_PRELOAD=./shared.so ./main
1
$ mv shared.so /tmp
$ LD_PRELOAD=/tmp/shared.so ./main
ERROR: ld.so: object '/tmp/shared.so' from LD_PRELOAD cannot be preloaded (failed to map segment from shared object): ignored.
4978

If you want to use seccomp to prevent executing untrusted shared objects, you can do that by restricting calls to mmap(), mprotect(), and obviously execve(). Make sure that the process can't map anything with PROT_EXEC. Restricting files is not enough, because any arbitrary data can be passed to mmap() through a file descriptor, and mprotect() can change the permissions of arbitrary pages of memory to make them executable. Additionally, since this example is a blacklist rather than a whitelist, you'll also have to deny access to some ptrace() arguments, because they can be used to break out of a seccomp sandbox.

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mmap), 1,
    SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC));
if (rc == -1)
    goto out;

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCESS), SCMP_SYS(mprotect), 1,
    SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC));
if (rc == -1)
    goto out;

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCESS), SCMP_SYS(execve), 0);
if (rc == -1)
    goto out;

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 1,
    SCMP_A0(SCMP_CMP_EQ, PTRACE_POKEUSER));
if (rc == -1)
    goto out;

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 1,
    SCMP_A0(SCMP_CMP_EQ, PTRACE_SETREGS));
if (rc == -1)
    goto out;

rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 1,
    SCMP_A0(SCMP_CMP_EQ, PTRACE_SETREGSET));
if (rc == -1)
    goto out;

There are probably ways to bypass this seccomp snippet that I haven't thought about, so this is just to give you an idea of what it takes.

I've never used NaCl so I can't say how you'd restrict execution with it.

2
  • 1
    Hemm yes, it prevent dynamic linking from the ᴇʟꜰ header along LD_LIBRARY_PATH. But it doesn’t prevent copying the library in memory as regular file, parsing the ᴇʟꜰ header, and mmap() it, all of this treating it as regular file (that way is used to implementdlopen()on hp‑ux and other systems). This is also why you don’t need a library to have the executable in order to perform dlopen(). Commented Aug 15, 2016 at 14:43
  • Make sure that the process can't map anything with PROT_EXEC. But I don’t want to disable dlopen(), I just want to restrict it’s behviour with some files (which is what Google app engine does). Commented Aug 16, 2016 at 13:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .