Background
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”.
Vulnerability
Due to a flaw in the library disabling code some variables are still accessible. Notably package.loaders
and package.cpath
. 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
modify 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).
Finally, call
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.
Remediation
This issue is fixed in version 0.15.31 released 2017-07-25. Immediate update is strongly recommended.
Timeline
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.
Acknowledgments
I would like to thank Brandon Wagner for his support with discovery, exploitation and writeup of this vulnerability.