From fa6bd3f901f7db831972cf35eec7f7c86c3bba9f Mon Sep 17 00:00:00 2001 From: nnposter Date: Sun, 13 Sep 2020 00:12:48 +0000 Subject: [PATCH] Update the SSH protocol flow. Closes #1460 Allows the server to start the key exchange before the protocol version exchange (banner exchange) is completed --- CHANGELOG | 4 ++++ scripts/ssh2-enum-algos.nse | 27 ++++++++++----------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index faad4ebb3..2619d5ce8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,10 @@ o [GH#2104] Fixed parsing of TCP options which would hang (infinite loop) if an option had an explicit length of 0. Affects Nmap 7.80 only. [Daniel Miller, Imed Mnif] +o [NSE][GH#1460] Script ssh2-enum-algos would fail if the server initiated + the key exchange before completing the protocol version exchange + [Scott Ellis, nnposter] + o [NSE][GH#2105] Fetching of SSH2 keys might fail because of key exchange confusion [nnposter] diff --git a/scripts/ssh2-enum-algos.nse b/scripts/ssh2-enum-algos.nse index 8b109d668..fda9f0e96 100644 --- a/scripts/ssh2-enum-algos.nse +++ b/scripts/ssh2-enum-algos.nse @@ -160,18 +160,20 @@ end action = function(host, port) local sock = nmap.new_socket() local status = sock:connect(host, port) - if not status then return end - status = sock:receive_lines(1) + -- send the client banner + -- NB: The protocol does not prescribe which side sends the banner first + status = sock:send("SSH-2.0-Nmap_SSH2_Enum_Algos\r\n") if not status then sock:close() return end - status = sock:send("SSH-2.0-Nmap_SSH2_Enum_Algos\r\n") + -- slurp the server banner + status = sock:receive_buf("\r?\n", false) if not status then sock:close() return @@ -179,26 +181,17 @@ action = function(host, port) local ssh = ssh2.transport - -- I would think that the server would send its kex data right after - -- receiving and verifying our protocol id string above, then we could - -- just use it here, but I've seen no definitive documentation saying - -- that we don't ever send ours first. All I've seen is that if the - -- server doesn't care about compatibility with older clients then it - -- MAY send its kex data after the protocol id string. So I guess I'll - -- send it here until I know for sure (removing this send works against - -- OpenSSH though). - local pkt = ssh.build(ssh.kex_init()) - - status = sock:send(pkt) + -- send the client key exchange + -- NB: The protocol does not prescribe which side sends the kex init first + status = sock:send(ssh.build(ssh.kex_init())) if not status then sock:close() return end - local status, response = ssh.receive_packet(sock) - + local response + status, response = ssh.receive_packet(sock) sock:close() - if not status then return end