Summary
=======

Rajesh Pangare discovered that AIDE, a host-based file and directory integrity
checker, improperly handles file names with invalid UTF-8 byte sequences. An
attacker can craft a malicious file or directory name by including invalid
UTF-8 bytes so that the file/directory is silently ignored by AIDE. A local
user can exploit this to bypass the AIDE detection of malicious files.

Project
=======

AIDE (Advanced Intrusion Detection Environment)

Affected versions
=================

AIDE >= 0.16, <= 0.19.4

CVE ID
======

A CVE ID has been requested, but is not yet available; please cross-reference
this vulnerability as GHSA-wq25-fm54-5q3q for now. The advisory will be
updated, as soon as a CVE ID is available.

Proof of concept
================

    $ mkdir test
    $ touch test/"$(printf 'invalid_\x80_byte.txt')"
    $ touch test/regular_file.txt
    $ aide --config <(printf -- "database_in=file:/dev/null\nroot_prefix = 
./test\n/ R") --check
    Start timestamp: 2026-10-17 19:30:35 +0200 (AIDE 0.19.4)
    AIDE found differences between database and filesystem!!
    Root prefix: ./test

    Summary:
      Total number of entries:  2
      Added entries:            2
      Removed entries:          0
      Changed entries:          0

    ---------------------------------------------------
    Added entries:
    ---------------------------------------------------

    d++++++++++++++++++: /
    f++++++++++++++++++: /regular_file.txt

    ---------------------------------------------------
    The attributes of the (uncompressed) database(s):
    ---------------------------------------------------

    /dev/null
     SHA256    : 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NM
                 pJWZG3hSuFU=
     SHA512    : z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXc
                 g/SpIdNs6c5H0NE8XYXysP+DGNKHfuwv
                 Y7kxvUdBeoGlODJ6+SfaPg==
     STRIBOG256: P1OaIT6XyALMIp1HTGqjKoJaNgsqkzqU
                 n9klII2c4bs=
     STRIBOG512: jpRdogmqhp8EVZKFKbyuRnnphzq3B7VT
                 FfVs65i+8Kc2L3FVKDVu6DzaXyqsTGrS
                 ujpxXBvNgcuOn5C/TBwaig==
     SHA512/256: xnK40e9W7Sirh8NiLFEUBpvdOte4+XN0
                 mNDAHs7wlno=
     SHA3-256  : p//G+L8e12ZRwUdWoGHWYvWA/03kO0n6
                 gtgKS4D4Q0o=
     SHA3-512  : pp9zzKI6msXItWfcGFp1bpfJghZP4lhZ
                 4NHcwUdcgKYVshI68fX5TBHj6UAsOsVY
                 9QAZnZW20+MBdYWGKB3NJg==


    End timestamp: 2026-10-17 19:30:35 +0200 (run time: 0m 0s)


The report correctly shows the addition of `regular_file.txt` but misses the
addition of the malicious file. The malicious file is also not added to the
AIDE database.

Analysis
========

The vulnerability is caused by improper handling of UTF-8 validation errors
returned from `pcre2_match` function, resulting in malicious file names never
matching any regular expression rule.

This allows a user to craft a file name including invalid UTF-8 byte sequences
to effectively hide the file from AIDE detection.

Mitigation
==========

Upgrade to AIDE v0.19.5

Alternatively apply the provided patch:

aide-0.19_GHSA-wq25-fm54-5q3q_invalid-utf-8.patch

Credit
======

The issue was reported by Rajesh Pangare.

References
==========
[AIDE](https://aide.github.io)
[v0.19.5](https://github.com/aide/aide/releases/tag/v0.19.5)
[GHSA-wq25-fm54-5q3q](https://github.com/aide/aide/security/advisories/GHSA-wq25-fm54-5q3q)
diff --git before/doc/aide.conf.5 after/doc/aide.conf.5
index 0f7da37..2213db2 100644
--- before/doc/aide.conf.5
+++ after/doc/aide.conf.5
@@ -480,6 +480,12 @@ against the complete path.
 Special characters can be escaped using
 two-digit URL encoding (for example, %20 to represent a space).
 
+Rules and file names are always treated as single byte sequences (i.e. not as
+UTF strings). That means that multi byte characters aren't matched by single
+dot ('.') or inside a character class. To match UTF multi byte characters,
+explicitly state them in the rule either verbatim (e.g. `®` for registered
+sign) or by hexadecimal escaping (e.g. '\\xC2\\xAE').
+
 AIDE uses a deepest-match algorithm to find the tree node to search,
 but a first-match algorithm inside the node.
 (see also \fBrule\fP log level).
diff --git before/src/aide.c after/src/aide.c
index 622e108..aeef53f 100644
--- before/src/aide.c
+++ after/src/aide.c
@@ -299,7 +299,7 @@ static void read_param(int argc,char**argv)
                 int len = (strlen(optarg)+1) * sizeof(char);
                 conf->limit=checked_malloc(len);
                 strncpy(conf->limit, optarg, len);
-                if((conf->limit_crx=pcre2_compile((PCRE2_SPTR) conf->limit, PCRE2_ZERO_TERMINATED, PCRE2_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
+                if((conf->limit_crx=pcre2_compile((PCRE2_SPTR) conf->limit, PCRE2_ZERO_TERMINATED, PCRE2_NEVER_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
                     PCRE2_UCHAR pcre2_error[128];
                     pcre2_get_error_message(pcre2_errorcode, pcre2_error, 128);
                     char * limit_safe = stresc(conf->limit);
diff --git before/src/conf_eval.c after/src/conf_eval.c
index 5774ce6..e884569 100644
--- before/src/conf_eval.c
+++ after/src/conf_eval.c
@@ -597,7 +597,7 @@ static void include_directory(const char* dir, const char* rx, bool execute, cha
     PCRE2_SIZE pcre2_erroffset;
     pcre2_code* crx;
 
-    if((crx = pcre2_compile((PCRE2_SPTR) rx, PCRE2_ZERO_TERMINATED, PCRE2_UTF, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
+    if((crx = pcre2_compile((PCRE2_SPTR) rx, PCRE2_ZERO_TERMINATED, PCRE2_NEVER_UTF, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
         PCRE2_UCHAR pcre2_error[128];
         pcre2_get_error_message(pcre2_errorcode, pcre2_error, 128);
         LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'%s': error in regular expression '%s' at %zu: %s", execute?"@@x_include":"@@include", rx, pcre2_erroffset, pcre2_error)
diff --git before/src/seltree.c after/src/seltree.c
index 912a2bc..2c623a1 100644
--- before/src/seltree.c
+++ after/src/seltree.c
@@ -231,7 +231,7 @@ rx_rule * add_rx_to_tree(char * rx, rx_restriction_t restriction, AIDE_RULE_TYPE
     int pcre2_errorcode;
     PCRE2_SIZE pcre2_erroffset;
 
-    if((r->crx=pcre2_compile((PCRE2_SPTR) r->rx, PCRE2_ZERO_TERMINATED, PCRE2_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
+    if((r->crx=pcre2_compile((PCRE2_SPTR) r->rx, PCRE2_ZERO_TERMINATED, PCRE2_NEVER_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
         PCRE2_UCHAR pcre2_error[128];
         pcre2_get_error_message(pcre2_errorcode, pcre2_error, 128);
         log_msg(LOG_LEVEL_ERROR, "%s:%d:%zu: error in rule '%s': %s (line: '%s')", filename, linenumber, pcre2_erroffset, rx, pcre2_error, linebuf);

Attachment: signature.asc
Description: PGP signature

Reply via email to