use crate::Result;
use crate::utils::network::Target;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
pub struct BeastTester {
target: Target,
}
impl BeastTester {
pub fn new(target: Target) -> Self {
Self { target }
}
pub async fn test(&self) -> Result<BeastTestResult> {
let tls10_cbc = self.test_tls10_cbc().await?;
let ssl3_cbc = self.test_ssl3_cbc().await?;
let vulnerable = tls10_cbc || ssl3_cbc;
let details = if vulnerable {
let mut parts = Vec::new();
if tls10_cbc {
parts.push("TLS 1.0 with CBC ciphers enabled");
}
if ssl3_cbc {
parts.push("SSL 3.0 with CBC ciphers enabled");
}
format!("Vulnerable: {}", parts.join(", "))
} else {
"Not vulnerable - TLS 1.0/SSL 3.0 CBC ciphers not supported".to_string()
};
Ok(BeastTestResult {
vulnerable,
tls10_cbc_supported: tls10_cbc,
ssl3_cbc_supported: ssl3_cbc,
details,
})
}
async fn test_tls10_cbc(&self) -> Result<bool> {
use openssl::ssl::{SslConnector, SslMethod, SslVersion};
let addr = self.target.socket_addrs()[0];
match timeout(Duration::from_secs(5), TcpStream::connect(addr)).await {
Ok(Ok(stream)) => {
let std_stream = stream.into_std()?;
std_stream.set_nonblocking(false)?;
let mut builder = SslConnector::builder(SslMethod::tls())?;
builder.set_min_proto_version(Some(SslVersion::TLS1))?;
builder.set_max_proto_version(Some(SslVersion::TLS1))?;
builder.set_cipher_list("AES128-SHA:AES256-SHA:DES-CBC3-SHA")?;
let connector = builder.build();
match connector.connect(&self.target.hostname, std_stream) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
_ => Ok(false),
}
}
async fn test_ssl3_cbc(&self) -> Result<bool> {
use openssl::ssl::{SslConnector, SslMethod, SslVersion};
let addr = self.target.socket_addrs()[0];
match timeout(Duration::from_secs(5), TcpStream::connect(addr)).await {
Ok(Ok(stream)) => {
let std_stream = stream.into_std()?;
std_stream.set_nonblocking(false)?;
let mut builder = SslConnector::builder(SslMethod::tls())?;
builder.set_min_proto_version(Some(SslVersion::SSL3))?;
builder.set_max_proto_version(Some(SslVersion::SSL3))?;
builder.set_cipher_list("AES128-SHA:AES256-SHA:DES-CBC3-SHA")?;
let connector = builder.build();
match connector.connect(&self.target.hostname, std_stream) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
_ => Ok(false),
}
}
}
#[derive(Debug, Clone)]
pub struct BeastTestResult {
pub vulnerable: bool,
pub tls10_cbc_supported: bool,
pub ssl3_cbc_supported: bool,
pub details: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_beast_result_creation() {
let result = BeastTestResult {
vulnerable: true,
tls10_cbc_supported: true,
ssl3_cbc_supported: false,
details: "Test".to_string(),
};
assert!(result.vulnerable);
assert!(result.tls10_cbc_supported);
}
}