Headline
CVE-2023-43660: fixed GHSA-3cjp-w4cp-m9c8 - interpreting SSH public key offers as a s… · warp-tech/warpgate@a4df7f7
Warpgate is a smart SSH, HTTPS and MySQL bastion host for Linux that doesn’t need special client apps. The SSH key verification for a user can be bypassed by sending an SSH key offer without a signature. This allows bypassing authentication under following conditions: 1. The attacker knows the username and a valid target name 2. The attacked knows the user’s public key and 3. Only SSH public key authentication is required for the user account. This issue has been addressed in version 0.8.1. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Expand Up @@ -466,6 +466,10 @@ impl ServerSession { let _ = reply.send(self._auth_publickey(username, key).await); }
ServerHandlerEvent::AuthPublicKeyOffer(username, key, reply) => { let _ = reply.send(self._auth_publickey_offer(username, key).await); }
ServerHandlerEvent::AuthPassword(username, password, reply) => { let _ = reply.send(self._auth_password(username, password).await); } Expand Down Expand Up @@ -1149,19 +1153,7 @@ impl ServerSession { .map_err(anyhow::Error::from) }
async fn _auth_publickey( &mut self, ssh_username: Secret<String>, key: PublicKey, ) -> russh::server::Auth { let selector: AuthSelector = ssh_username.expose_secret().into();
info!( "Public key auth as {:?} with key {}", selector, key.public_key_base64() );
fn _get_public_keys_from_of(&self, key: PublicKey) -> Vec<PublicKey> { let mut keys = vec![key.clone()]; // Try all supported hash algorithms if let PublicKey::RSA { key, hash } = &key { Expand All @@ -1178,6 +1170,48 @@ impl ServerSession { } } } keys }
async fn _auth_publickey_offer( &mut self, ssh_username: Secret<String>, key: PublicKey, ) -> bool { let keys = self._get_public_keys_from_of(key); let selector: AuthSelector = ssh_username.expose_secret().into();
for key in keys { if let Ok(true) = self .try_validate_public_key_offer( &selector, Some(AuthCredential::PublicKey { kind: key.name().to_string(), public_key_bytes: Bytes::from(key.public_key_bytes()), }), ) .await { return true; } } false }
async fn _auth_publickey( &mut self, ssh_username: Secret<String>, key: PublicKey, ) -> russh::server::Auth { let selector: AuthSelector = ssh_username.expose_secret().into();
info!( "Public key auth as {:?} with key {}", selector, key.public_key_base64() );
let keys = self._get_public_keys_from_of(key);
let mut result = Ok(AuthResult::Rejected); for key in keys { Expand Down Expand Up @@ -1361,6 +1395,29 @@ impl ServerSession { m }
async fn try_validate_public_key_offer( &mut self, selector: &AuthSelector, credential: Option<AuthCredential>, ) -> Result<bool> { match selector { AuthSelector::User { username, … } => { let cp = self.services.config_provider.clone();
if let Some(credential) = credential { return Ok(cp .lock() .await .validate_credential(username, &credential) .await?); }
Ok(false) } _ => Ok(false), } }
async fn try_auth( &mut self, selector: &AuthSelector, Expand Down