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.

  • 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 DL Aug 18 '16 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ᴄʟ. – 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 DL Aug 18 '16 at 16:44
  • oops, correct. They're declared in a manifest or bundled with the app, though. – Steve DL Aug 18 '16 at 16:46
-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.

  • 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(). – 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.