From b2c03814be16e6f13e26ae064d75ae5b5024ef71 Mon Sep 17 00:00:00 2001 From: "Scott R. Shinn" Date: Tue, 20 Oct 2026 16:36:54 -0400 Subject: [PATCH] fix analysisd: release owned hostname before ossecalert location alias Since #1668, OS_CleanMSG_ex() can hand OSSECAlert_Decoder_Exec() an agent event whose hostname is a heap copy of the agent name with EF_FREE_HNAME set. The decoder then set lf->hostname = lf->location without clearing that ownership flag, so Free_Eventinfo() freed the same buffer twice and aborted analysisd. Free the owned hostname and clear EF_FREE_HNAME immediately before the alias. The existing #1817 path then duplicates hostname on a full decode; early returns keep the unowned alias as before #1668. Add issue_1668_ossecalert_hostname regression. Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-5ZOE8CQ9. Signed-off-by: Scott R. Shinn --- .../decoders/plugins/ossecalert_decoder.c | 8 ++ src/tests/regressions/Makefile | 18 +++ src/tests/regressions/README.md | 2 + .../issue_1668_ossecalert_hostname.c | 124 ++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 src/tests/regressions/issue_1668_ossecalert_hostname.c diff --git a/src/analysisd/decoders/plugins/ossecalert_decoder.c b/src/analysisd/decoders/plugins/ossecalert_decoder.c index f31cf8ec9..146b2f0a9 100644 --- a/src/analysisd/decoders/plugins/ossecalert_decoder.c +++ b/src/analysisd/decoders/plugins/ossecalert_decoder.c @@ -125,6 +125,14 @@ void *OSSECAlert_Decoder_Exec(Eventinfo *lf) snprintf(oa_newlocation, 255, "%s|%s", lf->location, oa_location); free(lf->location); os_strdup(oa_newlocation, lf->location); + if (lf->flags & EF_FREE_HNAME) { + /* OS_CleanMSG gave us an owned hostname (agent name, #1668): + * release it before aliasing, or Free_Eventinfo frees + * lf->location twice. + */ + free(lf->hostname); + lf->flags &= ~EF_FREE_HNAME; + } lf->hostname = lf->location; diff --git a/src/tests/regressions/Makefile b/src/tests/regressions/Makefile index bafbfdf57..60f83f559 100644 --- a/src/tests/regressions/Makefile +++ b/src/tests/regressions/Makefile @@ -26,6 +26,7 @@ REGRESSION_BINS = \ issue_2065_sender_counter \ issue_1611_local_ip_bind \ issue_1668_agent_hostname \ + issue_1668_ossecalert_hostname \ unmerge_path_traversal \ jsonout_default \ issue_1814_control_chars \ @@ -94,6 +95,20 @@ issue_1668_agent_hostname: tests/regressions/issue_1668_agent_hostname.c \ -Wl,--start-group shared.a os_regex.a os_net.a os_xml.a os_zlib.a -Wl,--end-group \ -lpcre2-8 -lm -lpthread -lz +issue_1668_ossecalert_hostname: tests/regressions/issue_1668_ossecalert_hostname.c \ + analysisd/cleanevent.c analysisd/eventinfo.c analysisd/eventinfo_list.c \ + analysisd/decoders/plugins/ossecalert_decoder.c shared.a + $(CC) $(CFLAGS) -I./analysisd -c analysisd/cleanevent.c -o analysisd/cleanevent-1668oa.o + $(CC) $(CFLAGS) -I./analysisd -c analysisd/eventinfo.c -o analysisd/eventinfo-1668oa.o + $(CC) $(CFLAGS) -I./analysisd -c analysisd/eventinfo_list.c -o analysisd/eventinfo_list-1668oa.o + $(CC) $(CFLAGS) -I./analysisd -c analysisd/decoders/plugins/ossecalert_decoder.c \ + -o analysisd/decoders/plugins/ossecalert_decoder-1668oa.o + $(CC) $(CFLAGS) -I./analysisd -o $@ tests/regressions/issue_1668_ossecalert_hostname.c \ + analysisd/cleanevent-1668oa.o analysisd/eventinfo-1668oa.o analysisd/eventinfo_list-1668oa.o \ + analysisd/decoders/plugins/ossecalert_decoder-1668oa.o \ + -Wl,--start-group shared.a os_regex.a os_net.a os_xml.a os_zlib.a -Wl,--end-group \ + -lpcre2-8 -lm -lpthread -lz + unmerge_path_traversal: tests/regressions/unmerge_path_traversal.c shared.a $(CC) $(CFLAGS) -o $@ tests/regressions/unmerge_path_traversal.c \ -Wl,--start-group shared.a os_regex.a -Wl,--end-group \ @@ -182,6 +197,9 @@ clean: rm -f $(REGRESSION_BINS) \ analysisd/cleanevent-1668.o analysisd/eventinfo-1668.o \ analysisd/eventinfo_list-1668.o \ + analysisd/cleanevent-1668oa.o analysisd/eventinfo-1668oa.o \ + analysisd/eventinfo_list-1668oa.o \ + analysisd/decoders/plugins/ossecalert_decoder-1668oa.o \ analysisd/cleanevent-1814.o os_auth/ssl-2100.o \ logcollector/read_multiline-underflow.o \ os_crypto/shared/msgs-checksum.o \ diff --git a/src/tests/regressions/README.md b/src/tests/regressions/README.md index 35bd3c828..2f94ba06b 100644 --- a/src/tests/regressions/README.md +++ b/src/tests/regressions/README.md @@ -27,6 +27,8 @@ # - issue_1274_auth_ipv6_key auth IPv6 key handling # - issue_2065_sender_counter sender_counter isolated from agent 0 rids # - issue_1611_local_ip_bind remoted local_ip / ipv6 bind family +# - issue_1668_agent_hostname agent name vs device hostname split +# - issue_1668_ossecalert_hostname OSSECAlert decoder vs owned hostname (double free) # - jsonout_default jsonout_output on when the XML tag is omitted # - issue_1814_control_chars strip controls, keep UTF-8 # - issue_1953_xml_recursion OS_ReadXML recursion limit diff --git a/src/tests/regressions/issue_1668_ossecalert_hostname.c b/src/tests/regressions/issue_1668_ossecalert_hostname.c new file mode 100644 index 000000000..033c9a672 --- /dev/null +++ b/src/tests/regressions/issue_1668_ossecalert_hostname.c @@ -0,0 +1,124 @@ +/* + * Regression for the OSSECAlert decoder after #1668: an agent event with no + * syslog hostname reaches the decoder with an owned lf->hostname + * (EF_FREE_HNAME set). The decoder aliases lf->hostname to lf->location, + * so Free_Eventinfo() used to free the same buffer twice. + * + * ANT-2026-5ZOE8CQ9 / CWE-415 + * + * Build (from src/ after make TARGET=server): + * make -f tests/regressions/Makefile issue_1668_ossecalert_hostname + */ + +#include +#include +#include +#include + +#include "shared.h" +#include "analysisd/eventinfo.h" +#include "analysisd/cleanevent.h" +#include "analysisd/config.h" +#include "analysisd/analysisd.h" + +_Config Config; +__thread time_t c_time; +__thread int __crt_hour; +__thread int __crt_wday; +char __shost[512]; +OSDecoderInfo *NULL_Decoder = NULL; +int alert_only = 1; + +void *OSSECAlert_Decoder_Exec(Eventinfo *lf); + +static int run_case(const char *name, const char *msg, int want_rule) +{ + Eventinfo *lf; + OSDecoderInfo decoder_info; + char *buf; + int rc; + int failed = 0; + + printf("--- %s ---\n", name); + os_calloc(1, sizeof(*lf), lf); + Zero_Eventinfo(lf); + memset(&decoder_info, 0, sizeof(decoder_info)); + lf->decoder_info = &decoder_info; + + os_strdup(msg, buf); + rc = OS_CleanMSG_ex(buf, lf, time(NULL), 0); + free(buf); + + if (rc != 0) { + printf("FAIL: OS_CleanMSG_ex returned %d\n", rc); + Free_Eventinfo(lf); + return 1; + } + + OSSECAlert_Decoder_Exec(lf); + + /* An owned hostname must never share storage with location. */ + if ((lf->flags & EF_FREE_HNAME) && lf->hostname == lf->location) { + printf("FAIL: hostname aliases location while EF_FREE_HNAME is set\n"); + failed = 1; + } + if (want_rule && !lf->generated_rule) { + printf("FAIL: decoder did not run to completion\n"); + failed = 1; + } + if (!failed) { + printf("OK: hostname='%s' location='%s' flags=0x%x\n", + lf->hostname ? lf->hostname : "", + lf->location ? lf->location : "", lf->flags); + } + + /* glibc aborts here on the double free. */ + Free_Eventinfo(lf); + return failed; +} + +int main(void) +{ + int failed = 0; + static int rule_1002; + + memset(&Config, 0, sizeof(Config)); + Config.decoder_order_size = 8; + Config.g_rules_hash = OSHash_Create(); + OSHash_Add(Config.g_rules_hash, "1002", &rule_1002); + strncpy(__shost, "managerhost", sizeof(__shost) - 1); + c_time = time(NULL); + + printf("=== Issue 1668: OSSECAlert decoder hostname ownership ===\n"); + + /* No syslog header: hostname is an owned copy of the agent name. + * Trailing field without ';' takes the decoder's early return. */ + failed |= run_case( + "agent, no header, early return", + "1:(agent1) any->/var/log/app.log:" + "ossec: Alert Level: 3; Rule: 1002 - x; Location: y; z", + 0); + + /* Same, but the decoder runs to the end (two spaces after ';'). */ + failed |= run_case( + "agent, no header, full decode", + "1:(agent1) any->/var/log/app.log:" + "ossec: Alert Level: 3; Rule: 1002 - x; Location: y; text", + 1); + + /* Syslog header: hostname points into the log buffer on entry. */ + failed |= run_case( + "agent, syslog header", + "1:(agent1) any->/var/log/app.log:" + "Jan 15 12:00:00 web1 ossec: Alert Level: 3; Rule: 1002 - x; " + "Location: y; text", + 1); + + if (failed) { + printf("\nFAIL: issue_1668_ossecalert_hostname\n"); + return 1; + } + + printf("\nPASS: issue_1668_ossecalert_hostname\n"); + return 0; +}