Summary ======= David Bouman discovered a heap-based buffer overflow vulnerability in base64 functions of AIDE, an advanced intrusion detection system. An attacker could crash the program and possibly execute arbitrary code through large (<16k) extended file attributes or ACL. A local user might exploit this flaw for root privilege escalation.
Project ======= AIDE (https://aide.github.io) Affected versions ================= AIDE >= 0.13, <= 0.17.3 CVE ID ====== CVE-2021-45417 Proof of concept ================ To take advantage of the flaw the user needs write access to a mounted file system that supports large enough extended attributes (e.g. XFS) or ACL (e.g. tmpfs). AIDE needs to be compiled with --with-xattr or --with-posix-acl configure flag (this is the case for most distributions). # extended attributes on XFS filesystem $ touch user-file; xattr -w user.comment "$(for i in {1..40000} ; do printf '%c' A ; done)" user-file # aide --config=/dev/null --after "$(pwd)/user-file xattrs" --after "database_out=file:/dev/null" --init # ACL on tmpfs file system $ touch user-file; for i in {1000..2000} ; do setfacl -m u:${i}:r user-file ; done # aide --config=/dev/null --after "$(pwd)/user-file acl" --after "database_out=file:/dev/null" --init Analysis ======== The vulnerability is caused by a fixed buffer size (16384 in src/base64.h[base.h]) in the encode_base64/decode_base64 functions[base64.c]. Initially this was safe as the base64 functions were only used for encoding/decoding of the calculated hashsums. However since the addition of extended file attribute and ACL support in AIDE 0.13 encode_base64 is also used for encoding xattr and ACL values before writing them to the database. This allows a user to create a file with a large extended attribute value or large ACL causing aide (ussaly triggered by cron as root) to segfault. The issue is fixed by precalculating the size of the return buffer depending on the input in the encode_base64/decode_base64 functions. [base64.h] https://github.com/aide/aide/blob/v0.17.3/include/base64.h#L38 [base64.c] https://github.com/aide/aide/blob/v0.17.3/src/base64.c Mitigation ========== Upgrade to AIDE v0.17.4 (only containing the fix for this issue) [v0.17.4] Alternatively apply one of the provided patches: aide-0.17-cve-2021-45417.patch: patch for 0.17.x aide-0.16-cve-2021-45417.patch: patch for 0.16.x (backported for Debian oldstable) Though not tested the patch for 0.16.x might also apply for earlier releases < 0.16. If you cannot upgrade, consider removing `acl` and `xattrs` groups from rules matching files on affected file systems. [v0.17.4] https://github.com/aide/aide/releases/tag/v0.17.4 Credit ====== The issue was reported by David Bouman.
diff --git a/include/base64.h b/include/base64.h index 0ff7116..381ef5d 100644 --- a/include/base64.h +++ b/include/base64.h @@ -36,7 +36,6 @@ #include <assert.h> #include "types.h" -#define B64_BUF 16384 #define FAIL -1 #define SKIP -2 diff --git a/src/base64.c b/src/base64.c index fd01bac..1b0f301 100644 --- a/src/base64.c +++ b/src/base64.c @@ -85,11 +85,9 @@ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL }; /* Returns NULL on error */ -/* FIXME Possible buffer overflow on outputs larger than B64_BUF */ char* encode_base64(byte* src,size_t ssize) { char* outbuf; - char* retbuf; int pos; int i, l, left; unsigned long triple; @@ -101,7 +99,10 @@ char* encode_base64(byte* src,size_t ssize) error(240,"\n"); return NULL; } - outbuf = (char *)malloc(sizeof(char)*B64_BUF); + + /* length of encoded base64 string (padded) */ + size_t length = sizeof(char)* ((ssize + 2) / 3) * 4; + outbuf = (char *)malloc(length + 1); /* Initialize working pointers */ inb = src; @@ -162,20 +163,14 @@ char* encode_base64(byte* src,size_t ssize) inb++; } - /* outbuf is not completely used so we use retbuf */ - retbuf=(char*)malloc(sizeof(char)*(pos+1)); - memcpy(retbuf,outbuf,pos); - retbuf[pos]='\0'; - free(outbuf); + outbuf[pos]='\0'; - return retbuf; + return outbuf; } -/* FIXME Possible buffer overflow on outputs larger than B64_BUF */ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) { byte* outbuf; - byte* retbuf; char* inb; int i; int l; @@ -188,10 +183,18 @@ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) if (!ssize||src==NULL) return NULL; + /* exit on unpadded input */ + if (ssize % 4) { + error(3, "decode_base64: '%s' has invalid length (missing padding characters?)", src); + return NULL; + } + + /* calculate length of decoded string, substract padding chars if any (ssize is >= 4) */ + size_t length = sizeof(byte) * ((ssize / 4) * 3)- (src[ssize-1] == '=') - (src[ssize-2] == '='); /* Initialize working pointers */ inb = src; - outbuf = (byte *)malloc(sizeof(byte)*B64_BUF); + outbuf = (byte *)malloc(length + 1); l = 0; triple = 0; @@ -243,15 +246,11 @@ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) inb++; } - retbuf=(byte*)malloc(sizeof(byte)*(pos+1)); - memcpy(retbuf,outbuf,pos); - retbuf[pos]='\0'; - - free(outbuf); + outbuf[pos]='\0'; if (ret_len) *ret_len = pos; - return retbuf; + return outbuf; } size_t length_base64(char* src,size_t ssize) diff --git a/src/db.c b/src/db.c index 858240d..62c4faa 100644 --- a/src/db.c +++ b/src/db.c @@ -664,13 +664,15 @@ db_line* db_char2line(char** ss,int db){ time_t base64totime_t(char* s){ + if(strcmp(s,"0")==0){ + return 0; + } byte* b=decode_base64(s,strlen(s),NULL); char* endp; - if (b==NULL||strcmp(s,"0")==0) { + if (b==NULL) { /* Should we print error here? */ - free(b); return 0; } else {
diff --git a/include/base64.h b/include/base64.h index a446812..d9cbfd2 100644 --- a/include/base64.h +++ b/include/base64.h @@ -35,7 +35,6 @@ #include <assert.h> #include "types.h" -#define B64_BUF 16384 #define FAIL -1 #define SKIP -2 diff --git a/src/base64.c b/src/base64.c index e01c0f5..09098db 100644 --- a/src/base64.c +++ b/src/base64.c @@ -85,11 +85,9 @@ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL }; /* Returns NULL on error */ -/* FIXME Possible buffer overflow on outputs larger than B64_BUF */ char* encode_base64(byte* src,size_t ssize) { char* outbuf; - char* retbuf; int pos; int i, l, left; unsigned long triple; @@ -100,7 +98,10 @@ char* encode_base64(byte* src,size_t ssize) log_msg(LOG_LEVEL_DEBUG,"encode base64: empty string"); return NULL; } - outbuf = (char *)checked_malloc(sizeof(char)*B64_BUF); + + /* length of encoded base64 string (padded) */ + size_t length = sizeof(char)* ((ssize + 2) / 3) * 4; + outbuf = (char *)checked_malloc(length + 1); /* Initialize working pointers */ inb = src; @@ -161,20 +162,14 @@ char* encode_base64(byte* src,size_t ssize) inb++; } - /* outbuf is not completely used so we use retbuf */ - retbuf=(char*)checked_malloc(sizeof(char)*(pos+1)); - memcpy(retbuf,outbuf,pos); - retbuf[pos]='\0'; - free(outbuf); + outbuf[pos]='\0'; - return retbuf; + return outbuf; } -/* FIXME Possible buffer overflow on outputs larger than B64_BUF */ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) { byte* outbuf; - byte* retbuf; char* inb; int i; int l; @@ -188,10 +183,18 @@ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) return NULL; } + /* exit on unpadded input */ + if (ssize % 4) { + log_msg(LOG_LEVEL_WARNING, "decode_base64: '%s' has invalid length (missing padding characters?)", src); + return NULL; + } + + /* calculate length of decoded string, substract padding chars if any (ssize is >= 4) */ + size_t length = sizeof(byte) * ((ssize / 4) * 3)- (src[ssize-1] == '=') - (src[ssize-2] == '='); /* Initialize working pointers */ inb = src; - outbuf = (byte *)checked_malloc(sizeof(byte)*B64_BUF); + outbuf = (byte *)checked_malloc(length + 1); l = 0; triple = 0; @@ -242,15 +245,11 @@ byte* decode_base64(char* src,size_t ssize, size_t *ret_len) inb++; } - retbuf=(byte*)checked_malloc(sizeof(byte)*(pos+1)); - memcpy(retbuf,outbuf,pos); - retbuf[pos]='\0'; - - free(outbuf); + outbuf[pos]='\0'; if (ret_len) *ret_len = pos; - return retbuf; + return outbuf; } size_t length_base64(char* src,size_t ssize) diff --git a/src/db.c b/src/db.c index d8b23a2..ac55f0a 100644 --- a/src/db.c +++ b/src/db.c @@ -428,13 +428,15 @@ db_line* db_char2line(char** ss, database* db){ time_t base64totime_t(char* s, database* db, const char* field_name){ + if(strcmp(s,"0")==0){ + return 0; + } byte* b=decode_base64(s,strlen(s),NULL); char* endp; - if (b==NULL||strcmp(s,"0")==0) { + if (b==NULL) { /* Should we print error here? */ - free(b); return 0; } else {
signature.asc
Description: PGP signature
_______________________________________________ Aide mailing list Aide@ipi.fi https://www.ipi.fi/mailman/listinfo/aide