From d93f50914b07bdb3f41e5008724338ca889ea434 Mon Sep 17 00:00:00 2001 From: yixiangzhike Date: Fri, 15 Aug 2025 13:59:47 +0800 Subject: [PATCH] fix CVE-2025-54389 CVE-2025-54409 --- aide.spec | 10 +- backport-CVE-2025-54389.patch | 340 ++++++++++++++++++++++++++++++++++ backport-CVE-2025-54409.patch | 59 ++++++ 3 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2025-54389.patch create mode 100644 backport-CVE-2025-54409.patch diff --git a/aide.spec b/aide.spec index 1206ba0..508fb3a 100644 --- a/aide.spec +++ b/aide.spec @@ -2,7 +2,7 @@ Name: aide Version: 0.17.4 -Release: 7 +Release: 8 Summary: Advanced Intrusion Detection Environment License: GPLv2+ URL: http://sourceforge.net/projects/aide @@ -28,6 +28,8 @@ Patch3: backport-Switch-from-PCRE-to-PCRE2-closes-116.patch Patch4: backport-Fix-condition-for-error-message-of-failing-to-open-g.patch Patch5: backport-Use-signal-safe-write-function-in-signal-handler.patch Patch6: backport-Handle-SIGUSR1-only-after-config-parsing.patch +Patch7: backport-CVE-2025-54389.patch +Patch8: backport-CVE-2025-54409.patch %description AIDE (Advanced Intrusion Detection Environment, [eyd]) is a file and directory integrity checker. @@ -83,6 +85,12 @@ make check %{_mandir}/*/* %changelog +* Fri Aug 15 2025 yixiangzhike - 0.17.4-8 +- Type:CVE +- ID:CVE-2025-54389 CVE-2025-54409 +- SUG:NA +- DESC: fix CVE-2025-54389 CVE-2025-54409 + * Tue Jul 8 2025 yixiangzhike - 0.17.4-7 - Type:bugfix - ID:NA diff --git a/backport-CVE-2025-54389.patch b/backport-CVE-2025-54389.patch new file mode 100644 index 0000000..5b41d5d --- /dev/null +++ b/backport-CVE-2025-54389.patch @@ -0,0 +1,340 @@ +From: Hannes von Haugwitz +Description: Fix for CVE-2025-54389 + +Reference: https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/aide/0.17.4-1ubuntu0.2/aide_0.17.4-1ubuntu0.2.debian.tar.xz +Conflict: Code context adaptation in src/aide.c + +diff --git before/doc/aide.1 after/doc/aide.1 +index a7b9462..5d10e33 100644 +--- before/doc/aide.1 ++++ after/doc/aide.1 +@@ -122,12 +122,25 @@ SIGUSR1 toggles the log_level between current and debug level. + .PP + .SH NOTES + ++.IP "Checksum encoding" ++ + The checksums in the database and in the output are by default base64 + encoded (see also report_base16 option). + To decode them you can use the following shell command: + + echo | base64 \-d | hexdump \-v \-e '32/1 "%02x" "\\n"' + ++.IP "Control characters" ++ ++Control characters (00-31 and 127) are always escaped in log and plain report ++output. They are escaped by a literal backslash (\\) followed by exactly 3 ++digits representing the character in octal notation (e.g. a newline is output ++as "\fB\\012\fR"). A literal backslash is not escaped unless it is followed by ++3 digits (0-9), in this case the literal backslash is escaped as ++"\fB\\134\fR". Reports in JSON format are escaped according to the JSON specs ++(e.g. a newline is output as "\fB\\b\fR" or an escape (\fBESC\fR) is output as ++"\fB\\u001b\fR") ++ + .PP + .SH FILES + +diff --git before/include/util.h after/include/util.h +index aaff780..d339740 100644 +--- before/include/util.h ++++ after/include/util.h +@@ -51,6 +51,9 @@ int cmpurl(url_t*, url_t*); + + int contains_unsafe(const char*); + ++char *strnesc(const char *, size_t); ++char *stresc(const char *); ++ + void decode_string(char*); + + char* encode_string(const char*); +diff --git before/src/aide.c after/src/aide.c +index b04e721..576fbd3 100644 +--- before/src/aide.c ++++ after/src/aide.c +@@ -274,7 +274,8 @@ static void read_param(int argc,char**argv) + if((conf->limit_crx=pcre2_compile((PCRE2_SPTR) conf->limit, PCRE2_ZERO_TERMINATED, PCRE2_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) { + PCRE2_UCHAR pcre2_error[128]; + pcre2_get_error_message(pcre2_errorcode, pcre2_error, 128); +- INVALID_ARGUMENT("--limit", error in regular expression '%s' at %zu: %s, conf->limit, pcre2_erroffset, pcre2_error) ++ char * limit_safe = stresc(conf->limit); ++ INVALID_ARGUMENT("--limit", error in regular expression '%s' at %zu: %s, limit_safe, pcre2_erroffset, pcre2_error) + + } + conf->limit_md = pcre2_match_data_create_from_pattern(conf->limit_crx, NULL); +@@ -572,7 +573,11 @@ int main(int argc,char**argv) + rx_rule* rule = NULL; + int match = check_rxtree(conf->check_path, conf->tree, &rule, conf->check_file_type, true); + if (match < 0) { +- fprintf(stdout, "[ ] %c '%s': outside of limit '%s'\n", get_restriction_char(conf->check_file_type), conf->check_path, conf->limit); ++ char* limit_safe = stresc(conf->limit); ++ char* path_safe = stresc(conf->check_path); ++ fprintf(stdout, "[ ] %c '%s': outside of limit '%s'\n", get_restriction_char(conf->check_file_type), path_safe, limit_safe); ++ free(path_safe); ++ free(limit_safe); + exit(2); + } else { + exit(match?0:1); +diff --git before/src/gen_list.c after/src/gen_list.c +index 98b437c..7059899 100644 +--- before/src/gen_list.c ++++ after/src/gen_list.c +@@ -510,7 +510,9 @@ int check_rxtree(char* filename,seltree* tree, rx_rule* *rule, RESTRICTION_TYPE + int match = check_seltree(tree, filename, file_type, rule); + if (dry_run) { + char * str; +- fprintf(stdout, "[%c] %c '%s': ", match?'X':' ', get_restriction_char(file_type), filename); ++ char* filename_safe = stresc(filename); ++ fprintf(stdout, "[%c] %c '%s': ", match?'X':' ', get_restriction_char(file_type), filename_safe); ++ free(filename_safe); + if (match > 0) { + char* attr_str; + fprintf(stdout, "%s: '%s%s %s %s' (%s:%d: '%s')\n", get_rule_type_long_string(match), match == EQUAL_MATCH?"=":"", (*rule)->rx, str = get_restriction_string((*rule)->restriction), attr_str = diff_attributes(0, (*rule)->attr), (*rule)->config_filename, (*rule)->config_linenumber, (*rule)->config_line); +diff --git before/src/log.c after/src/log.c +index 5a25a36..5d7fa74 100644 +--- before/src/log.c ++++ after/src/log.c +@@ -25,6 +25,7 @@ + + #include "log.h" + #include "locale-aide.h" ++#include "util.h" + + LOG_LEVEL prev_log_level = LOG_LEVEL_UNSET; + LOG_LEVEL log_level = LOG_LEVEL_UNSET; +@@ -60,7 +61,7 @@ static void cache_line(LOG_LEVEL level, const char* format, va_list ap) { + + void * tmp = realloc(cached_lines, (ncachedlines+1) * sizeof(log_cache)); /* freed in log_cached_lines() */ + if (tmp == NULL) { +- log_msg(LOG_LEVEL_ERROR, "realloc() failed: %s", strerror(errno)); ++ fprintf(stderr, "%s: realloc: failed to allocate memory\n", log_level_array[LOG_LEVEL_ERROR-1].log_string); + exit(EXIT_FAILURE); + } else { + cached_lines = tmp; +@@ -85,7 +86,12 @@ const char * get_log_level_name(LOG_LEVEL level) { + + static void log_cached_lines(void) { + for(int i = 0; i < ncachedlines; ++i) { +- log_msg(cached_lines[i].level, "%s", cached_lines[i].message); ++ LOG_LEVEL level = cached_lines[i].level; ++ if (level == LOG_LEVEL_ERROR || level <= log_level) { ++ char * msg_safe = stresc(cached_lines[i].message); ++ fprintf(stderr, "%s: %s\n", log_level_array[level-1].log_string, msg_safe); ++ free(msg_safe); ++ } + free(cached_lines[i].message); + } + ncachedlines = 0; +@@ -96,9 +102,24 @@ static void vlog_msg(LOG_LEVEL level,const char* format, va_list ap) { + FILE* url = stderr; + + if (level == LOG_LEVEL_ERROR || level <= log_level) { +- fprintf(url, "%s: ", log_level_array[level-1].log_string ); +- vfprintf(url, format, ap); +- fprintf(url, "\n"); ++ ++ va_list aq; ++ va_copy(aq, ap); ++ size_t n = vsnprintf(NULL, 0, format, aq) + 1; ++ va_end(aq); ++ ++ int size = n * sizeof(char); ++ char *msg_unsafe = malloc(size); ++ if (msg_unsafe == NULL) { ++ fprintf(stderr, "%s: malloc: failed to allocate %d bytes of memory\n", log_level_array[LOG_LEVEL_ERROR-1].log_string, size); ++ exit(EXIT_FAILURE); ++ } ++ ++ vsnprintf(msg_unsafe, n, format, ap); ++ char *msg_safe = stresc(msg_unsafe); ++ free(msg_unsafe); ++ fprintf(url, "%s: %s\n", log_level_array[level-1].log_string, msg_safe); ++ free(msg_safe); + } else if (log_level == LOG_LEVEL_UNSET) { + cache_line(level, format, ap); + } +diff --git before/src/report.c after/src/report.c +index b342a0f..e3cee48 100644 +--- before/src/report.c ++++ after/src/report.c +@@ -631,6 +631,7 @@ if ((conf->action&(DO_COMPARE|DO_DIFF) || (conf->action&DO_INIT && r->detailed_i + #endif + )) { + ++ char *filename_safe = stresc(((node->checked&NODE_REMOVED)?node->old_data:node->new_data)->filename); + if(r->summarize_changes) { + int i; + char* summary = checked_malloc ((report_attrs_order_length+1) * sizeof (char)); +@@ -676,17 +677,18 @@ if ((conf->action&(DO_COMPARE|DO_DIFF) || (conf->action&DO_INIT && r->detailed_i + } + } + summary[report_attrs_order_length]='\0'; +- report_printf(r, "\n%s: %s", summary, ((node->checked&NODE_REMOVED)?node->old_data:node->new_data)->filename); ++ report_printf(r, "\n%s: %s", summary, filename_safe); + free(summary); summary=NULL; + } else { + if (node->checked&NODE_ADDED) { +- report_printf(r,_("\nadded: %s"),(node->new_data)->filename); ++ report_printf(r,_("\nadded: %s"), filename_safe); + } else if (node->checked&NODE_REMOVED) { +- report_printf(r,_("\nremoved: %s"),(node->old_data)->filename); ++ report_printf(r,_("\nremoved: %s"), filename_safe); + } else if (node->checked&NODE_CHANGED) { +- report_printf(r,_("\nchanged: %s"),(node->new_data)->filename); ++ report_printf(r,_("\nchanged: %s"), filename_safe); + } + } ++ free(filename_safe); + } + } else { + break; /* list sorted by report_level */ +@@ -698,36 +700,49 @@ if ((conf->action&(DO_COMPARE|DO_DIFF) || (conf->action&DO_INIT && r->detailed_i + static void print_attribute(REPORT_LEVEL report_level, db_line* oline, db_line* nline, + DB_ATTR_TYPE attr, report_t *r, const char* name, + DB_ATTR_TYPE report_attrs, DB_ATTR_TYPE added_attrs, DB_ATTR_TYPE removed_attrs) { +- char **ovalue, **nvalue; ++ char **ovalues = NULL; ++ char **nvalues = NULL; + int onumber, nnumber, olen, nlen, i, k, c; + int p = (width_details-(4 + MAX_WIDTH_DETAILS_STRING))/2; + + if ( (attr&report_attrs && r->level >= report_level) + || (report_attrs && attr&(added_attrs|removed_attrs) && r->level >= REPORT_LEVEL_ADDED_REMOVED_ATTRIBUTES) ) { + +- onumber=get_attribute_values(attr, oline, &ovalue, r); +- nnumber=get_attribute_values(attr, nline, &nvalue, r); ++ onumber=get_attribute_values(attr, oline, &ovalues, r); ++ nnumber=get_attribute_values(attr, nline, &nvalues, r); + + i = 0; + while (i= 0 || nlen-p*k >= 0) { + c = k*(p-1); + if (!onumber) { +- report_printf(r," %-*s%c %-*c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[i][c]:""); ++ report_printf(r," %-*s%c %-*c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[c]:""); + } else if (!nnumber) { +- report_printf(r," %-*s%c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p-1, olen-c>0?&ovalue[i][c]:""); ++ report_printf(r," %-*s%c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p-1, olen-c>0?&ovalue[c]:""); + } else { +- report_printf(r," %-*s%c %-*.*s| %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, p-1, olen-c>0?&ovalue[i][c]:"", p-1, nlen-c>0?&nvalue[i][c]:""); ++ report_printf(r," %-*s%c %-*.*s| %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, p-1, olen-c>0?&ovalue[c]:"", p-1, nlen-c>0?&nvalue[c]:""); + } + k++; + } + ++i; ++ free(ovalue); ++ free(nvalue); + } +- for(i=0; i < onumber; ++i) { free(ovalue[i]); ovalue[i]=NULL; } free(ovalue); ovalue=NULL; +- for(i=0; i < nnumber; ++i) { free(nvalue[i]); nvalue[i]=NULL; } free(nvalue); nvalue=NULL; ++ for(i=0; i < onumber; ++i) { free(ovalues[i]); ovalues[i]=NULL; } free(ovalues); ovalues=NULL; ++ for(i=0; i < nnumber; ++i) { free(nvalues[i]); nvalues[i]=NULL; } free(nvalues); nvalues=NULL; + } + } + +@@ -760,7 +775,9 @@ static void print_dbline_attributes(REPORT_LEVEL report_level, db_line* oline, d + if (file_type) { + report_printf(r, "%s: ", file_type); + } +- report_printf(r, "%s\n", (nline==NULL?oline:nline)->filename); ++ char *filename_safe = stresc((nline==NULL?oline:nline)->filename); ++ report_printf(r, "%s\n", filename_safe); ++ free(filename_safe); + } + + for (int j=0; j < report_attrs_order_length; ++j) { +@@ -829,9 +846,11 @@ static void terse_report(seltree* node) { + if (!(node->checked&(NODE_MOVED_IN|NODE_MOVED_OUT))){ + if (r->level >= REPORT_LEVEL_LIST_ENTRIES + && (node->old_data->attr&~(r->ignore_removed_attrs))^(node->new_data->attr&~(r->ignore_added_attrs)) ) { ++ char *entry_safe = stresc(node->old_data->filename); + char *str = NULL; + report_printf(r, "Entry %s in databases has different attributes: %s\n", +- node->old_data->filename,str= diff_attributes(node->old_data->attr&~(r->ignore_removed_attrs),node->new_data->attr&~(r->ignore_added_attrs))); ++ entry_safe,str= diff_attributes(node->old_data->attr&~(r->ignore_removed_attrs),node->new_data->attr&~(r->ignore_added_attrs))); ++ free(entry_safe); + free(str); + } + DB_ATTR_TYPE changed_attrs = (node->changed_attrs)&~(r->ignore_changed_attrs); +@@ -933,14 +952,18 @@ static void print_report_header() { + if (r->level >= REPORT_LEVEL_SUMMARY) { + int first = 1; + if (conf->limit != NULL) { +- report_printf(r, _("Limit: %s"), conf->limit); ++ char *limit_safe = stresc(conf->limit); ++ report_printf(r, _("Limit: %s"), limit_safe); ++ free(limit_safe); + first = 0; + } + + if (conf->action&(DO_INIT|DO_COMPARE) && conf->root_prefix_length > 0) { + if (first) { first=0; } + else { report_printf(r," | "); } +- report_printf(r, _("Root prefix: %s"),conf->root_prefix); ++ char *prefix_safe = stresc(conf->root_prefix); ++ report_printf(r, _("Root prefix: %s"), prefix_safe); ++ free(prefix_safe); + } + + if (r->level != REPORT_LEVEL_CHANGED_ATTRIBUTES) { +diff --git before/src/util.c after/src/util.c +index 1826059..ba7359e 100644 +--- before/src/util.c ++++ after/src/util.c +@@ -99,6 +99,40 @@ int cmpurl(url_t* u1,url_t* u2) + return RETOK; + }; + ++static size_t escape_str(const char *unescaped_str, char *str, size_t s) { ++ size_t n = 0; ++ size_t i = 0; ++ char c; ++ while (i < s && (c = unescaped_str[i])) { ++ if ((c >= 0 && (c < 0x1f || c == 0x7f)) || ++ (c == '\\' && isdigit(unescaped_str[i+1]) ++ && isdigit(unescaped_str[i+2]) ++ && isdigit(unescaped_str[i+3]) ++ ) ) { ++ if (str) { snprintf(&str[n], 5, "\\%03o", c); } ++ n += 4; ++ } else { ++ if (str) { str[n] = c; } ++ n++; ++ } ++ i++; ++ } ++ if (str) { str[n] = '\0'; } ++ n++; ++ return n; ++} ++ ++char *strnesc(const char *unescaped_str, size_t s) { ++ int n = escape_str(unescaped_str, NULL, s); ++ char *str = checked_malloc(n); ++ escape_str(unescaped_str, str, s); ++ return str; ++} ++ ++char *stresc(const char *unescaped_str) { ++ return strnesc(unescaped_str, strlen(unescaped_str)); ++} ++ + /* Returns 1 if the string contains unsafe characters, 0 otherwise. */ + int contains_unsafe (const char *s) + { diff --git a/backport-CVE-2025-54409.patch b/backport-CVE-2025-54409.patch new file mode 100644 index 0000000..db1456a --- /dev/null +++ b/backport-CVE-2025-54409.patch @@ -0,0 +1,59 @@ +From: Hannes von Haugwitz +Description: Fix for CVE-2025-54409 + +Reference: https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/aide/0.17.4-1ubuntu0.2/aide_0.17.4-1ubuntu0.2.debian.tar.xz +Conflict: NA + +diff --git before/src/db.c after/src/db.c +index ac55f0a..ac842bc 100644 +--- before/src/db.c ++++ after/src/db.c +@@ -351,17 +351,27 @@ db_line* db_char2line(char** ss, database* db){ + num = 0; + while (num < line->xattrs->num) + { +- byte *val = NULL; +- size_t vsz = 0; +- + tval = strtok(NULL, ","); + line->xattrs->ents[num].key = db_readchar(checked_strdup(tval)); + tval = strtok(NULL, ","); +- val = base64tobyte(tval, strlen(tval), &vsz); +- line->xattrs->ents[num].val = val; +- line->xattrs->ents[num].vsz = vsz; +- +- ++num; ++ if (strcmp(tval,"0") != 0) { ++ line->xattrs->ents[num].val = decode_base64(tval, strlen(tval), &line->xattrs->ents[num].vsz); ++ } else { ++ line->xattrs->ents[num].val = checked_strdup(""); ++ line->xattrs->ents[num].vsz = 0; ++ } ++ if (line->xattrs->ents[num].val == NULL) { ++ LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, "error while reading xattrs for '%s' from database (discarding extended attributes)", line->filename) ++ for (int j = num; j >= 0 ; --j) { ++ free(line->xattrs->ents[j].key); ++ line->xattrs->ents[j].key = NULL; ++ free(line->xattrs->ents[j].val); ++ line->xattrs->ents[j].val = NULL; ++ } ++ line->xattrs->num = 0; ++ } else { ++ ++num; ++ } + } + } + break; +diff --git before/src/util.c after/src/util.c +index ba7359e..092457e 100644 +--- before/src/util.c ++++ after/src/util.c +@@ -39,7 +39,7 @@ + #include "db_config.h" + #include "util.h" + +-#define URL_UNSAFE " <>\"#%{}|\\^~[]`@:\033'" ++#define URL_UNSAFE " <>\"#%{}|\\^~[]`@:\033'," + #define ISPRINT(c) (isascii(c) && isprint(c)) + + const char* btoa(bool b) { -- Gitee