Announcement

Collapse
No announcement yet.

Lua crash (and potential security breach)

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Lua crash (and potential security breach)

    Hello,

    When using ImportPackages in lua, there is a glitch that may lead to random code execution on the server (and through this it may compromise the server safety and/or leak data).

    The cause of the bug is that when you use AddFunctionExport, you add a single function to export, not the whole context. However, that context may be used in the return value of the exported function and jumped on in the ImportPackages' size. In this case, you will access to a random point of memory, usualy one that have been freeed by lua's garbage collector.

    Here is a demonstration of the problem :

    objects.lua file, in the toolslib package
    Code:
    function NewObject()
    local result = {
            new=function ()
    return 42
    end
    }
    return result
    end
    AddFunctionExport("Object", NewObject)
    The important thing here is that the returned object include a lambda, but this lambda will never be exported itself since the object is not the function. Now if you do :

    server.lua file, in the mytest package
    Code:
    tools = ImportPackage("toolslib")
    
    MyObject = tools.Object()
    The instant this code is run, your server will segf. It also works with function returning functions, like this :

    Code:
    function myfunc()
         function test(a) print(a) end
         return test
    end
    AddFunctionExport("test", myfunc)
    Code:
    test = ImportPackage("toolslib")
    test()(2)
    This will raise a lua error, stating that we are trying to call nil - because test is no longer defined. Of course, both examples works fine when used in the same package, since references are correct.

    This problem may lead to important concerns, especialy if we want to start using an external library - since any compromission in the external library may lead not only to our game server crash, but also the whole physical / virtual server vulnerability :/

    I'm not exactly certain on how this could be solved. A way could be to remove ImportPackage support altogether and allow using require on client : that would allow to access potentially "private" functions (although there are some way in lua to prevent that). I guess there is some kind of pseudo-isolation between lua packages that would be broken by this method, but it would give more security and more flexibility to create scripts. That would also prevent some loadstring based hack that could be used now to get the files and that could add some more vulnerabilities both on the server and the client side.

    Another way could be to map importpackage function to get their context and import the exported function's context into the importing module : that would probably remove the problem but that could create some name collision that may create hard-to-find bugs.

    I hope you'll find a way around this ! Please feel free to ask if I omitted any element.

    Thank you !

    #2
    I did a few more tests on this and here are a few more bugs related to this ImportPackage function...

    Let's try to use self (lua pseudo-keyword) in our function ! Here is my file in toolslib

    Code:
    function wrapper(self, val)
          print(self)
          print(self.toto)
          print(val)
          print(val.a)
    end
    AddFunctionExport("wrap", wrapper)
    
    function toto()
          print(42)
    end
    AddFunctionExport("toto", toto)
    We are going to have some fun with our wrapper function. Let's start with a simple test ; in another module, I do this :

    Code:
    wrap = ImportPackage("toolslib")
    function toto()
          print(1)
    end
    wrap.wrap(wrap, {a=3})
    (toto isn't used right now, but it will be later)

    The output expected for this would be :
    - Table: XXXX
    - Function: XXXX
    - Table: XXXX
    - 3

    Since self is the table imported by ImportPackage. However, here is the result :

    Code:
    [2019-12-16 17:36:01] [info] Starting package "toolslib"
    [2019-12-16 17:36:01] [info] Starting package "windowlibtest"
    [2019-12-16 17:36:01] [script] table: 0x564a02904210
    [2019-12-16 17:36:01] [script] nil
    [2019-12-16 17:36:01] [script] table: 0x564a02904250
    [2019-12-16 17:36:01] [script] 3
    The nil isn't supposed to be here ! It means that I'm not even able to map-back my functions in my module to be able to create method tables (lua's version of oop).

    Now, let's add some magic to this. Without changing at all the package file, let's use our toto function :

    Code:
    wrap = ImportPackage("toolslib")
    function toto()
          print(1)
    end
    wrap.wrap(wrap, {a=3, b=toto})
    Nothing is called here, we are just giving a pointer in our table - and this pointer won't be called in the wrap function since it didn't change. However, here is the result when I run this code :

    Code:
    [2019-12-16 17:43:10] [info] Starting package "toolslib"
    [2019-12-16 17:43:10] [info] Starting package "windowlibtest"
    [2019-12-16 17:43:10] [error] attempt to call a nil value
    [2019-12-16 17:43:10] [info] Package load done, clients will download 24 files with a total of 345 KB
    I don't really know why or how, but it seems that somewhere in the ImportPackage code used to send contextes from one lua to the other, the function is called (in lua I guess ?). I tried a few other way to pass a function as an argument to an imported function, but none of them works - it's always nil, and sometimes it seems like it's called by itself somewhere. Sometimes it segf, too, but I mentionned that in my previous post ^^

    Comment


      #3
      Functions can not be passed to exported functions. In 1.0.3 there are additional checks to clear them from tables.
      Thanks for reporting.

      Comment

      Working...
      X