Lua沙盒具有泄漏的特殊功能

发布于 2024-12-11 10:30:13 字数 335 浏览 1 评论 0原文

我正在尝试使用 如何创建安全的 Lua 沙箱? 构建我自己的泄漏沙箱。

我正在尝试创建一个Lua沙箱,其中一些Lua函数可以访问沙箱之外的其他一些Lua函数。例如,我希望我的沙箱有一个特殊的“显示”功能,它可以调用“打印”,但沙箱中也没有“打印”。

主要问题是我试图在已经很大的代码库中构建一个沙箱,所以我不能删除函数。

这怎么可能?

解决方案必须是纯 Lua 函数,这不是我的错。

I am trying to use How can I create a secure Lua sandbox? to build my own leaky sandbox.

I am trying to create a Lua sandbox where some Lua functions can access some other Lua functions outside the sandbox. For example I want my sandbox to have a special "display" function which can call "print" but not have "print" in the sandbox too.

The main problem is that I am trying to build a sandbox within an already large codebase, so I cannot nil away functions.

How is this possible?

The solution has to be a pure Lua function due to no fault of mine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

娜些时光,永不杰束 2024-12-18 10:30:13

创建沙箱时,您可以通过从更大的环境中挑选函数和值来创建新的沙箱环境。您不需要破坏或“消除”原始环境中的任何内容。

  1. 通过挑选函数和值创建沙箱环境
  2. 加载脚本(这会编译它并将其作为要调用的函数返回)
  3. 将脚本的环境设置为沙箱环境
  4. 在沙箱中执行脚本

因此,

local script = loadstring "display(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)

打印

0.69314718055995

local script = loadstring "print(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)

失败

false   [string "print(math.log(2, 3))"]:1: attempt to call global 'print' (a nil value)

When you create a sandbox, you do it by cherry picking functions and values from a larger environment to create a new sandbox environment. You do not need to destroy or "nil out" anything in the original environment.

  1. Create your sandbox environment by cherry picking functions and values
  2. Load a script (this compiles it and returns it as a function to call)
  3. Set the environment of the script to the sandbox environment
  4. Execute the script in the sandbox

So,

local script = loadstring "display(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)

prints

0.69314718055995

whereas

local script = loadstring "print(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)

fails with

false   [string "print(math.log(2, 3))"]:1: attempt to call global 'print' (a nil value)
九歌凝 2024-12-18 10:30:13

是不是特别需要调用Lua标准库的print函数?您可以模拟 print 的功能吗?因为那将是最简单的方法。

但是,如果您想要对 print 进行包装,有两种方法可以实现:使用纯 Lua 代码和使用 C/C++ 代码。

纯Lua解决方案如下。请注意,这应该在加载任何外部脚本之前完成。首先,打开包含print的Lua标准库。然后运行这个 Lua 脚本:

local internal_print = print

return function(...)
    --Do display logic.
    internal_print(...) --Or whatever else you want.
end

这将返回“display”函数。如果您愿意,您可以将其存储在名为 display 的全局变量中,或者称为其他名称。

之后,您可以nil输出print全局变量,从而使其几乎完全无法访问。

如果您想从 C/C++ 执行此操作,则非常相似。首先,像以前一样,您注册包含 print 的 Lua 标准库,以便您可以获得它的函数。然后,使用 lua_getglobal(L, "print") 来获取 print 函数并将其压入堆栈。接下来,使用 lua_pushcclosure 注册 C/C++ 函数。但是你想指定一个上值,Lua 在注册时从堆栈中弹出该值。

现在,您注册的函数位于堆栈上,等待被推入 Lua 变量或全局表条目中。

警告:Lua 调试库可以查看上值,从而从新函数中获取 print 函数。因此,如果您想要完美的安全性,请摆脱 debug.getupvalue

Does it specifically need to call the Lua standard library print function? Can you instead emulate the functionality of print? Because that would be the easiest way.

However, if you want to have a wrapper around print, there are two ways to do it: with pure Lua code, and with C/C++ code.

The pure Lua solution is as follows. Note that this should be done before loading any external scripts. First, open the Lua standard library that has print in it. Then run this Lua script:

local internal_print = print

return function(...)
    --Do display logic.
    internal_print(...) --Or whatever else you want.
end

This will return the "display" function. You can store it in a global variable called display if you like, or called something else.

After that, you can nil out the print global variable, thus making it almost entirely inaccessible.

If you want to do it from C/C++, it's very similar. First, as before, you register the Lua standard library that includes print, so that you can get the function for it. Then, you use lua_getglobal(L, "print") to get the print function and push it onto the stack. Next, you register your C/C++ function using using lua_pushcclosure. But you want to specify one upvalue, which Lua pops off the stack at registration time.

And now your registered function is on the stack, waiting to be pushed into a Lua variable or global table entry.

Warning: the Lua debug library can poke at upvalues and thus get the print function from your new function. So if you want perfect security, get rid of debug.getupvalue.

夏末染殇 2024-12-18 10:30:13

构建您的沙箱(或多个沙箱,如果每个沙箱都有不同的要求)并将不受信任的代码一次移入沙箱中一部分。在我的快速 cli 测试中,5.1 和 5.2 都将运行在沙箱外部定义的函数,无需修改。要使用 Doug 的示例,假设 display 是使用 print 的现有代码的一部分:

-- 5.1
local function display(...)
  print(...)
end
local script = loadstring "display(math.log(2, 3))"
local env = {display = display, math = math, string = string}
setfenv(script, env)
print(pcall(script))

-- 5.2
local function display(...)
  print(...)
end
local script = loadstring "display(math.log(2, 3))"
local e=_ENV
_ENV={display = display, math = math, string = string}
e.print(e.pcall(script))
_ENV=e

请注意,在上述两个示例中,display > 函数正在使用 print 而不修改该代码,因为创建此函数时您不在沙箱中。

过去,我存储了一个指向非沙盒环境的本地指针,但我无法重现快速 cli 测试中需要的情况。如果您能举出一个示例,我可能会想出一种不需要 e 变量的解决方法。下面是使用 5.2 的代码示例:

local e=_ENV

for k,v in e.pairs(value) do
-- iterate
end

另一个示例,对于我的只读表代码,我再次使用 e

function ro_table (t)
  local t = t
  if t then
    return e.setmetatable({}, 
      { __index=t, 
        __newindex= function(_,_,_) e.error ("Attempt to modify read-only table") end, 
      })
  else
    return nil
  end
end

Build your sandbox (or multiple sandboxes if they each have different requirements) and move the untrusted code into the sandbox one piece at a time. In my quick cli tests, both 5.1 and 5.2 will run functions that were defined outside of the sandbox without modification. To use Doug's example, assume display is part of your pre-existing code that uses print:

-- 5.1
local function display(...)
  print(...)
end
local script = loadstring "display(math.log(2, 3))"
local env = {display = display, math = math, string = string}
setfenv(script, env)
print(pcall(script))

-- 5.2
local function display(...)
  print(...)
end
local script = loadstring "display(math.log(2, 3))"
local e=_ENV
_ENV={display = display, math = math, string = string}
e.print(e.pcall(script))
_ENV=e

Note that in both of the above examples, the display function is using print without modification to that code since you were not in the sandbox when this function was created.

In the past, I've stored a local pointer to the un-sandboxed environment, but I can't reproduce the situation where that's needed in my quick cli tests. If you can come up with an example, I can probably come up with a workaround that doesn't require the e variable. Here's an example of that code using 5.2:

local e=_ENV

for k,v in e.pairs(value) do
-- iterate
end

another example, for my read only table code, I'm again using the e:

function ro_table (t)
  local t = t
  if t then
    return e.setmetatable({}, 
      { __index=t, 
        __newindex= function(_,_,_) e.error ("Attempt to modify read-only table") end, 
      })
  else
    return nil
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文