6

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.

user2284570
  • 1,402
  • 1
  • 14
  • 33
  • 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. – user2284570 Aug 14 '16 at 17:17
  • NaCl requires static linking, it's not what you want. – Steve Dodier-Lazaro Aug 18 '16 at 16:40
  • @SteveDL : do not confuse static lining and dynamic linking at startup. ɴaᴄʟ does not prevent`dlopen()`at runtime. And in fact, the gconv modules *(which are loaded with the`GCONV_PATH`after the program started)* of Google app engine are linked against ɴaᴄʟ. – user2284570 Aug 18 '16 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. – Steve Dodier-Lazaro Aug 18 '16 at 16:44
  • oops, correct. They're declared in a manifest or bundled with the app, though. – Steve Dodier-Lazaro Aug 18 '16 at 16:46
  • @SteveDL :`I would say a proper answer would depend on what exactly you control and don't control in the execution environment`. If I told that I’m very intersted in the way Google app engine proceeds, it just means that anything that could work for Google app engine would work for me. What do you mean by`They're declared in a manifest or bundled with the app, though` ? Are you meaning that ɴaᴄʟ whitelist shared object files that can be loaded by design. In my case, [I’m using ɴaᴄʟ as glibc](https://chromium.googlesource.com/native_client/nacl-glibc/). – user2284570 Aug 18 '16 at 17:05
  • That is the implication yeah. I would look at the NaCl source code for clues. – Steve Dodier-Lazaro Aug 19 '16 at 18:20
  • @SteveDL : but, the documentation only refers how to use the manifest file with chromium… There’s no [web browser involved at all in my case](https://chromium.googlesource.com/native_client/nacl-glibc/)… I can’t see any line handling manifest files in https://chromium.googlesource.com/native_client/nacl-glibc/ – user2284570 Aug 19 '16 at 18:36
  • You might want to find how Chromium handles the manifest then. Do they use some mount namespace for /usr/lib on top of which they only mount the files present in the manifest? That would be a way to solve your issue. – Steve Dodier-Lazaro Aug 22 '16 at 12:23
  • @SteveDL There's no web browser involved here. So no chromium. nacl is the libc. – user2284570 Aug 24 '16 at 06:34

1 Answers1

0

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.

landmark
  • 50
  • 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 implement`dlopen()`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()*. – user2284570 Aug 15 '16 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)*. – user2284570 Aug 16 '16 at 13:36