1

I want to safely run untrusted user-submitted code (for example a single c++ file, without anything but standard libraries, and without multithreading) on my server. I want the program to be able to read from an input file and write to an output file, but have no access to anything else ony my server. It should not be able to break anything on my system.

I tried to achieve this by:

  1. using chroot to separate the program from my file system
  2. starting the user program with the following c program, that should block unsafe syscalls
#include <stdio.h>
#include <stdlib.h>
#include <seccomp.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 0);

    seccomp_load(ctx) < 0;

    system("./userProgram");
}

Now my issue is that I didn't know how many other syscalls the program would make. Even for a really simple userProgram dozens of syscalls such as mmap, access, openat, pread64, set_robust_list, etc. were made.

Is it safe to allow all of these syscalls? And would I have to manually run potential user programs with strace to see which syscalls are made?

8
  • 3
    system() greatly increases the number of syscalls you need to allow, because instead of starting your child process directly you're starting a shell, and telling the shell to start the child in turn. Much safer to directly use execve(). Commented Aug 17, 2024 at 16:47
  • 3
    That said, in general you shouldn't be using seccomp as a sole security mechanism. Use it to close off specific attack vectors that require chroot -- it's not intended to be, and isn't suitable to be, comprehensive; you'll want to combine it with limited filesystem namespaces, process namespaces, &c. so that even though you can't close off open() &c., you can limit what content exists in the namespace to be opened. Commented Aug 17, 2024 at 16:49
  • 1
    In general, this is a place where as a beginner you're better off using a 3rd-party tool that combines seccomp with all the other security layers necessary to combine to a suitable solution. This might mean using something like bubblewrap or firejail, or for some use cases a full qemu virtual machine. Either way, you need to start by defining a threat model, and then decide which tools you need to protect against it -- don't start with the tool first. Commented Aug 17, 2024 at 16:51
  • 3
    And for running arbitrary user-submitted code, I'd absolutely go the qemu route; seccomp is simply not suited to the task. More than that, make sure you slim down your qemu virtual machine configuration to minimize attack surface -- no video devices, no network devices (especially make sure the default -net user functionality is disabled), snapshot mode to ensure that all writes are instance-specific, &c. Commented Aug 17, 2024 at 16:54
  • 1
    ...jumping back, system() is also a bad idea because it requires your process to be able to run /bin/sh. Since one of the very first things an attacker is likely to do is try to set up a reverse shell, that's something you want to lock off very quickly. Commented Aug 17, 2024 at 16:56

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.