← Back to KnowledgeThe Real Security Model of Garry's Mod: Sandbox Strength and the net.Receive Checklist

Server owners are often surprised to hear this, but it needs saying plainly: no documented genuine sandbox escape exists for GLua - no case of pure Lua code defeating GMod’s engine-level restrictions to reach the underlying operating system. The os library is stripped down to date/time functions only (os.clock, os.date, os.difftime, os.time) with no os.execute and no process or environment access. io isn’t exposed at all - GMod provides its own sandboxed file library instead, with writes constrained to a data/ directory, blocked dangerous extensions (.db, .mdmp, .dmp), and protected filenames like server.cfg.

Every serious real-world incident we could find was either a developer voluntarily feeding untrusted or remote data into an intentionally powerful function, or a C++/engine-level bug unrelated to the Lua sandbox itself. The correct way to frame this for a security review: the sandbox holds up. The attack surface is addon code choosing to call dangerous-but-legitimate functions on attacker-controlled input.

The one pattern behind almost every real backdoor

The single most common real backdoor pattern in this ecosystem: a net.Receive handler with no permission check feeds attacker-controlled or remote string data into RunString, RunStringEx, or CompileString - often preceded by an unchecked http.Fetch. This isn’t hypothetical: a 2014 Workshop addon marketed as “Screen Grabber (Anti-Cheat!)” did exactly this, and was auto-pushed to every subscriber through a routine Workshop update. Neither CompileString nor RunString carries any built-in safeguard against this - the function’s entire purpose is to compile and execute arbitrary code, so nothing in the engine stops attacker-controlled input from reaching it.

The rule this leads to is close to absolute: never feed a net-received or http-fetched, attacker-influenced string into RunString/RunStringEx/CompileString. A first-pass audit for this is mechanical and something any competent reviewer should do immediately: grep the addon’s source for RunString, CompileString, http.Fetch/http.Post combined with _G access, and unexplained SteamID allow-lists. Community tools built around exactly this technique exist (Xalalau/backdoor-shield, Wownicehat/Gmod-NBP-No-Backdoor-Please), which is itself a signal of how common the pattern is.

SQL injection: the driver mismatch is the trap

sql.Query targets local SQLite only - the wiki’s own example for it shows string concatenation of a player’s SteamID64 and money value as the anti-pattern to avoid, and this exact vulnerable pattern shows up regularly in low-quality DarkRP forks. sql.SQLStr(str) escapes for SQLite specifically, and the wiki explicitly warns against using it for MySQL - SQLite and MySQL escape sequences are not compatible, so a codebase that escapes with sql.SQLStr and then runs the result against a MySQLOO/tmysql connection has a real injection vulnerability sitting behind what looks like a defensive line of code.

The safer default for local SQLite work is sql.QueryTyped(query, ...), which binds ? placeholders as real typed parameters and is restricted to a single statement. For MySQL, MySQLOO supports genuine prepared/parameterized statements (PreparedQuery:setNumber/setString/setBoolean) - the correct standard for DarkRP-style addons storing player data in MySQL. Regardless of driver, player-controlled values - job names, SteamIDs, chat-derived strings - should never be string-concatenated directly into a query.

Container diagram: attacker-controlled server serves a payload via http.Fetch to an unchecked net.Receive handler, which executes it via RunString()

net.Receive checklist

Every net.Receive handler is, by construction, code that runs in response to client-controlled input. A short, non-negotiable checklist:

Priority audit zones for any third-party addon: database writes, all client→server net traffic, and any server-side processing of user-supplied strings (chat messages, name changes).

What we can check without your cooperation - and what we can’t

There are two clean tiers here, and being upfront about the boundary matters. Without any special access, a single connection to a server as a normal player - reading net_graph, watching console output for Lua error spam or “File exists in two addons” conflict warnings, and a single, one-off A2S_INFO query (the same protocol every server-browser tool uses) - is a legitimate, no-permission basis for spotting obvious problems. It is not a substitute for a real audit.

A genuine security or performance audit requires the server owner’s cooperation: RCON access for the real player/IP roster, live cvar inspection, server console, and log files; or direct source access to the addon code itself. There is no passive path to that level of detail, and automated or repeated querying of a server without permission - as opposed to a single manual check - crosses into territory server communities have organized against, given A2S_INFO’s history as an amplification vector. We scope every engagement around this boundary explicitly rather than blur it.

Related reading: Engine Limits Every Server Owner Should Know Before They Hit Them covers the entity and networking ceilings relevant when reviewing addon code that spawns entities or writes networked state at scale.