June 29th, 2024

Bytecode Breakdown: Unraveling Factorio's Lua Security Flaws

A security researcher found memory corruption flaws in Factorio's Lua, enabling servers to run code on clients. Hardening Lua by disabling risky modules can prevent unauthorized code execution, crucial for securing dynamic languages.

Read original articleLink Icon
Bytecode Breakdown: Unraveling Factorio's Lua Security Flaws

A security researcher uncovered memory corruption vulnerabilities in Factorio's Lua implementation, allowing malicious servers to execute code on clients. Lua is used in Factorio for game logic and mods, with a large modding community. The multiplayer mode's deterministic lockstep feature requires all players to execute Lua code to maintain synchronization, potentially exposing the game to network-based attacks. Exploiting Lua's powerful functions like loadstring can lead to memory manipulation and code execution. Lua bytecode, not machine code, is executed by the interpreter, making it susceptible to manipulation. To exploit Factorio, a malicious map with crafted Lua code can be hosted on a server to target connecting clients. Hardening Lua by disabling dangerous modules like os and loadstring is recommended to prevent unauthorized code execution. Understanding Lua bytecode and its execution process is crucial for comprehending and potentially exploiting vulnerabilities in dynamic languages like Lua.

Related

Factorio Fluids 2.0

Factorio Fluids 2.0

The Factorio Friday Facts detail the game's fluid system overhaul. The new algorithm simplifies fluid flow with segments, improving efficiency, throughput, and distribution while addressing previous issues. Sacrificing realism for gameplay, the update aims to enhance player experience.

A portable lightweight C FFI for Lua, based on libffi

A portable lightweight C FFI for Lua, based on libffi

A GitHub repository offers a portable lightweight C FFI for Lua, based on libffi. It aims for LuaJIT FFI compatibility, developed in C. Includes features, examples, basic types, build instructions, testing, and acknowledgements.

Vulnerability in Popular PC and Server Firmware

Vulnerability in Popular PC and Server Firmware

Eclypsium found a critical vulnerability (CVE-2024-0762) in Intel Core processors' Phoenix SecureCore UEFI firmware, potentially enabling privilege escalation and persistent attacks. Lenovo issued BIOS updates, emphasizing the significance of supply chain security.

The Dirty Pipe Vulnerability

The Dirty Pipe Vulnerability

The Dirty Pipe Vulnerability (CVE-2022-0847) in Linux kernel versions since 5.8 allowed unauthorized data overwriting in read-only files, fixed in versions 5.16.11, 5.15.25, and 5.10.102. Discovered through CRC errors in log files, it revealed systematic corruption linked to ZIP file headers due to a kernel bug in Linux 5.10. The bug's origin was pinpointed by replicating data transfer issues between processes using C programs, exposing the faulty commit. Changes in the pipe buffer code impacted data transfer efficiency, emphasizing the intricate nature of kernel development and software component interactions.

OpenSSL CVE-2024-5535: `SSL_select_next_proto` buffer overread

OpenSSL CVE-2024-5535: `SSL_select_next_proto` buffer overread

A bug, CVE-2024-5535, in OpenSSL since 2011 allows heap data leakage. Impacts Python <= 3.9, Node.js <= 9. NPN support removal in newer versions reduces risk. Bug affects SSL_select_next_proto in OpenSSL, BoringSSL, LibreSSL. Memory safety risks demand caution and updates.

Link Icon 19 comments
By @JonChesterfield - 7 months
Unexpected!

Since lua interprets bytecode, it can check the arguments to the bytecode are meaningful. Point to memory lua allocated, things like that.

Turns out it doesn't do that. Feed it bytecode with invalid arguments passed to the instructions and it executes it anyway. The rest of the compromise follows.

Further, instead of fixing their interpreter, the game plan is to statically analyse bytecode. Which turns out to only work in simple cases.

For a sandbox friendly interpreted language this is pretty disappointing. I wonder if they'd take patches to stop the interpreter trusting the input - presumably performance regression is the fear there, which seems dubious when luajit is the fast option anyway.

By @CapsAdmin - 7 months
I wish this was more defined or documented somehow. You're kind of left on your own to figure out whether a language is reasonably guaranteed to be safe or not.

Some example scenarios:

- Code is static and is executed directly by a user, the default case languages care typically care about. Including Lua.

- Code is dynamically fetched and executed through some update process, hopefully only through official channels. Here you can get away by making the process secure, but who knows.

- Code can be added by the user through plugins, this can be made easier through stores with the click of a button. You can review plugins, but this is hardly done. Here you need to consider if the code should be sandboxed or the user should be careful.

- A multiplayer game where a server can be extended with custom code via plugins, but not the clients. Here you need to consider that the users/gamers who are hosting servers are eager to try many different plugins. The plugin community (gamers) can also be a lot more dangerous.

- A multiplayer game where the server can execute arbitrary code on clients, just like a browser. Here you need to be very careful about sandboxing, especially on clients as gamers will just join random servers without thinking about the security implications.

The last point being Factorio's case. I'm not necessarily disagreeing that it's the developers job to evaluate this, but sometimes it's not obvious that for example the load function in Lua can run arbitrary bytecode which is unsafe.

To be honest, I wasn't aware that Lua's bytecode is unsafe, but I am aware that LuaJIT's bytecode is unsafe. But as far as I know this fact is just stated randomly in the mailing list and github issues as an obvious fact.

There is another thing about servers being able to crash clients (just run some infinite loop on them), but this much harder and maybe pointless to avoid.

By @hypeatei - 7 months
Factorio has a really good dev team behind it so I trust that they're doing their best to fix these issues. Though, gamedev in general seems to be more of a creative endeavor which puts things like code practices and security on the back burner. I wonder how many zero day exploits are lurking in game clients / servers.
By @est31 - 7 months
In general, verifying programs is extremely hard, not just because of rice's theorem but because it's so easy to miss a spot, especially for non-trivial bytecode languages like lua's. wasm has no concepts of for loops for example.

It's strange that after upstream has given up on the problem as it was too hard, factorio devs have chosen to try to fix the verifier/write their own (not sure which of the two they did).

Minetest's loadstring function forbids bytecode entirely: https://github.com/minetest/minetest/blob/9a1501ae89ffe79c38...

I wonder why factorio mods need the ability to execute raw lua bytecode. If they don't have it, there would be no need for a verifier.

It's quite dangerous in the first place to execute lua code downloaded over the network. JS execution environments have gone through decades of cycles of discoveries of exploits and fixes. Lua gets those as well but on a smaller scale, and with less staffing to improve security. The main protection is I guess that there is fewer people running malicious game servers.

By @bluelightning2k - 7 months
Total newb question but why do games use lua, as opposed to (for instance) embedded JavaScript with some defined interface into the game (e.g. APIs to adjust game state).

Seems to me that this would benefit from the much more severe hardening work that's gone into isolating browser environments (a hard and VERY well tested and funded target), as well as the massive work that's gone into performance optimizing dynamic types.

Plus if the mod needs UI there's the canvas and potentially even React etc if a Dom-like model is available.

By @bbor - 7 months
As a non-security dev, I'll drop the obligatory "wow this is incredibly impressive!" I can't believe how clear and logically you'd have to think to track down these intricate failure cases. Definitely not my strong suit! I'm much more of an "ideas guy" ;)

Content-wise: Wow... We are totally, completely, utterly screwed once people start putting together ensembles of AI SWEs equipped with 10,000 blog posts like this one on finding weird memory exploits. Ultimately I think we're gonna need a whole new paradigm for security, or at least some new element in the stack. It's my potentially naive opinion that all the modern talk about "trusted" clients and DB roles and all that is trying to patch holes in swiss cheese; hopefully, we can find a new stack of LLM-maintained swiss cheese to add on instead!

By @bhk - 7 months
So... this demonstrates an exploit that relies on a feature that is advertised as exploitable: loading byte code. What am I missing?
By @cedws - 7 months
We are seriously lucky such capable people are on the good side.
By @josephcsible - 7 months
IMO, Lua bytecode should never be usable anywhere outside of embedded systems that don't have enough resources to run the Lua source code parser. Besides security vulnerabilities, the only other thing it seems to be useful for is closed-source programs.
By @BeefySwain - 7 months
Unless I missed it (I admit I skimmed towards the end) The author does not discuss at all the actual remediation that was taken. I would love to hear more about that.
By @davikr - 7 months
Never enable in your Lua apps:

a. Bytecode

b. Debug, Io, Os libraries

...

Also, be very careful with sandboxing. It can be deceptively easy to break out of.

Everyone eventually learns this lesson, see: Roblox (removed bytecode almost a decade ago after, if I recall correctly, an exploit exfiltrated their server tokens), Company of Heroes (bytecode bug leading to RCE)

Luau should come with safe defaults, from what I've been told.

By @quenix - 7 months
I’m confused about one thing.

It doesn’t follow to me, that since all clients are running their own simulation, Lua scripts must run on every client too.

If a client runs a Lua script, why can’t we just run it on their machine and propagate any game state changes (if the script adds an inserted, for example,) as if the player made those changes themselves?

By @ec109685 - 7 months
Is it impractical to employ firecracker vm like separation to isolate untrusted code, severely reducing the impact of any bugs?

Browsers split their various components across multiple processes to provide isolation. VMs would provide even more isolation.

By @therobots927 - 7 months
I literally just downloaded a factorial demo to my work laptop. Is this something I need to be concerned about if I don’t play online?
By @nmz - 7 months
bytecode is also architecture dependent
By @gjsman-1000 - 7 months
At this point, I have serious doubts whether bytecode and JIT systems, whether it be Lua in Factorio or JavaScript in Chrome, can ever be verifiably secure. I think we would all be better off if, like Apple’s Lockdown mode, we can disable anything JIT on a high stakes system.

I don’t blame Factorio though - this (anonymous?) researcher is 100x developer material.