Skip to main content

Jixun's Blog 填坑还是开坑,这是个好问题。

Binary patch for OpenSSH CVE-2024-6387 (EL9)

An OpenSSH vulnerability CVE-2024-6387 has been disclosed, where a rare race condition can give an attacker root access to the server.

In RHEL and its derivative distributions, only EL9 was exploitable.

One of the mitigation was to configure LoginGraceTime to 0 which may cause server to refuse connections in circumstances, or wait for a new patched release. I can’t wait, so I made myself a binary patch while waiting for it.

Right now AlmaLinux has provided update to the package. However, for Rocky Linux, you need to install their Security SIG to have this update (yet).

Binary Patch

From the public technical details, all we need was to remove the sshlogv call from the function sshsigdie:

Because this fix is part of a large commit (81c1099), on top of an even larger defense-in-depth commit (03e3de4, “Start the process of splitting sshd into separate binaries”), it might prove difficult to backport. In that case, the signal handler race condition itself can be fixed by removing or commenting out the async-signal-unsafe code from the sshsigdie() function; …

We locate it by open the sshd binary file using Ghidra, then look for symbol sshsigdie:

Find the target function via Ghidra

Once we’ve worked out the byte pattern and locate its offset, we can create a script to automate the patching.

This patch script was written for Python 3.

※ Alma Linux already provides a security patch. The binary patch script is not recommended and is for archival & study purpose only.

  1. Save the script as patch.py:
#!/usr/bin/python3

from hashlib import sha256
import sys
import os

print('''
openssh-server-8.7p1-38.el9 bin-patch for Alma/Rocky EL9 (x86-64 & aarch64)

Usage:

  {} </path/to/sshd>

by Jixun <https://jixun.uk/>

'''.format(sys.argv[0]))

patches = [
    dict(
        name='AlmaLinux: openssh-server-8.7p1-38.el9.x86_64',
        before='2526374c30da03dea655c0c5835882a7953d7179f3acf598a7de516a70e6b2d1',
        after='d30cdcdd04ae5f60d295bdc07b6f8e1ce6e21321f48df4cd62c8a3b45883325b',
        offset=0x81EF6,
        payload=b'\x90\x90\x90\x90\x90',
    ),
    dict(
        name='AlmaLinux: openssh-server-8.7p1-38.el9.aarch64',
        before='09130656a2dd53e19fdeb28b39aaef489191f7ae4c8f3e1b90a7a60f02bf143d',
        after='8de58238fbef8cd5f879476a3690f28a8f7d41bb2f26cb89d6ddccc972ffcf79',
        offset=0x7AD20,
        payload=b'\x1F\x20\x03\xD5'
    ),
    dict(
        name='RockyLinux: openssh-server-8.7p1-38.el9.x86_64',
        before='6bfc9552d9f976e5cbe01256ba43737cfdc6c9109ebb93d3ee4942a3669408c0',
        after='1bcb71692078d7c95488e2bad3c1b0b0f525c178f033d1c875dc872ad554aef8',
        offset=0x81EF6,
        payload=b'\x90\x90\x90\x90\x90',
    ),
    dict(
        name='RockyLinux: openssh-server-8.7p1-38.el9.aarch64',
        before='e869b20162cead2be4d83f1735f9ed119cf9c5e79e0c3e62e62c8200dca19b39',
        after='caebae2953d438c1c9c7c631c1baa4006ef4d7efaf2f0341d8b4c5cf73964f1f',
        offset=0x7AD20,
        payload=b'\x1F\x20\x03\xD5'
    ),
]

file = sys.argv[1]
with open(file, 'rb') as f:
    sshd_bin = f.read()
sshd_hash = sha256(sshd_bin).hexdigest()

for patch in patches:
    print('[*] Checking {}...'.format(patch['name']))
    if sshd_hash == patch['after']:
        print('    Already patched!')
        continue
    if sshd_hash != patch['before']:
        print('    No match')
        continue

    print("    validating patch...")
    offset = patch['offset']
    payload = patch['payload']
    payload_len = len(payload)
    patched_bin = bytearray(sshd_bin)
    patched_bin[offset:offset+payload_len] = payload
    patched_hash = sha256(patched_bin).hexdigest()
    if patched_hash != patch['after']:
        print('FATAL: hash does not match (got {}, expected {})'.format(
            patched_hash, patch['after']))
        sys.exit(1)

    print("    backup...")
    os.replace(file, file + '.bak')

    print("    writing...")
    with open(file, 'wb', opener=lambda path, flags: os.open(path, flags, mode=0o755)) as f:
        f.write(patched_bin)

    print("    ok, remember to restart sshd service!")
  1. Run the patch script: sudo python3 patch.py /usr/sbin/sshd
  2. And finally, restart ssh server: sudo systemctl restart sshd

Sources:

Apply update provided by distribution

Force refresh cache with dnf makecache, and then update it:

sudo dnf makecache
sudo dnf update

If we verify the version:

rpm -qa openssh-server

It should report openssh-server-8.7p1-38.el9.alma.2.your_arch (Alma Linux) or openssh-server-8.7p1-38.el9_4.security.0.5.your_arch (Rocky Linux)

My rants

Alma is on the slower side to release security patches…

Though it’s still not bad to have it fixed within a day of disclosure (and it was a Monday!). RedHat seems to have only provided a mitigation yet.

Creative Commons Licence This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Comments