Factorio is a very popular multiplayer factory management and automation game. It supports modification though the use of Lua scripts. For security and determinism (in a multiplayer game all clients process the game state separately, any client difference would result in desyncronization and crashing) access to certain Lua core libraries is disabled. This includes OS, debug and package. Factorio supports a Lua REPL that can be used by administrative users in multiplayer games and will also autorun Lua provided by the server on joining in a less widely used system called “scenarios”.
Due to a flaw in the library disabling code some variables are still accessible. Notably
package.cpath determines where Lua will look when attempting to load a C module and
package.loaders is an array of loading methods Lua uses when instructed to load a library. This makes a sandbox escape to arbitrary C execution trivial. A malicious mod author would simply have to include a C library that exports
int luaopen_XXX(lua_State * L) where XXX is the libraries filename
package.cpath to point to the library (By default
package.cpath points to the game executable folder, in the standalone versions of Factorio the mods folder is 2 directories higher).
in Lua which will execute
luaopen_XXX in the C library. Exploitation is also possible without requiring users to locally install malicious libraries. The Factorio Lua environment provides
LuaGameScript::write_file(filename, data, [append], [for_player]) where data is a string.
Files are written relative to a “script-output” directory which is next to the mods folder so
package.cpath can be modified just as before. This allows remote attackers operating either as users in a multiplayer game with elevated privileges or running their own server with a malicious scenario to write a library to a file, alter
package.cpath, load and then run that library.
This issue is fixed in version 0.15.31 released 2017-07-25. Immediate update is strongly recommended.
2016-12-01: I discovered some members of package were exposed. At the time I believed this to not to have any security impact as I didn’t notice
package.cpath was writable.
2017-07-21: I revisited the issue and found an exploit vector, issue reported.
9* hours later: (EDIT: This used to say 14 hours, I made a mistake with timezone math.) I’m informed the issue has been patched and the patch will be included in the next release.
2017-07-25: Patch released, I confirmed the vulnerability has been fixed.
2017-07-26: Blog post published.
I would like to thank Brandon Wagner for his support with discovery, exploitation and writeup of this vulnerability.