diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index f6377b9f..1c459672 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -1096,6 +1096,14 @@ class SSH2 */ var $smartMFA = true; + /** + * Extra packets counter + * + * @var bool + * @access private + */ + var $extra_packets; + /** * Default Constructor. * @@ -1502,7 +1510,7 @@ class SSH2 $preferred['client_to_server']['comp'] : $this->getSupportedCompressionAlgorithms(); - $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c')); + $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c', 'kex-strict-c-v00@openssh.com')); // some SSH servers have buggy implementations of some of the above algorithms switch (true) { @@ -1566,6 +1574,7 @@ class SSH2 return false; } + $this->extra_packets = 0; $kexinit_payload_server = $this->_get_binary_packet(); if ($kexinit_payload_server === false) { $this->bitmap = 0; @@ -1590,6 +1599,12 @@ class SSH2 } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { + if ($this->session_id === false && $this->extra_packets) { + user_error('Possible Terrapin Attack detected'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + } if (strlen($response) < 4) { return false; @@ -1996,6 +2011,10 @@ class SSH2 return false; } + if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { + $this->get_seq_no = $this->send_seq_no = 0; + } + $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); @@ -3766,9 +3785,11 @@ class SSH2 $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: + $this->extra_packets++; $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_DEBUG: + $this->extra_packets++; $this->_string_shift($payload, 2); if (strlen($payload) < 4) { return false; @@ -3780,6 +3801,7 @@ class SSH2 case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: + // this is here for key re-exchanges after the initial key exchange if ($this->session_id !== false) { $this->send_kex_first = false; if (!$this->_key_exchange($payload)) {