{"uuid": "1e8b422c-2f83-4686-9be1-51b6c4c798f3", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2023-48795", "type": "seen", "source": "https://gist.github.com/lzap/8023a876d991961149b80a238ff31944", "content": "#!/usr/bin/env ruby\n# Reproducer: net-ssh 7.3.3 vs RHEL10 (OpenSSH 9.9)\n#\n# Run with: bundle exec ruby rhel10_ssh_reproducer.rb [host] [user]\n\nrequire \"bundler/setup\"\nrequire \"net/ssh\"\n\nHOST = ARGV[0] || \"192.168.122.73\"\nUSER = ARGV[1] || \"lzap\"\nRSA_KEY = File.expand_path(\"~/.ssh/id_rsa\")\nED25519_KEY = File.expand_path(\"~/.ssh/id_ed25519\")\n\npassed = 0\nfailed = 0\n\ndef test(name)\n  print \"  #{name}... \"\n  begin\n    result = yield\n    puts \"PASS \u2014 #{result}\"\n    return true\n  rescue Exception =&gt; e\n    puts \"FAIL \u2014 #{e.class}: #{e.message}\"\n    return false\n  end\nend\n\nputs \"=\" * 70\nputs \"net-ssh #{Net::SSH::Version::STRING} vs RHEL10 compatibility report\"\nputs \"Target: #{USER}@#{HOST}\"\nputs \"=\" * 70\n\n# -----------------------------------------------------------------------\n# 1. Algorithm support audit\n# -----------------------------------------------------------------------\nputs \"\\n## 1. Algorithm support in net-ssh #{Net::SSH::Version::STRING}\\n\\n\"\n\nkex_algos = Net::SSH::Transport::Algorithms::ALGORITHMS[:kex]\nhk_algos  = Net::SSH::Transport::Algorithms::ALGORITHMS[:host_key]\nenc_algos = Net::SSH::Transport::Algorithms::ALGORITHMS[:encryption]\n\nrhel10_kex = %w[\n  mlkem768x25519-sha256 curve25519-sha256 curve25519-sha256@libssh.org\n  ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521\n  diffie-hellman-group-exchange-sha256 diffie-hellman-group14-sha256\n  diffie-hellman-group16-sha512 diffie-hellman-group18-sha512\n]\n\nrhel10_host_key = %w[\n  ecdsa-sha2-nistp256 sk-ecdsa-sha2-nistp256@openssh.com\n  ecdsa-sha2-nistp384 ecdsa-sha2-nistp521\n  ssh-ed25519 sk-ssh-ed25519@openssh.com\n  rsa-sha2-256 rsa-sha2-512\n]\n\nrhel10_ciphers = %w[\n  aes256-gcm@openssh.com chacha20-poly1305@openssh.com\n  aes256-ctr aes128-gcm@openssh.com aes128-ctr\n]\n\nputs \"KEX algorithms (RHEL10 DEFAULT vs net-ssh):\"\nrhel10_kex.each do |a|\n  supported = kex_algos.include?(a)\n  puts \"  #{supported ? '[OK]' : '[!!]'} #{a}\"\nend\n\nkex_overlap = rhel10_kex &amp; kex_algos\nputs \"  Overlap: #{kex_overlap.length}/#{rhel10_kex.length} \u2014 #{kex_overlap.join(', ')}\"\n\nputs \"\\nHost key algorithms (RHEL10 DEFAULT vs net-ssh):\"\nrhel10_host_key.each do |a|\n  supported = hk_algos.include?(a)\n  puts \"  #{supported ? '[OK]' : '[!!]'} #{a}\"\nend\n\nhk_overlap = rhel10_host_key &amp; hk_algos\nputs \"  Overlap: #{hk_overlap.length}/#{rhel10_host_key.length} \u2014 #{hk_overlap.join(', ')}\"\n\nputs \"\\nCiphers (RHEL10 DEFAULT vs net-ssh):\"\nrhel10_ciphers.each do |a|\n  supported = enc_algos.include?(a)\n  puts \"  #{supported ? '[OK]' : '[!!]'} #{a}\"\nend\n\nenc_overlap = rhel10_ciphers &amp; enc_algos\nputs \"  Overlap: #{enc_overlap.length}/#{rhel10_ciphers.length} \u2014 #{enc_overlap.join(', ')}\"\n\ned25519_gem = begin; require \"ed25519\"; true; rescue LoadError; false; end\nbcrypt_gem = begin; require \"bcrypt_pbkdf\"; true; rescue LoadError; false; end\nputs \"\\nOptional gems: ed25519=#{ed25519_gem} bcrypt_pbkdf=#{bcrypt_gem}\"\n\n# -----------------------------------------------------------------------\n# 2. Connection tests\n# -----------------------------------------------------------------------\nputs \"\\n## 2. Connection tests against #{HOST}\\n\"\n\nputs \"\\n### 2a. RSA key authentication\"\nr = test(\"RSA key (keys_only, config disabled)\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    keys: [RSA_KEY], keys_only: true, verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2b. ED25519 key authentication\"\nr = test(\"ED25519 key (keys_only, config disabled)\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    keys: [ED25519_KEY], keys_only: true, verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2c. Default key discovery (auto \u2014 loads all keys from ~/.ssh/)\"\nr = test(\"Default key discovery\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2d. Foreman-style connection (simulated key_data with RSA)\"\nr = test(\"Foreman-style with RSA key_data\") do\n  key_data = File.read(RSA_KEY)\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    key_data: [key_data], keys_only: true, keys: [],\n    auth_methods: [\"publickey\"], verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2e. Foreman-style connection (simulated key_data with ED25519)\"\nr = test(\"Foreman-style with ED25519 key_data\") do\n  key_data = File.read(ED25519_KEY)\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    key_data: [key_data], keys_only: true, keys: [],\n    auth_methods: [\"publickey\"], verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2f. Host key verification (ed25519 host key only)\"\nr = test(\"Force ssh-ed25519 host key negotiation\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    keys: [RSA_KEY], keys_only: true,\n    host_key: \"ssh-ed25519\", verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2g. Force curve25519 KEX\"\nr = test(\"Force curve25519-sha256 KEX\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    keys: [RSA_KEY], keys_only: true,\n    kex: \"curve25519-sha256\", verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2h. Force chacha20-poly1305 cipher\"\nr = test(\"Force chacha20-poly1305@openssh.com cipher\") do\n  Net::SSH.start(HOST, USER, timeout: 10, non_interactive: true, config: false,\n    keys: [RSA_KEY], keys_only: true,\n    encryption: \"chacha20-poly1305@openssh.com\", verify_host_key: :never) do |ssh|\n    ssh.exec!(\"cat /etc/redhat-release\").strip\n  end\nend\npassed += 1 if r; failed += 1 unless r\n\nputs \"\\n### 2i. Fog::SSH (as used by Foreman)\"\nbegin\n  require \"fog/core\"\n  r = test(\"Fog::SSH with RSA key\") do\n    conn = Fog::SSH.new(HOST, USER,\n      keys: [RSA_KEY], keys_only: true,\n      config: false, timeout: 10, verify_host_key: :never)\n    results = conn.run(\"cat /etc/redhat-release\")\n    results.first.stdout.strip\n  end\n  passed += 1 if r; failed += 1 unless r\nrescue LoadError\n  puts \"  Fog::SSH \u2014 SKIP (fog-core not available)\"\nend\n\n# -----------------------------------------------------------------------\n# 3. Summary\n# -----------------------------------------------------------------------\nputs \"\\n\" + \"=\" * 70\nputs \"RESULTS: #{passed} passed, #{failed} failed\"\nputs \"=\" * 70\nputs \"\\n## Issues found:\\n\"\n\nissues = []\n\nunless kex_algos.any? { |a| a.include?(\"curve25519\") }\n  issues &lt;&lt; \"MISSING KEX: curve25519-sha256 \u2014 RHEL10's 2nd-preference KEX algorithm.\\n\" \\\n            \"  net-ssh falls back to ecdh-sha2-nistp521, but this means no overlap\\n\" \\\n            \"  if RHEL10 restricts to curve25519/mlkem-only KEX.\"\nend\n\nunless kex_algos.any? { |a| a.include?(\"mlkem\") }\n  issues &lt;&lt; \"MISSING KEX: mlkem768x25519-sha256 \u2014 RHEL10's top-preference post-quantum\\n\" \\\n            \"  KEX algorithm. Not critical yet but will become mandatory.\"\nend\n\nunless hk_algos.include?(\"ssh-ed25519\")\n  issues &lt;&lt; \"MISSING HOST KEY: ssh-ed25519 \u2014 If a RHEL10 host has ONLY an ed25519\\n\" \\\n            \"  host key (no ecdsa/rsa), net-ssh cannot negotiate a host key algorithm\\n\" \\\n            \"  and the connection fails entirely.\"\nend\n\nunless ed25519_gem\n  issues &lt;&lt; \"MISSING GEM: ed25519 \u2014 Required for ed25519 client key authentication\\n\" \\\n            \"  and ssh-ed25519 host key verification. Not in Foreman's Gemfile.\\n\" \\\n            \"  Fix: Add 'ed25519' and 'bcrypt_pbkdf' to Gemfile.\"\nend\n\nunless enc_algos.include?(\"chacha20-poly1305@openssh.com\")\n  issues &lt;&lt; \"MISSING CIPHER: chacha20-poly1305@openssh.com \u2014 RHEL10's preferred cipher.\\n\" \\\n            \"  net-ssh falls back to aes256-gcm/aes256-ctr.\"\nend\n\nissues &lt;&lt; \"MISSING STRICT KEX: net-ssh #{Net::SSH::Version::STRING} does not implement\\n\" \\\n          \"  the strict KEX extension (kex-strict-*-v00@openssh.com) for CVE-2023-48795\\n\" \\\n          \"  (Terrapin). Connection works but is vulnerable to prefix truncation attacks.\"\n\nif issues.empty?\n  puts \"  None \u2014 all algorithms supported.\"\nelse\n  issues.each_with_index { |issue, i| puts \"#{i + 1}. #{issue}\\n\" }\nend\n", "creation_timestamp": "2026-08-17T16:21:49.192111Z"}