40

I want to make a little programming puzzle on my website. There's going to be a task. The user will be asked to upload a C++ source file with their solution. The file should be compiled, run with some input and checked if it produces right output. What are the security risks? How do I make sure that the uploaded file won't be able to do anything malicious?

Alternatively, there are sites like Tutorials Point that let you compile and run C++ code. How do they manage the security?

Pharap
  • 138
  • 1
  • 8
Jen
  • 503
  • 4
  • 5
  • 16
    https://repl.it/languages/cpp has an online C++ sandbox. their free API will give you 25000 compiles with 10 concurrent users – J.A.K. Feb 06 '17 at 05:16
  • See also https://stackoverflow.com/questions/4187229/is-it-possible-to-sandbox-and-run-c-or-c-sharp-code-thats-entered-in-a-textfi – Colonel Panic Feb 06 '17 at 15:06
  • @ColonelPanic: That's only one of [many](https://stackoverflow.com/search?q=online+judge+sandbox), and one of the poorer ones at that. – Ben Voigt Feb 07 '17 at 19:11
  • You need to run it in sandbox. Linux SELinux sandbox would do the job of blocking any I/Os to local filesystem. However you'd need also to assure limits like RAM and concurrency, Linux does solve these issues as well. Finally, you'd have also hardened kernel to prevent kernel exploits of some sort. – Aria Feb 10 '17 at 08:52

3 Answers3

53

It is impossible to analyze a program to find out if it will do anything malicious. That is true regardless of whether you are attempting to analyze the source or compiled code.

The way to do what you are asking for is done by compiling and running the code in a sandbox. Once the program has terminated (or after a timeout you have decided upon) you destroy the sandbox.

The security of such a construction is as secure as the sandbox you are using. Depending on the requirements of the code you need to run the sandbox could be either something simple like Linux secure computing mode, or something complicated like a full blown virtual machine - ideally without network connectivity.

The more complicated the sandbox you need the larger risk of a security vulnerability in the sandbox undermining an otherwise good design.

Some languages can safely be compiled outside a sandbox. But there are languages where even compiling them can consume unpredictable amount of resources. This question on a sister site shows some examples of how a small source code can blow up to a large output.

If the compiler itself is free from vulnerabilities it may be sufficient to set limits on the amount of CPU, memory, and disk space it is allowed to consume. For better security you can run the compiler inside a virtual machine.

Obviously these methods can be combined for an additional layer of security. If I were to construct such a system I would probably start a virtual machine and inside the virtual machine use ulimit to limit the resource usage of the compiler. Then I would link the compiled code in a wrapper to run it in secure computing mode. Finally still inside the virtual machine I would run the linked executable.

kasperd
  • 5,402
  • 1
  • 19
  • 38
  • 3
    One thing that might bear pointing out is that the machine should be in a quarantined network segment and if possible not allowed to initiate outgoing network connections. Being raided by the FBI because you attempted to hack into whatever the current administration is sensitive about is no fun. – DRF Feb 06 '17 at 08:01
  • 2
    @DRF Actually it should not be given network connectivity at all. I thought about that but forgot to include it in my answer. I have included it now. – kasperd Feb 06 '17 at 10:38
  • That is of course an ideal situation but I doubt the OP has access to automated airgap technology and he seems to want an automated system. Ideally I think you have some sort of DMZ scheme where the internal system (which compiles and executes the software in a virtual machine built for it from scratch which has no network access or only limited access to other VM's if the code has need of this) only has access to the upload server which hands it the code to use. – DRF Feb 06 '17 at 12:51
  • 2
    @DRF The VM should not have network connectivity. The physical host it runs on is going to need network connectivity. I think most VM solutions can do that. – kasperd Feb 06 '17 at 21:35
  • 2
    @DRF It should be pretty simple to disable network access in a VM. It's not an airgap but it's a virtual airgap. (Just like you have virtual disks and so on) – user253751 Feb 06 '17 at 22:29
  • "But there are languages where even compiling them can consume unpredictable amount of resources." ← indeed there are languages that allow you to run arbitrary code at compile time, hooray metaprogramming – Ben Millwood Feb 07 '17 at 15:35
28

This is a really hard problem, and one all online code judges has to solve. Basically, you are asking how you can prevent somebody who can execute arbitrary code on your machine from taking it over.

I have been coding on an online judge (Kattis) for a decade or so, and here are some of my experiences from building the security solutions for this kind of scenario:

  1. Very early versions was based on a solaris jail. Turns out that you can cause quite a lot of havoc inside a jail, and it does not give you the granularity you need.
  2. We implemented a system call filtering solution using ptrace. This introduces a very (several context switches) large overhead on system call, and keeping the security profile in sync as compilers and runners change is a nightmare. The final nail in the coffin for this solution was threading. If you allow threading, an application can use a thread to rewrite the systemcall between the inspection and the execution, and for example Java requires threading.
  3. These days we use a combination of cgroups and namespaces. This gives a surprisingly low overhead, and as these are part of the security primitives in the Linux kernel they are robust. Have a look at MOE Isolate for an idea of how this can be done. Isolate most likely solves your problem.

Note that while containers, such as docker or virtual machines, are popular, they may not be the best choose for a security solution in this kind of scenario. It is hard to get the fine-grained control and resource monitoring you probably want, it is hard to prevent a malicious process from screwing around inside your container and, starting and destroying the containers has a lot of overhead.

pehrs
  • 381
  • 2
  • 2
  • Have you used seccomp for syscall filtering – does it have the same kind of problems? – user1686 Feb 07 '17 at 06:44
  • @grawity Overhead is better with seccomp compared to our old ptrace solution. However, it is still much worse than cgroups and namespaces, and writing the rulesets is still a problem. Also note that the overhead of seccomp typically increases with the number of rules in the ruleset, which can complicate things significantly when it comes to desigining a good ruleset for the filesystem. – pehrs Feb 07 '17 at 10:28
7

In the particular case of a puzzle website, consider the alternative: don't bother. Ask participants to upload the output so you don't have to run untrusted code. This saves you computing power, avoids a security risk, and allows people to compete in any language. If there's a prize at stake, you can verify the winning entry later manually.

If the form of your puzzle allows, you can frustrate copy-and-paste solutions by generating random inputs and writing a verifier. This is how Google Code Jam works. See https://code.google.com/codejam/problem-preparation.html#iogen

Colonel Panic
  • 2,214
  • 2
  • 22
  • 23
  • 4
    One should still request the source code alongside with input/output, otherwise a dishonest participant can gamble the system by submitting the solution before their code is finished. – Dmitry Grigoryev Feb 06 '17 at 17:58
  • 1
    Of course I was also considering this, but the serverside execution gives you these options: 1) Control what the user can use of the library functions. Eg. if the task is to implement some sort of linked list, you would want to disable std::list. 2) Check the time and memory complexity of the program user wrote. 3) Do more than one I/O test 'at once' -> user upload once and you can run tests for several levels of input complexity. – – Jen Feb 09 '17 at 13:26