Inventory and mitigate OpenSSH vulnerability CVE-2024-6387

Posted by Craig Comstock
August 5, 2024

The rather serious recent OpenSSH vulnerability CVE-2024-6387 could affect as many as 14 million server instances exposed on the internet. Let’s make it easy to examine your infrastructure and see if you need to do any upgrades or mitigations.

On the back of my CFEngine T-shirt it says:

Know more, React faster

When I have a problem to solve in CFEngine I look for an easy and correct solution. CFEngine Build is a good first place to look. Two modules stand out as possibly useful;

  1. We can use inventory-openssl-versions as a template to inventory OpenSSH version. This is the “Know more” step.

  2. For the “React faster” we may need to change something. In case we can’t upgrade OpenSSH we will need to mitigate the vulnerability by modifying sshd config. For this purpose we can leverage library-sshd-config. This module has the advantage of preparing a staged configuration as well as restarting the service in case of a change.

Adding modules

Previously we have used the Build app in Mission Portal. This time let’s use the cfbs command line tool.

command
cfbs add inventory-openssl-versions
output
Added module: inventory-openssl-versions
The default commit message is 'Added module 'inventory-openssl-versions'' - edit it? [yes/y/NO/n]
Committing using git:

[home 5964af0] Added module 'inventory-openssl-versions'
 1 file changed, 15 insertions(+)
command
cfbs add library-sshd-config
output
Added module: library-sshd-config
The default commit message is 'Added module 'library-sshd-config'' - edit it? [yes/y/NO/n]
Committing using git:

[home 0da07db] Added module 'library-sshd-config'
 1 file changed, 15 insertions(+)

So let’s “copy-paste-modify” the policy from inventory-openssl-versions to inventory OpenSSH aka sshd version.

OpenSSH version information in inventory

For simplicity’s sake we will rely only on the sshd command only instead of also checking package managers.

body file control
{
  namespace => "inventory_sshd_version";
}

bundle agent inventory_sshd_version
{
  vars:
    !windows::
      "_sshd_path"
        string => "/usr/sbin/sshd";

  classes:
    linux::
      "sshd_binary_works"
        if => returnszero("${_sshd_path} -t 1>/dev/null 2>&1", "useshell");
        # -t option is Test mode, should return zero

  vars:
    sshd_binary_works::
      "sshd_output"
        string =>
          nth(
            splitstring(
              execresult("${_sshd_path} -nosuchargument", "useshell"),
              # Sadly sshd has no -V|--version so instead we use
              # -nosuchargument which causes sshd to output a full version
              # like OpenSSH_8.5p1
              "\\n",
              3),
          1);

  classes:
    sshd_binary_works::
      "found_sshd_version"
        expression => regextract(
          "^.*OpenSSH_([0-9\.p]+).*",
          "${sshd_output}",
          "captures"
        );

  vars:
    found_sshd_version::
      # OpenSSH versions are in the format (major).(minor)p(patch)
      # so we replace the "p" with "." to conform more to semantic
      # version standard used by version_compare() function.
      "sshd_version"
        string => string_replace(
          "${captures[1]}",
          "p",
          "."),
        meta => {"inventory", "attribute_name=OpenSSH version"};

  reports:
    DEBUG|DEBUG_INVENTORY_OPENSSH_VERSION::
      "OpenSSH version: $(sshd_version)";
}

After a few agent runs we can check inventory and see if we have any troubles.

inventory including OpenSSH version and OpenSSL versions

React faster

Two of my systems have an affected version! If a system was affected and I couldn’t upgrade the OpenSSH version, I would create policy to enact the mitigation procedure as recommended in the technical details:

Finally, if sshd cannot be updated or recompiled, this signal handler race condition can be fixed by simply setting LoginGraceTime to 0 in the configuration file. This makes sshd vulnerable to a denial of service (the exhaustion of all MaxStartups connections), but it makes it safe from the remote code execution presented in this advisory.

So let’s write some policy.

The first trick is to figure out if the the OpenSSH version is affected. Let’s express the logic of whether the version is OK in a simple expression:

(version >= 4.4p1 && version < 8.5p1) || version > 9.8p1

Thankfully, we have a helper in a new function available in our latest LTS, 3.24: version_compare(). With it, we can define a class sshd_version_ok using this policy:

  classes:
    "sshd_version_ok"
      expression =>
        or(
          and(
            version_compare("${sshd_version}", ">=", "4.4.1"),
            version_compare("${sshd_version}", "<", "8.5.1")
          ),
          version_compare("${sshd_version}", ">", "9.8.1")
        );

But this could cause sshd_version_ok to not be defined in case the sshd_version is not defined.

This is often an issue in CFEngine where negative knowledge can’t really be certain. We should rewrite this as “positive knowledge” aka something we determine explicitly.

  "sshd_version_cve"
    expression =>
      and(
        isvariable("sshd_version"),
        or(
          version_compare("${sshd_version}", "<", "4.4.1"),
          and(
            version_compare("${sshd_version}", ">=", "8.5.1"),
            version_compare("${sshd_version}", "<=", "9.8.1")
          )
        )
      );

And now we can write our mitigation policy:

bundle agent openssh_mitigation_CVE_2006_5051_CVE_2008_4109
{
  vars:
    "sshd_version"
      string => "${inventory_openssh_version:inventory_openssh_version.sshd_version}";

  classes:
    "sshd_version_cve"
      expression =>
        and(
          isvariable("sshd_version"),
          or(
            version_compare("${sshd_version}", "<", "4.4.1"),
            and(
              version_compare("${sshd_version}", ">=", "8.5.1"),
              version_compare("${sshd_version}", "<=", "9.8.1")
            )
          )
        );

  vars:
    "sshd_config[LoginGraceTime]"
      string => "0";

  methods:
    sshd_version_cve::
      "sshd config mitigation for CVE-2006-5051 and CVE-2008-4109"
        usebundle => lib_sshd_config:global_key_values(
          "$(this.namespace):$(this.bundle).sshd_config");
}

And with that, we know more: what versions of OpenSSH are present and have reacted faster: mitigated sshd config if needed.

TODO

  • Both inventory_openssh_version and openssh_mitigation_CVE_2006_5051_CVE_2008_4109 could be made into CFEngine Build modules.
  • We could make more generic modules to inventory various softwares and mitigate various vulnerabilities.

Questions?

If you have questions or need help, reach out on the mailing list or GitHub discussions. If you have a support contract, feel free to open a ticket in our support system.