Running untrusted code (sandboxing in CoreCLR)


#1

Hi there! I’m doing some research about running untrusted C# code in a sandbox. I’ve hit some roadblocks, and I’m hoping someone here could point me in the right direction.

I’m building a game that builds upon user code. You should be able to download code from other creators and run it on your machine, without risk of bad things happening to your computer. I need to make sure there’s no file operations, no network communication, etc. It’s been done many times before, but usually you see devs using Lua or some own script language for this. I prefer C# for a lot of reasons, so I’m trying to find a solution for using it safely.

I took a look at Unity (http://unity3d.com/). They use C# for scripting. For web applications, I think they made a stripped version of Mono that excludes any functionality that could let you mess with the user’s computer. I’m trying to get exactly that, but with CoreCLR as base instead. Does this seem like a reasonable approach?

Also, is it possible to sandbox code somehow in CoreCLR? AppDomains seems to be missing so I’m not sure how you’d do it.


For anyone curious, here’s my setup so far, using the desktop CLR:

  • Process A:
  • Application: Main game application. Handles rendering, playing sounds, etc.
  • Process B:
  • Host: Builds the mod using Roslyn, then executes it in a sandboxed AppDomain. Mod talks to Host, then Host communicates with Application (and reverse for user input).
  • Mod: Untrusted code. Has access to an API for drawing, playing sounds, sampling input, etc, but not allowed to do file operations or anything else dangerous.

I’m running the mod on a separate process so the user code cannot shut down the main process by a stack overflow. It’s okay that they can crash the mod/host process, as long as the main application lives.

My problems with this approach, and why I’m thinking about a switch from the desktop CLR to CoreCLR:

  • Sandbox: Unsure if the sandbox is good enough. I get the vibes from the warning at the top of https://msdn.microsoft.com/en-us/library/bb763046(v=vs.110).aspx that I cannot trust the sandbox completely. Not sure if this is any different on CoreCLR, but at least I’d have the means to look under the hood.
  • Stability: I want to be very sure that the user can use my program without trouble, even if there are big future changes in the desktop CLR. I also want to be completely sure that any such future changes won’t poke holes through my sandbox. Bundling a fixed version of the CLR seems like a good idea.
  • Portability: This solution runs on windows, but AFAIK there’s no working sandbox in Mono. I really would like multiplatform support.

#2

Sorry that I don’t have much of an answer for this, but I’ve been exploring something similar in my spare time. You are correct that you cannot use AppDomain with .NET Core, which makes this a much bigger challenge.

From what I can gather based on a small amount of my own research*, you would have to use a forked version of the runtime (and maybe libraries?) and stub out I/O. It may also be worth looking at Native Client (NaCl), which does something sort of similar. A blog post about how the Go team at Google had to do this for their playground talks about this a bit and shows some of the relevant files. Hopefully this helps!

*note that this isn’t a whole lot, so take it as a grain of salt


#3

The word for security boundaries now is to use the ones that the OS provides, such as running in a separate process under a user with limited privileges.


#4

Hrm, I got some time to investigate the source for CoreCLR and I noticed that you can enter security settings when you start it all up (corerun.cpp, host->CreateAppDomainWithManager). I haven’t had time to experiment with this, but on the desktop CLR it seemed to do everything I want, e.g. disallowing code that can do anything bad.

I’m a bit confused why this functionality is even in the CoreCLR if it’s deemed too unsafe to be usable. I kind of see why it can be a problem in code with access to the entire .NET library, but does this recommendation apply to very constrained environments like the one I’m proposing?


#5

Interesting comparison with the Go Playground. So in their case, they restricted access to the following (at least, this is what they mentioned):

  • Time/Sleep
  • File system
  • Network

I don’t need to even expose this functionality for my scripts, and will probably do so by simply disallowing access to the specified assemblies (System.IO, System.Net, etc). The problem for me is whether there’s something I haven’t though about that can do bad things without me realizing it. Like a built-in language function that can access files on disk, or such. I know of at least two targets, file system and network, but are there any more I’m not thinking about? Anyone with more insight on this?

Also, bleroy: I’d rather avoid having a separate, limited-privilege process if I can, as I’d have to make specific solutions per platform. But, it’s the option I’ll go for if I can’t get anything else to work.


#6

Okay, if anyone else is interested, this is an interesting lead on the process sandboxing approach:

Basically running the Chromium sandbox (which is apparently very standalone-y) instead of trying to figure out all the voodoo and black magic around restricted tokens. Also, multiplatform support!

Still haven’t gotten a test up and running, but I’ll write more when I do.


.NET Foundation Website | Blog | Projects | Code of Conduct