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 {

Attachment: signature.asc
Description: PGP signature

_______________________________________________
Aide mailing list
Aide@ipi.fi
https://www.ipi.fi/mailman/listinfo/aide

Reply via email to