diff --git a/aide-0.16-CVE-2025-54389-part1.patch b/aide-0.16-CVE-2025-54389-part1.patch new file mode 100644 index 0000000..b600868 --- /dev/null +++ b/aide-0.16-CVE-2025-54389-part1.patch @@ -0,0 +1,292 @@ +diff --git a/ChangeLog b/ChangeLog +index 263c438f4a2a38edc45f91c0d5a216112a8fa38c..6aa3de30b76ae98bebe89df49a7041bc6e50df25 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,27 +1,31 @@ ++2025-08-07 Hannes von Haugwitz ++ * Escape control characters in report and log output (CVE-2025-54389), ++ thanks to Rajesh Pangare for reporting this issue ++ + 2016-07-25 Hannes von Haugwitz +- * Release version 0.16 ++ * Release version 0.16 + + 2016-07-11 Hannes von Haugwitz + * Fix example aide.conf (xattr -> xattrs) + * aide.conf.5: update "SELECTION LINES" section + * Released version 0.16rc1 + + 2016-07-10 Hannes von Haugwitz + * Fix compilation with latest libaudit + * Use AC_PROG_CC_C99 instead of AC_PROG_CC + * Add AM_PROG_CC_C_O + * aide.conf.in: logfile -> file + * Update README + * Update manual pages (aide.1 and aide.conf.5) + + 2016-07-07 Hannes von Haugwitz + * Adapt manual to version 0.16 + + 2016-06-08 Hannes von Haugwitz + * Add missing break statements + + 2016-04-15 Hannes von Haugwitz + * Released version 0.16b1 + + 2016-04-13 Hannes von Haugwitz + * Fix spelling errors +diff --git a/include/util.h b/include/util.h +index 79988536c974ca83b14696380f6006031e0fa5e4..68e6ee2a905856bc7b73f1a67633585e0c1d814d 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -22,48 +22,51 @@ + #ifndef _UTIL_H_INCLUDED + #define _UTIL_H_INCLUDED + #include + #include + #include "db_config.h" + + #define HEXD2ASC(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'A')) + + #define ASC2HEXD(x) (((x) >= '0' && (x) <= '9') ? \ + ((x) - '0') : (toupper(x) - 'A' + 10)) + + #define ISXDIGIT(x) isxdigit ((unsigned char)(x)) + + #define CLEANDUP(x) (contains_unsafe (x) ? encode_string (x) : strdup (x)) + + #ifndef HAVE_STRICMP + # define stricmp(a,b) strcasecmp( (a), (b) ) + #endif + + int cmpurl(url_t*, url_t*); + + url_t* parse_url(char*); + + int contains_unsafe(const char*); + ++char *strnesc(const char *, size_t); ++char *stresc(const char *); ++ + void decode_string(char*); + + char* encode_string(const char*); + + char* perm_to_char(mode_t perm); + + void sig_handler(int signal); + + void init_sighandler(void); + + char *expand_tilde(char * path); + + #ifndef HAVE_STRNSTR + char* strnstr(char* haystack,char* needle,int n); + #endif + + #ifndef HAVE_STRNLEN + size_t strnlen(const char *s, size_t maxlen); + #endif + + int syslog_facility_lookup(char *); + + #endif +diff --git a/src/aide.c b/src/aide.c +index f85c1b4b95301eb3e2cf9212093751f39ea49b10..b9b2e325cfffcd4f9f3ce4c0ae3d06dce7a6956b 100644 +--- a/src/aide.c ++++ b/src/aide.c +@@ -164,54 +164,58 @@ static int read_param(int argc,char**argv) + error(0,_("-B must have a parameter\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'A': { + if (optarg!=NULL) { + int errorno=commandconf('A',optarg); + if (errorno!=0){ + error(0,_("Configuration error in after statement:%s\n"),optarg); + exit(INVALID_CONFIGURELINE_ERROR); + } + } else { + error(0,_("-A must have a parameter\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'l': { + if (optarg!=NULL) { + const char* pcre_error; + int pcre_erroffset; + conf->limit=malloc(strlen(optarg)+1); + strcpy(conf->limit,optarg); + if((conf->limit_crx=pcre_compile(conf->limit, PCRE_ANCHORED, &pcre_error, &pcre_erroffset, NULL)) == NULL) { +- error(0,_("Error in limit regexp '%s' at %i: %s\n"), conf->limit, pcre_erroffset, pcre_error); ++ char *limit_safe = stresc(conf->limit); ++ error(0,_("Error in limit regexp '%s' at %i: %s\n"), limit_safe, pcre_erroffset, pcre_error); ++ free(limit_safe); + exit(INVALID_ARGUMENT_ERROR); + } +- error(200,_("Limit set to '%s'\n"), conf->limit); ++ char *limit_safe = stresc(conf->limit); ++ error(200,_("Limit set to '%s'\n"), limit_safe); ++ free(limit_safe); + } else { + error(0,_("-l must have an argument\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'r': { + if(optarg!=NULL) { + do_repurldef(optarg); + }else { + error(0,_("-r must have an argument\n")); + } + break; + } + case 'i': { + if(conf->action==0){ + conf->action=DO_INIT; + }else { + error(0, + _("Cannot have multiple commands on a single commandline.\n")); + exit(INVALID_ARGUMENT_ERROR); + }; + break; + } + case 'C': { +diff --git a/src/util.c b/src/util.c +index ea438273296fbac24fb5d83cd0f2661aa93c0c0a..c39ff352d7fd707471b6d6add8c099a3ca643b9d 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -2,89 +2,128 @@ + * + * Copyright (C) 1999-2002,2004-2006,2010,2011,2013,2016 Rami Lehti, Pablo + * Virolainen, Mike Markley, Richard van den Berg, Hannes von Haugwitz + * $Header$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "aide.h" + #include + #include + #include + #include ++#include + #include + #include + #include + /*for locale support*/ + #include "locale-aide.h" + /*for locale support*/ + + + #ifndef MAXHOSTNAMELEN + #define MAXHOSTNAMELEN 256 + #endif + + #include "report.h" + #include "db_config.h" + #include "util.h" + + #define URL_UNSAFE " <>\"#%{}|\\^~[]`@:\033'" + #define ISPRINT(c) (isascii(c) && isprint(c)) + + static const char* url_name[] = { + "file", "stdin", "stdout", "stderr", "fd", "sql", "syslog", "database", "https", "http", "ftp" }; + + static const int url_value[] = { + url_file, url_stdin, url_stdout,url_stderr,url_fd, url_sql, url_syslog, url_database, url_https, url_http, url_ftp }; + + const int url_ntypes=sizeof(url_value)/sizeof(URL_TYPE); + + int cmpurl(url_t* u1,url_t* u2) + { + if(u1->type!= u2->type){ + return RETFAIL; + }; + if(strcmp(u1->value,u2->value)!=0){ + return RETFAIL; + } + + 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 = malloc(n); ++ if (str == NULL) { ++ error(0, "malloc: failed to allocate %d bytes of memory\n", n); ++ exit(EXIT_FAILURE); ++ } ++ escape_str(unescaped_str, str, s); ++ return str; ++} ++ ++char *stresc(const char *unescaped_str) { ++ return strnesc(unescaped_str, strlen(unescaped_str)); ++} ++ + url_t* parse_url(char* val) + { + url_t* u=NULL; + char* r=NULL; + char* val_copy=NULL; + int i=0; + + if(val==NULL){ + return NULL; + } + + u=(url_t*)malloc(sizeof(url_t)); + + /* We don't want to modify the original hence strdup(val) */ + val_copy=strdup(val); + for(r=val_copy;r[0]!=':'&&r[0]!='\0';r++); + + if(r[0]!='\0'){ + r[0]='\0'; + r++; + } + u->type=url_unknown; + for(i=0;itype=url_value[i]; + diff --git a/aide-0.16-CVE-2025-54389-part2.patch b/aide-0.16-CVE-2025-54389-part2.patch new file mode 100644 index 0000000..e5045cd --- /dev/null +++ b/aide-0.16-CVE-2025-54389-part2.patch @@ -0,0 +1,91 @@ +diff -U0 aide-0.16/ChangeLog.orig aide-0.16/ChangeLog +diff -up aide-0.16/doc/aide.1.in.orig aide-0.16/doc/aide.1.in +diff -up aide-0.16/doc/aide.1.orig aide-0.16/doc/aide.1 +diff -up aide-0.16/include/util.h.orig aide-0.16/include/util.h +diff -up aide-0.16/src/aide.c.orig aide-0.16/src/aide.c +diff -up aide-0.16/src/compare_db.c.orig aide-0.16/src/compare_db.c +--- aide-0.16/src/compare_db.c.orig 2025-08-20 16:40:25.219559352 +0200 ++++ aide-0.16/src/compare_db.c 2025-08-20 16:40:33.945999660 +0200 +@@ -526,15 +526,24 @@ static void print_line(seltree* node) { + } + } + summary[length]='\0'; +- error(2,"\n%s: %s", summary, (node->checked&NODE_REMOVED?node->old_data:node->new_data)->filename); ++ const char *rawname = (node->checked&NODE_REMOVED?node->old_data:node->new_data)->filename; ++ char *filename_safe = stresc(rawname); ++ error(2,"\n%s: %s", summary, filename_safe); ++ free(filename_safe); + free(summary); summary=NULL; + } else { + if (node->checked&NODE_ADDED) { +- error(2,"added: %s\n",(node->new_data)->filename); ++ char *filename_safe = stresc((node->new_data)->filename); ++ error(2,"added: %s\n",filename_safe); ++ free(filename_safe); + } else if (node->checked&NODE_REMOVED) { +- error(2,"removed: %s\n",(node->old_data)->filename); ++ char *filename_safe = stresc((node->old_data)->filename); ++ error(2,"removed: %s\n",filename_safe); ++ free(filename_safe); + } else if (node->checked&NODE_CHANGED) { +- error(2,"changed: %s\n",(node->new_data)->filename); ++ char *filename_safe = stresc((node->new_data)->filename); ++ error(2,"changed: %s\n",filename_safe); ++ free(filename_safe); + } + } + } +@@ -552,6 +561,9 @@ static void print_dbline_attributes(db_l + error(2,"%s: ", file_type); + } + error(2,"%s\n", (nline==NULL?oline:nline)->filename); ++ char *filename_safe = stresc((nline==NULL?oline:nline)->filename); ++ error(2,"%s\n", filename_safe); ++ free(filename_safe); + attrs=force_attrs|(~(ignored_changed_attrs)&changed_attrs); + for (j=0; j < length; ++j) { + if (details_attributes[j]&attrs) { +@@ -559,21 +571,35 @@ static void print_dbline_attributes(db_l + nnumber=get_attribute_values(details_attributes[j], nline, &nvalue); + i = 0; + while (i= 0 || nlen-p*k >= 0) { + c = k*(p-1); + if (!onumber) { +- error(2," %s%-9s%c %-*c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[i][c]:""); ++ error(2," %s%-9s%c %-*c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, ' ', p-1, nlen-c>0?&nvalue_safe[c]:""); + } else if (!nnumber) { +- error(2," %s%-9s%c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p-1, olen-c>0?&ovalue[i][c]:""); ++ error(2," %s%-9s%c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p-1, olen-c>0?&ovalue_safe[c]:""); + } else { +- error(2," %s%-9s%c %-*.*s| %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, p-1, olen-c>0?&ovalue[i][c]:"", p-1, nlen-c>0?&nvalue[i][c]:""); ++ error(2," %s%-9s%c %-*.*s| %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, p-1, olen-c>0?&ovalue_safe[c]:"", p-1, nlen-c>0?&nvalue_safe[c]:""); + } + k++; + } + ++i; ++ free(ovalue_safe); ++ free(nvalue_safe); + } + 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; +diff -up aide-0.16/src/error.c.orig aide-0.16/src/error.c +diff -up aide-0.16/src/gen_list.c.orig aide-0.16/src/gen_list.c +diff -up aide-0.16/src/util.c.orig aide-0.16/src/util.c diff --git a/aide-0.16-CVE-2025-54389.patch b/aide-0.16-CVE-2025-54389.patch new file mode 100644 index 0000000..e9c4348 --- /dev/null +++ b/aide-0.16-CVE-2025-54389.patch @@ -0,0 +1,83 @@ +diff -U0 aide-0.16/ChangeLog.orig aide-0.16/ChangeLog +--- aide-0.16/ChangeLog.orig 2025-08-20 12:10:17.957899161 +0200 ++++ aide-0.16/ChangeLog 2025-08-20 12:10:21.043397557 +0200 +@@ -0,0 +1,4 @@ ++2025-08-07 Hannes von Haugwitz ++ * Escape control characters in report and log output (CVE-2025-54389), ++ thanks to Rajesh Pangare for reporting this issue ++ +@@ -2 +6 @@ +- * Release version 0.16 ++ * Release version 0.16 +diff -up aide-0.16/doc/aide.1.orig aide-0.16/doc/aide.1 +diff -up aide-0.16/include/util.h.orig aide-0.16/include/util.h +--- aide-0.16/include/util.h.orig 2025-08-20 12:10:17.961526476 +0200 ++++ aide-0.16/include/util.h 2025-08-20 12:10:21.043645131 +0200 +@@ -44,6 +44,9 @@ url_t* parse_url(char*); + + 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 -up aide-0.16/src/aide.c.orig aide-0.16/src/aide.c +diff -up aide-0.16/src/gen_list.c.orig aide-0.16/src/gen_list.c +diff -up aide-0.16/src/util.c.orig aide-0.16/src/util.c +--- aide-0.16/src/util.c.orig 2025-08-20 12:10:17.954526409 +0200 ++++ aide-0.16/src/util.c 2025-08-20 12:10:21.043912548 +0200 +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -63,6 +64,44 @@ 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 = malloc(n); ++ if (str == NULL) { ++ error(0, "malloc: failed to allocate %d bytes of memory\n", n); ++ exit(EXIT_FAILURE); ++ } ++ escape_str(unescaped_str, str, s); ++ return str; ++} ++ ++char *stresc(const char *unescaped_str) { ++ return strnesc(unescaped_str, strlen(unescaped_str)); ++} ++ + url_t* parse_url(char* val) + { + url_t* u=NULL; diff --git a/aide.spec b/aide.spec index 6491c7d..5ecf983 100644 --- a/aide.spec +++ b/aide.spec @@ -1,7 +1,7 @@ Summary: Intrusion detection environment Name: aide Version: 0.16 -Release: 103%{?dist} +Release: 104%{?dist} URL: http://sourceforge.net/projects/aide License: GPLv2+ @@ -41,6 +41,8 @@ Patch9: aide-static-analysis.patch Patch10: aide-0.16-CVE-2021-45417.patch Patch11: aide-db-problem.patch Patch12: rootPrefix.patch +Patch13: aide-0.16-CVE-2025-54389-part1.patch +Patch14: aide-0.16-CVE-2025-54389-part2.patch %description AIDE (Advanced Intrusion Detection Environment) is a file integrity @@ -87,6 +89,11 @@ mkdir -p -m0700 %{buildroot}%{_localstatedir}/lib/aide %dir %attr(0700,root,root) %{_localstatedir}/log/aide %changelog +* Wed Aug 20 2025 Attila Lakatos - 0.16-104 +RHEL 9.7 ERRATUM +- CVE-2025-54389 aide: improper output neutralization enables bypassing +Resolves: RHEL-109912 + * Wed Jan 15 2025 Radovan Sroka - 0.16-103 RHEL 9.6.0 ERRATUM - /boot/grub2/grubenv's timestamp is getting modified continuously due to "boot_success" implementation