How to secure replacment? - lua

So I need to secure my code.
My code is obfuscated and im using load(tostring(resultServerr))() but someone can do load = print on top and it will print everything from my code..
I have to secure it to avoid replacement but i dont know how.

If you use Lua 5.2 or higher, you can provide your own sandboxed _ENV table in 4th argument of load. If you use Lua 5.1 or 5.0 you can use setfenv, which works almost the same way as new _ENV.
local func, err = load(unsafecode, nil, nil, {})
if not func then print(err) return end
func()
or
local func, err = load(unsafecode)
if not func then print(err) return end
setfenv(func, {})
func()
Here I use empty table {} to protect from using all globals, adding new ones and overwriting existing. If you want to provide some functions, just add them into this table, they won't be removed from _G if they change it inside the sandboxed code.

It depends on how and where are you using lua environment and what normal user is allowed to do with lua?
If only you controls startup process of the server or programm and the user has no access to startup process, it is possible to do sandboxing on init state as suggested Spar, or something like:
init.lua:
function prepare()
local load = _G.load
_G.load_extension = function()
local obfuscated = get_extension()
load(obfuscated)
end
end
Original load is stored as local variable and can't be overwritten for load_extension in following lua scripts/functions.
If normal user can modify init scripts or can load external libraries (.dll/.so), you're out of luck to properly secure your code.
If the user has access to resultServerr content, you're out of luck hiding it, as this variable can be passed to any other process/function, not only print or load.

Overwrite print = nil then it can't print.
You can create your own hidden print function if you still need that capability; just remember what you called it, so you can access it later.
hidden_print = print
print = nil
print('this')
stdin:1: attempt to call a nil value (global 'print')
stack traceback:
stdin:1: in main chunk
[C]: in ?
hidden_print('that')
that

Related

Handling lfs.dir when access to directory is denied

The lfs module allows reading a directory using the lfs.dir iterator. If the directory cannot be accessed (e.g. because permissions do not allow it), an error is raised.
I could check that directory can be read like this:
local status, err = pcall(lfs.dir, "mydir")
if not status then
print("error occured", err)
else
for file in lfs.dir("mydir") do
print("file:", file)
end
end
However, this means calling lfs.dir twice. Not sure how much overhead that is, but given that I am working on a tool that scans huge directory structures very quickly, I would very much like to avoid it. Another solution would be to wrap the loop in a function and pcall that function. However, I was wondering whether the pcall can be built in the iterator itself, like creating a custom iterator as a "safe" wrapper around lfs.dir() that would allow me to silently ignore directories that cannot be read. How can I do that? And how much overhead would that be?
Using pcall
You don't have to look at thee implementation details of LFS to use pcall: The Lua reference manual tells you that there will be exactly 4 values evaluated to iterate a generic for:
The loop starts by evaluating explist to produce four values: an iterator function, a state, an initial value for the control variable, and a closing value.
Thus you can use pcall for any generic for iterator as follows:
local status, iterator_or_err, state, control_var, closing_val = pcall(lfs.dir, "mydir")
if status then
for file in iterator_or_err, state, control_var, closing_val do
print("file:", file)
end
else
print("error occurred", iterator_or_err)
end
Perhaps slightly more elegant would be capturing the return values in a table:
local t = {pcall(lfs.dir, "mydir")}
if t[1] then
for file in table.unpack(t, 2) do
print("file:", file)
end
else
print("error occurred", t[2])
end
Downside: This creates a garbage table. The most elegant solution is to use xpcall:
Using xpcall
Whenever you have to handle vararg returns, xpcall is handy because Lua will call a handler you provide with the status & the vararg.
xpcall(lfs.dir, function(status, ...)
if status then
for file in ... do
print("file:", file)
end
else
print("error occurred", ...)
end
end, "mydir")
Yes, you can invoke lfs.dir only once:
local status, err_or_iter, ud, v3, v4 = pcall(lfs.dir, "mydir")
if not status then
print("error occured", err_or_iter)
else
for file in err_or_iter, ud, v3, v4 do
print("file:", file)
end
end

Making local the _G variables & execute location

First doubt
For example, next can be stated as local very easily
local next = next
But how would it be with table.insert for example?
Making an anonymous function with table.insert inside on a local var would actually work?
Second doubt
Is it possible to know from where a function is being executed? This take us back to my first doubt, how can i ensure a _G variable is being executed locally
Really asking because besides default _G variables, i have few more added on my project
But how would it be with table.insert for example?
local table = table
how can i ensure a _G variable is being executed locally
It is still the same function value. You just added a local reference to it. Local variables can be looked up faster. You would have to call a function very often to really benefit from doing that.
This is opinionated but that opinion is shared among most programmers:
Don't waste time on premature optimization. Don't create local references for every global you encounter.
With load() you can give Lua code an own environment.
Normally (without that own environment) _G (5.1) or _ENV (since 5.3) is used.
Example you can play with...
> _VERSION
Lua 5.4
> load('do local tab, concat, insert = {}, concat, insert insert(tab, "Hello World!") return concat(tab) end', 'own_env', 't', {concat = table.concat, insert = table.insert})()
Hello World!
> load('do local tab, concat, insert = {}, concat, insert insert(tab, "Hello World!") return concat(tab) end', 'own_env', 't')()
[string "own_env"]:1: local 'insert' is not callable (a nil value)
stack traceback:
[string "own_env"]:1: in main chunk
(...tail calls...)
[C]: in ?
Source: https://www.lua.org/manual/5.4/manual.html#pdf-load

LuaJIT setfenv not appearing to set further function calls to the given environment

I'm attempting to sandbox some functions using setfenv, and I recieve the following output:
123
nil
Why is testValue when calling sandboxTest() nil, but it's 123 when it's accessed in callSandboxedTest()?
Using LuaJIT 2.1.0-beta2 (Lua 5.1)
function sandboxTest()
print(testValue)
end
local aNumber = 123
function callSandboxedTest()
setfenv(1, {
print = print,
testValue = aNumber,
sandboxTest = sandboxTest
})
print(testValue)
sandboxTest()
end
callSandboxedTest()
Environments aren't part of the call stack. Every function has its own environment. So sandboxTest has an environment, as does callSandboxTest. Changing the environment of one function has no effect on the environment of another.
sandboxTest will continue to use the default environment, so it will access the regular global table to find testValue. And since testValue is never set in the global table, sandboxTest will get nil.
That's why, when maintaining a sandbox, it is very important to carefully choose what functionality to expose to the sandbox. If a function needs to be part of the sandbox, then that function needs to have its environment set.
That's why it's best to sandbox based on compiled Lua chunks rather than individual functions. When creating functions, the functions created will inherit the current environment.
You haven't modified the environment that sandboxTest is using; you only modified the environment of the current function. You can use setfenv to set the environment of a particular function by passing a function name (passing a number modifies the environment of a function in the call stack):
setfenv(sandboxTest, {
print = print,
testValue = aNumber,
sandboxTest = sandboxTest
})
This will print 123 123.

Recreating setfenv() in Lua 5.2

How can I recreate the functionality of setfenv in Lua 5.2? I'm having some trouble understanding exactly how you are supposed to use the new _ENV environment variable.
In Lua 5.1 you can use setfenv to sandbox any function quite easily.
--# Lua 5.1
print('_G', _G) -- address of _G
local foo = function()
print('env', _G) -- address of sandbox _G
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
setfenv(foo, env)
foo()
-- we should have global in our environment table but not in _G
print(bar, env.bar)
Running this example shows an output:
_G table: 0x62d6b0
env table: 0x635d00
nil 1
I would like to recreate this simple example in Lua 5.2. Below is my attempt, but it does not work like the above example.
--# Lua 5.2
local function setfenv(f, env)
local _ENV = env or {} -- create the _ENV upvalue
return function(...)
print('upvalue', _ENV) -- address of _ENV upvalue
return f(...)
end
end
local foo = function()
print('_ENV', _ENV) -- address of function _ENV
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
foo_env = setfenv(foo, env)
foo_env()
-- we should have global in our envoirnment table but not in _G
print(bar, env.bar)
Running this example shows the output:
upvalue table: 0x637e90
_ENV table: 0x6305f0
1 nil
I am aware of several other questions on this subject, but they mostly seem to be dealing with loading dynamic code (files or string) which work quite well using the new load function provided in Lua 5.2. Here I am specifically asking for a solution to run arbitrary functions in a sandbox. I would like to do this without using the debug library. According to the Lua documentation we should not have to rely on it.
You cannot change the environment of a function without using the debug library from Lua in Lua 5.2. Once a function has been created, that is the environment it has. The only way to modify this environment is by modifying its first upvalue, which requires the debug library.
The general idea with environments in Lua 5.2 is that the environment should be considered immutable outside of trickery (ie: the debug library). You create a function in an environment; once created there, that's the environment it has. Forever.
This is how environments were often used in Lua 5.1, but it was easy and sanctioned to modify the environment of anything with a casual function call. And if your Lua interpreter removed setfenv (to prevent users from breaking the sandbox), then the user code can't set the environment for their own functions internally. So the outside world gets a sandbox, but the inside world can't have a sandbox within the sandbox.
The Lua 5.2 mechanism makes it harder to modify the environment post function-creation, but it does allow you to set the environment during creation. Which lets you sandbox inside the sandbox.
So what you really want is to just rearrange your code like this:
local foo;
do
local _ENV = { print = print }
function foo()
print('env', _ENV)
bar = 1
end
end
foo is now sandboxed. And now, it's much harder for someone to break the sandbox.
As you can imagine, this has caused some contention among Lua developers.
It's a bit expensive, but if it's that important to you...
Why not use string.dump, and re-load the function into the right environment?
function setfenv(f, env)
return load(string.dump(f), nil, nil, env)
end
function foo()
herp(derp)
end
setfenv(foo, {herp = print, derp = "Hello, world!"})()
To recreate setfenv/getfenv in Lua 5.2 you can do the following:
if not setfenv then -- Lua 5.2
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-- this assumes f is a function
local function findenv(f)
local level = 1
repeat
local name, value = debug.getupvalue(f, level)
if name == '_ENV' then return level, value end
level = level + 1
until name == nil
return nil end
getfenv = function (f) return(select(2, findenv(f)) or _G) end
setfenv = function (f, t)
local level = findenv(f)
if level then debug.setupvalue(f, level, t) end
return f end
end
RPFeltz's answer (load(string.dump(f)...)) is a clever one and may work for you, but it doesn't deal with functions that have upvalues (other than _ENV).
There is also compat-env module that implements Lua 5.1 functions in Lua 5.2 and vice versa.
In Lua5.2 a sandboxeable function needs to specify that itself. One simple pattern you can use is have it receive _ENV as an argument
function(_ENV)
...
end
Or wrap it inside something that defines the env
local mk_func(_ENV)
return function()
...
end
end
local f = mk_func({print = print})
However, this explicit use of _ENV is less useful for sandboxing, since you can't always assume the other function will cooperate by having an _ENV variable. In that case, it depends on what you do. If you just want to load code from some other file then functions such as load and loadfile usually receive an optional environment parameter that you can use for sandboxing. Additionally, if the code you are trying to load is in string format you can use string manipulation to add _ENV variables yourself (say, by wrapping a function with an env parameter around it)
local code = 'return function(_ENV) return ' .. their_code .. 'end'
Finally, if you really need dynamic function environment manipulation, you can use the debug library to change the function's internal upvalue for _ENV. While using the debug library is not usually encouraged, I think it is acceptable if all the other alternatives didn't apply (I feel that in this case changing the function's environment is deep voodoo magic already so using the debug library is not much worse)

lua how require works

I'm using a graphics library that lets you program in Lua. I have a need for the A* pathfinding library so I found one online. It's just 1 lua file that does the pathfinding and 1 example file. In the example file it uses the object like:
-- Loading the library
local Astar = require 'Astar'
Astar(map,1) -- Inits the library, sets the OBST_VALUE to 1
I run the script and everything works. So now I add the Astar.lua file to the path location where my graphics engine is running and do the same thing and I get the error on the Astar(map, 1) line:
"attempt to call local 'AStar' (a number value)
Any ideas why I would be getting that error when I'm doing the same thing as the example that comes with this AStar lib?
Here is a little of the AStar file
-- The Astar class
local Astar = {}
setmetatable(Astar, {__call = function(self,...) return self:init(...) end})
Astar.__index = Astar
-- Loads the map, sets the unwalkable value, inits pathfinding
function Astar:init(map,obstvalue)
self.map = map
self.OBST_VALUE = obstvalue or 1
self.cList = {}
self.oList = {}
self.initialNode = false
self.finalNode = false
self.currentNode = false
self.path = {}
self.mapSizeX = #self.map[1]
self.mapSizeY = #self.map
end
So note that when I run this from my graphics engine it's returning 1, but when run from the example that it came with it's returning a table, which is what it should be returning. So not sure why it would only be returning 1.
How is Astar getting added to the package.loaded table for the example script, as opposed to your code?
QUICK LUA SYNTACTIC SUGAR REVIEW:
func 'string' is equivalent to func('string')
tabl.ident is equivalent to tabl['ident']
When you run a script using require('Astar'), this is what it does:
checks if package.loaded['Astar'] is a non-nil value.
If it is, it returns this value. Otherwise it continues down this list.
Runs through filenames of the patterns listed in package.path (and package.cpath), with '?' replaced with 'Astar', until it finds the first file matching the pattern.
Sets package.loaded['Astar'] to true.
Runs the module script (found via path search above- for the sake of this example we'll assume it's not a C module) with 'Astar' as an argument (accessible as ... in the module script).
If the script returns a value, this value is placed into package.loaded['Astar'].
The contents of package.loaded['Astar'] are returned.
Note that the script can load the package into package.loaded['Astar'] as part of its execution and return nothing.
As somebody noted in the comments above, your problem may come from loading the module using 'AStar' instead of 'Astar'. It's possible that Lua is loading this script using this string (since, on the case-insensitive Windows, a search for a file named "AStar.lua" will open a file called "Astar.lua"), but the script isn't operating with that (by using a hard-coded "Astar" instead of the "AStar" Lua is loading the script under).
You need to add return Astar at the end of Astar.lua.

Resources