Last active
April 5, 2021 18:55
-
-
Save x4fx77x4f/e997c64a57389349726e783b8c351554 to your computer and use it in GitHub Desktop.
Script to check for problems with getfenv/setfenv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@shared | |
local env = {} | |
env.getfenv = getfenv | |
local function func(...) | |
local fenv = getfenv(...) | |
return fenv | |
end | |
setfenv(func, env) | |
local labels = { | |
[_G] = "_G", | |
[env] = "env" | |
} | |
print(getfenv(func)) | |
-- 0 should be getfenv's environment | |
-- 1 should be the caller's environment | |
-- 2 should be the caller's caller's environment | |
-- etc... | |
-- Once you exhaust the stack, you get "bad argument #1 to 'getfenv' (invalid level)" | |
for i=0, 10 do | |
local fenv = func(i) | |
print(CLIENT and "CLIENT" or "SERVER", i, labels[fenv] or fenv and "???" or "nil") | |
end | |
assert(func() == env) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@name getfenv/setfenv pentest | |
--@shared | |
local print = print | |
local pcall = pcall | |
local getfenv = getfenv | |
local setfenv = setfenv | |
local _G = _G | |
dummy = true | |
local passed, total = 0, 0 | |
local function test(desc, cond) | |
total = total+1 | |
passed = passed+(cond and 1 or 0) | |
print((cond and "pass. " or "FAIL! ")..desc) | |
end | |
print("-----starting "..(CLIENT and "client" or "server").."side test-----") | |
local workedEver = false | |
local proxyResult | |
local function proxy(i) | |
proxyResult = getfenv(i) | |
end | |
for i=-2, 10 do | |
local success, err = pcall(proxy, i) | |
--print(i, success, err, proxyResult) | |
test("positive getfenv("..i..") doesn't leak", proxyResult == _G or proxyResult == nil) | |
--if proxyResult == _G then print(i) end | |
workedEver = workedEver or proxyResult == _G | |
proxyResult = nil | |
end | |
if not workedEver then | |
print("warning: getfenv(n) never returned _G. This is not a vuln, but it should be looked into") | |
end | |
--print(getfenv(), _G) | |
test("getfenv() == _G", getfenv() == _G) | |
local failEnv = setmetatable({}, { | |
__index = function(self, k) | |
error("If you are reading this, shit has hit the fan!") | |
end, | |
__newindex = function(self, k, v) | |
error("If you are reading this, shit has hit the fan!") | |
end | |
}) | |
local workedEver = false | |
for i=-2, 10 do | |
local oldSuccess, oldErr = pcall(getfenv, i) | |
local success, err = pcall(setfenv, i, failEnv) | |
--print(i, success, err) | |
if err ~= _G then | |
test("out of bounds setfenv("..i..") fails", not success) | |
else | |
workedEver = true | |
test("in bounds setfenv("..i..") works", success) | |
test("in bounds setfenv("..i..") doesn't leak", err == _G or err == nil) | |
end | |
if oldSuccess then | |
pcall(setfenv, i, oldErr) | |
end | |
end | |
if not workedEver then | |
print("warning: setfenv(n) never succeeded. This is not a vuln, but it should be looked into") | |
end | |
local dofile = dofile | |
local success, err = pcall(setfenv, dofile, failEnv) | |
test("setfenv(out of bounds function) fails", not success) | |
local function func() print(dummy) end | |
local success, err = pcall(setfenv, func, failEnv) | |
test("setfenv(in bounds function) works", success) | |
test("setfenv(in bounds function) doesn't leak", err == func) | |
-- I've heard from somewhere that if a metamethod is called from outside | |
-- of the sandbox, there is extra potential for exploitation. | |
local tested = false | |
local magic = setmetatable({}, { | |
__add = function(a, b) | |
if not tested then | |
tested = true | |
for i=-10, 10 do | |
local success, env = pcall(getfenv, i) | |
test("metamethod getfenv("..i..") doesn't leak", env == _G or not env or not success) | |
end | |
end | |
return 0 | |
end | |
}) | |
local a, b = Color(), Color() | |
a[1] = magic | |
b[1] = magic | |
local c = a+b | |
assert(tested, "failed to test metamethod") | |
print("-----end of "..(CLIENT and "client" or "server").."side test-----") | |
print((passed == total and "passed all tests. " or "FAILED SOME TESTS! ").."passed "..passed.." out of "..total) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@name getfenv suicide | |
--@shared | |
-- This is a test to make sure modifying whitelistedEnvs externally works. | |
-- I can't think of any good reason WHY an external script should be | |
-- able to modify whitelistedEnvs, but extra options probably won't | |
-- hurt anything -- as long as no one does something stupid like: | |
-- Player(2):GetEyeTrace().Entity.instance.whitelistedEnvs[_G] = true | |
timer.create('', 1, 0, function() | |
local env = getfenv(0) -- the environment of getfenv itself, according to lua docs | |
if env == nil then | |
print(SERVER and "SERVER" or "CLIENT", "no changes detected...") | |
else | |
print(SERVER and "SERVER" or "CLIENT", "CHANGE SUCCESS! WHY WOULD YOU EVER DO THIS?") | |
timer.remove('') | |
end | |
end) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@name setfenv black magic | |
--@server | |
local code = [[ | |
local location = 1 | |
local env = getfenv(location) | |
setfenv(location, { | |
PASS = true, | |
FAIL = true | |
}) | |
return PASS, env | |
]] | |
local func, err = (CompileString or loadstring)(code) | |
assert(type(func) == 'function', err or func) | |
local _G = _G | |
local print = print | |
local pass, env = func() | |
local fail = FAIL | |
if pass and not fail then | |
print("pass") | |
elseif not pass and fail then | |
print("FAIL: setfenv set its caller") | |
elseif pass and fail then | |
print("FAIL: setfenv set both itself and its caller???") | |
elseif not pass and not fail then | |
print("FAIL: setfenv did absolutely nothing") | |
end | |
print(env == _G and "env == _G" or "env ~= _G") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@name setfenv RAM eater | |
--@client | |
local math_random = math.random | |
local pcall = pcall | |
local quotaUsed = quotaUsed | |
local string_char = string.char | |
local table_concat = table.concat | |
local function dummy() end | |
local threshold = quotaMax()*0.5 | |
hook.add('think', '', function() | |
local str = {} | |
local i = 1 | |
while quotaUsed() < threshold do | |
str[i] = string_char(math_random(0, 255)) | |
i = i+1 | |
end | |
str = table_concat(str, '') | |
pcall(setfenv, dummy, str) | |
end) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@shared | |
local prefix = SERVER and "SERVER" or "CLIENT" | |
local env = { | |
FAIL = true, | |
print = print | |
} | |
local lazy = setmetatable({ | |
env | |
}, { | |
__mode = 'v' | |
}) | |
local function guinea() | |
print(prefix, FAIL and "FAILED TWICE", "didn't fail twice wtf?") | |
end | |
setfenv(guinea, env) | |
env = nil | |
--guinea = nil | |
hook.add('think', '', function() | |
if #lazy ~= 1 then | |
print(prefix, "FAILED ONCE") | |
guinea() | |
hook.remove('think', '') | |
end | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment