1

Right now I am doing a lot of.

local env = {
    print = print,
}

setfenv(func, env) and then using metamethods to lock propertys on Instances, but it is really inefficient and has lots of bypasses. I googled it and everything I find is the same as this: unworking.

  • 1
    What "bypasses" are you referring to? Sandboxing means that the script can only access the stuff you explicitly allow. Given the code you show, `func` will only be able to access `print` and whatever parameters you call it with. – Nicol Bolas Dec 21 '15 at 02:53
  • "Let's say I wanted to give access to getfenv() aswell." If you don't want people to walk into your home, you don't give them keys. The same goes here: if you don't want people to break out of the sandbox, *don't* give them the tools to do so. So any good sandbox will prevent you from using `getfenv`. – Nicol Bolas Dec 21 '15 at 03:09
  • @Nicol But a better sandbox would allow sandboxxed access to *anything*. When I think of a sandbox I think of as if it's my own miniture world where I can do things safely, without access to the "parent environment". For example getfenv(2) would error because the "parent environment" doesn't exist. (Well it does, but this shouldn't seen from inside the sandbox.) – warspyking Dec 21 '15 at 20:11
  • So I think an improvement to your metaphor is *If you don't want people to walk into your home, don't show them where it is.* – warspyking Dec 21 '15 at 20:12
  • I don't have to worry about bytecode; ROBLOX already removed it. – user5701868 Dec 22 '15 at 13:43
  • @warspyking are you able to help me? – user5701868 Dec 23 '15 at 01:42
  • @user Me? Nah, I don't have the time to do that. – warspyking Dec 23 '15 at 02:08

1 Answers1

7

In Lua 5.1, sandboxing is pretty simple. If you have a Lua script in a file somewhere, and you want to prevent it from accessing any functions or anything other than the parameters you provide, you do this:

local script = --Load the script via whatever means. DO NOT RUN IT YET!
setfenv(script, {})

script is now sandboxed. It cannot access anything other than the values you directly provide. Functions it creates cannot access anything outside of this sandbox environment. Your original global environment is completely cut off from them, except for what you permit it to access.

Obviously you can put whatever you like in that table; that table will contain whatever globally accessible stuff you like. You should probably give Lua scripts access to basic Lua standard library functions; most of those are pure functions that can't do anything unpleasant.

Here's a list of Lua standard library stuff that you must not give the user access to, if you want to maintain the integrity your sandbox:

  • getfenv. There are valid reasons for a user to be able to setfenv, so that it can create mini-sandboxes of its own within your sandbox. But you cannot allow access to the environment of any functions you put in the sandbox if you want to maintain the integrity of the sandbox.
  • getmetatable: Same reasoning as above; setting metatables is OK. Though malicious code can break an object if they change its metatable, but malicious code can break your entire system just by doing an infinite loop.
  • The entire debug library. All manner of chicanery is possible through the debug library.

You also apparently need to solve this problem that Lua 5.1 has with loading bytecode from within a Lua script. That can be used to break the sandbox. Unfortunately, Lua 5.1 doesn't really have good tools for that. In Lua 5.2+, you can encapsulate load and loadfile, such that you internally pass "t" as the mode parameter no matter what the user provides. But with Lua 5.1, you need some way to encapsulate load et.al. such that you can tell when the data is text and when it's not. You could probably find the code that Lua uses to distinguish bytecode from text by reading the Lua source.

Or you can just disallow load and its friends altogether.

If you want to prevent the user from doing ugly things to the system, then disallow the os and io libraries.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I may be worth mentioning in "must not give access to" section that disabling `loadstring` or at least loading bytecode with `loadstring` is also needed: https://gist.github.com/corsix/6575486 – Paul Kulchenko Dec 21 '15 at 18:29
  • @PaulKulchenko: Thanks for the note. I assume that LuaJIT does not have this specific problem, due to using a different bytecode format (though it may be possible to use LuaJIT bytecode to break the sandbox). – Nicol Bolas Dec 21 '15 at 18:38
  • 1
    @NicolBolas You mean like this? https://www.corsix.org/content/malicious-luajit-bytecode ;-) No libraries loaded. (Except jit (for jit compilation), but it's not referenced/used in the code.) For any kind of sandboxing, make sure to avoid all bytecode loading. – nobody Dec 21 '15 at 20:22
  • 2
    Unless you're setting time limits at the OS level, another thing that should be removed or restricted is access to the string libraries' pattern matching functions (and remember the metatable on strings!). (`debug.sethook` for abort won't help, you'll need to stop long-running C functions.) Example: `function slow(i) return ("a"):rep(i):match(("a+"):rep(i-1)..'a') end` – `slow( 26 )` takes about 1.5s on my computer, and every increment by one doubles the time. (So `slow(64)` would already run for about 13k years…) – nobody Dec 21 '15 at 20:38
  • `getmetatable` is fine to expose to sandboxes, as long as metatables that you don't want the sandboxed code to have access to have a non-nil `__metatable` field. – Colonel Thirty Two Dec 21 '15 at 22:08