Multiple Systemd Vulnerabilities

Qualys has disclosed 3 vulnerabilities in systemd-journald, it has been named “System Down: A systemd-journald exploit” . systemd-journald is a system service that is responsible for collecting and storing logging data. It receives data from various sources like Kernel log messages, system log messages, Structured system log messages, Audit records etc

The issue affects most systemd-based Linux distributions. SUSE Linux Enterprise 15, openSUSE Leap 15.0, and Fedora 28 and 29 are not vulnerable as they are compiled with stack clash protection.

CVE-2018-16864
systemd-journald accepts logs from processes using syslog(). It does event logging with append-only logfiles that are actually binary files. A large argument can cause journald to crash due to segmentation fault. The issue occurs due to a memory allocation using alloca() based on user controlled input. In the code snippet below we can see alloca(_len_ + 1) where _len_ is total length of strings in array _appendees_. When alloca() is called it allocates space within the stack frame of the caller.

919 #define strjoina(a, ...)                                                \
920         ({                                                              \
921                 const char *_appendees_[] = { a, __VA_ARGS__ };         \
922                 char *_d_, *_p_;                                        \
923                 int _len_ = 0;                                          \
924                 unsigned _i_;                                           \
925                 for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
926                         _len_ += strlen(_appendees_[_i_]);              \
927                 _p_ = _d_ = alloca(_len_ + 1);                          \
928                 for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
929                         _p_ = stpcpy(_p_, _appendees_[_i_]);            \
930                 *_p_ = 0;                                               \
931                 _d_;                                                    \
932         })

The alloca() operation in disassembly is sub %rax,%rsp, rsp being the current top of the stack. This type of allocation is similar for local variables and function arguments . To exploit this vulnerability, the goal is to manipulate the _appendees_ array to allocate a large enough memory, such that the strcpy() operation will clash (stack clash) with read-only or unmapped memory regions (wild copy).

CVE-2018-16865
This issue occurs due an attacker controlled allocation in journal_file_append_entry(). By sending a large native message to /run/systemd/journal/socket, We can cause the function to allocate 4GB (items). This is enough to jump over the stack guards and into mmap regions. To exploit this vulnerability we need to overcome 3 hurdles.

– If variable items is too large it may overwrite some essential variables in libc that may cause journald to crash or deadlock.
– In the code snippet below after alloca() the for loop is responsible for writing in to the allocated ‘items’. These are 64-bit hash values, we need a suitable value in data such that its hash is a valid function pointer of our choosing and it satisfies valid_user_field().

1963 int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) {
....
1986         items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec));
1987
1988         for (i = 0; i < n_iovec; i++) {
1989                 uint64_t p;
1990                 Object *o;
1991
1992                 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1993                 if (r < 0) 1994 return r; 1995 1996 xor_hash ^= le64toh(o->data.hash);
1997                 items[i].object_offset = htole64(p);
1998                 items[i].hash = o->data.hash;
1999         }

– We need journald’s stack pointer for overwriting libc’s function pointer. This is where CVE-2018-16866 comes in to the picture.

CVE-2018-16866
It is an out-bounds read vulnerability in journald. This vulnerability can be exploited as an information leak. Function syslog_parse_identifier() parses a syslog message to check for ‘:‘. If a syslog message ends with ‘:‘, strchr(WHITESPACE, p[e]) returns a pointer to ‘\0‘. This pointer is incremented and points to the first character stored after ‘\0‘, which is an out-bounds read. This value can be retrieved from the journal.

Mitigation
We request organizations to apply the latest patches as they are released by the respective vendors. Qualys will release detections for these vulnerabilities as soon as possible.

References
[oss-security] Qualys Security Advisory
Qualys Security Advisory
SYSTEMD-JOURNALD.SERVICE
alloca()

Leave a Reply

Your email address will not be published. Required fields are marked *