Currently, I am writing a sandbox in Lua. It's working so far, but I could use getfenv
for that function to get the scope outside the sandbox. For my sandbox, I filled an empty table with trusted functions and libraries. However, with functions such as print
, you could use getfenv
to get the global variable(s) in that scope. For example:
asd = "asd"
assert(pcall(assert(load([[
print(getfenv(print).asd) -- "asd"
]], nil, "t", {print = print, getfenv = getfenv}))))
This can obviously allow the "adversary" to bypass the sandbox.
You need to write a wrapper for getfenv
that prevents leaking the unsandboxed environment, like MediaWiki's Scribunto extension does:
local function my_getfenv( func )
local env
if type( func ) == 'number' then
if func <= 0 then
error( "'getfenv' cannot get the global environment" )
end
env = old_getfenv( func + 1 )
elseif type( func ) == 'function' then
env = old_getfenv( func )
else
error( "'getfenv' cannot get the global environment" )
end
if protectedEnvironments[env] then
return nil
else
return env
end
end
The gist of it is that you check the returned environment, and if it's a protected environment (e.g., _G
), then refuse to return it. The only trick is handling the argument, since it means different things depending on its type, and it's sensitive to the extra function in the call stack.