Added the ability to write the host_notes from the nessus import, also

restructured ALOT of code into seperate files to make it easier to find
things.
This commit is contained in:
2026-09-03 17:01:52 -05:00
parent 9f4d0fccc4
commit 3903db6a69
8 changed files with 1232 additions and 1101 deletions
+163
View File
@@ -0,0 +1,163 @@
use keyring::Entry;
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
use std::error::Error;
use std::fs::OpenOptions;
use std::io::{BufReader, Read, Write};
use std::net::TcpStream;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::time::{self, Instant};
#[derive(Clone, Debug)]
pub struct Server {
pub address: String,
pub connected: bool,
pub timer: Duration,
pub config: Option<Arc<ClientConfig>>,
pub last_check: time::Instant,
pub message_que: Vec<String>,
pub action_que: Vec<String>,
pub client_id: usize,
pub selected_client: usize,
pub logged_in: bool,
pub password: String,
pub name: String,
pub cert_text: String,
}
impl Server {
pub fn donwload_certificate(&self) -> Result<String, Box<dyn Error>> {
let mut stream = TcpStream::connect(self.address.clone())?;
stream.write_all("CERT_REQ".as_bytes())?;
let mut buf = [0u8; 8192];
let bytes_read = stream.read(&mut buf)?;
let response = String::from_utf8_lossy(&buf[..bytes_read]);
if let Some((_, cert)) = response.split_once("|") {
return Ok(cert.to_string());
}
Err("Server did not send certificate".into())
}
pub fn connect(&mut self) -> Result<usize, Box<dyn Error>> {
if let Ok(cert) = self.donwload_certificate() {
self.config = Some(self.build_tls_config(cert.as_str())?);
self.cert_text = cert;
}
let mut stream = self.tls_connect()?;
stream.write("HELLO|attack".as_bytes())?;
let mut buf = [0; 8192];
let bytes_read = stream.read(&mut buf)?;
let response = String::from_utf8_lossy(&buf[..bytes_read]);
if let Some((_, id)) = response.split_once("|") {
self.client_id = id.trim().parse()?;
}
self.connected = true;
self.last_check = Instant::now();
let entry = Entry::new("tetanus", &self.address)?;
if let Ok(password) = entry.get_password() {
self.login(password)?;
}
return Ok(self.client_id);
}
pub fn login(&mut self, pass: String) -> Result<(), Box<dyn Error>> {
let mut stream = self.tls_connect()?;
stream.write(format!("NONE**{}|||LOGIN|{}||", self.client_id, pass).as_bytes())?;
let mut buf = [0; 8192];
let bytes_read = stream.read(&mut buf)?;
let response = String::from_utf8_lossy(&buf[..bytes_read]);
if response.contains("successful") {
self.password = pass;
self.last_check = Instant::now();
self.logged_in = true;
self.message_que.push(format!("SET_NAME|{}", self.name));
return Ok(());
} else {
return Err("[error] logging in!".into());
}
}
pub fn checkin(&mut self) {
if self.logged_in {
if let Ok(mut stream) = self.tls_connect() {
self.last_check = Instant::now();
let payload = format!(
"{}**{}|||{}\n",
self.password.clone(),
self.client_id.clone(),
self.message_que.join("||")
);
if let Err(e) = stream.write_all(payload.as_bytes()) {
self.action_que
.push(format!("Error|sending reponse output to server! {e}"));
}
self.message_que.clear();
let mut buffer = [0; 4096];
if let Ok(bytes_read) = stream.read(&mut buffer) {
if bytes_read > 0 {
let response = String::from_utf8_lossy(&buffer[..bytes_read]);
response.split("||").into_iter().for_each(|action| {
self.action_que.push(action.trim().to_string());
if action.contains("Not authenticated") {
self.logged_in = false;
}
});
}
}
}
}
}
pub fn build_tls_config(&self, pem: &str) -> Result<Arc<ClientConfig>, Box<dyn Error>> {
let mut roots = RootCertStore::empty();
let mut reader = BufReader::new(pem.as_bytes());
let certs = rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>()?;
for cert in certs {
roots.add(cert)?;
}
let config = ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth();
Ok(Arc::new(config))
}
fn tls_connect(&self) -> Result<StreamOwned<ClientConnection, TcpStream>, Box<dyn Error>> {
let config = self.config.clone().ok_or("TLS config not loaded")?;
let tcp = TcpStream::connect(&self.address)?;
let (host, _) = self.address.split_once(':').ok_or("Invalid address")?;
let server_name = ServerName::try_from(host.to_string())?;
let connection = ClientConnection::new(config, server_name)?;
let tls_stream = StreamOwned::new(connection, tcp);
Ok(tls_stream)
}
pub fn disconnect(&mut self) -> Result<(), Box<dyn Error>> {
let mut stream = self.tls_connect()?;
stream.write(format!("{}**{}|||DISCONNECT||", self.password, self.client_id).as_bytes())?;
self.connected = false;
Ok(())
}
pub fn save(&mut self, mut path: PathBuf) -> Result<(), Box<dyn Error>> {
path.push(format!("{}.conf", self.address));
let mut conf_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?;
let outdata = format!(
"
name: {}
address: {}
cert: {}
",
self.name, self.address, self.cert_text
);
write!(conf_file, "{}", outdata)?;
let entry = Entry::new("tetanus", &self.address)?;
entry.set_password(&self.password)?;
Ok(())
}
}
+197
View File
@@ -0,0 +1,197 @@
use crate::ShodanData;
use crate::ToolMessage;
use serde_json;
use shodan_rust::ShodanClient;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::net::TcpStream;
use std::path::PathBuf;
use std::sync::mpsc::Sender;
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct Host {
pub ip: String,
pub hostname: String,
pub open_ports: Vec<usize>,
pub control_port: usize,
pub users: Vec<User>,
pub pwned: bool,
pub id: usize,
pub findings: Vec<String>,
pub shodan_data: Option<ShodanData>,
}
impl Host {
pub fn new() -> Self {
Self {
ip: String::new(),
hostname: String::new(),
open_ports: Vec::new(),
control_port: 0,
users: Vec::new(),
pwned: false,
id: 0,
findings: Vec::new(),
shodan_data: None,
}
}
pub fn ip(&mut self, ip: String) {
self.ip = ip;
}
pub fn hostname(&mut self, name: String) {
self.hostname = name;
}
pub fn open_ports(&mut self, ports: Vec<usize>) {
self.open_ports = ports;
}
pub fn control_port(&mut self, port: usize) {
self.control_port = port;
}
pub fn users(&mut self, users: Vec<User>) {
self.users = users;
}
pub fn pwnd(&mut self) {
self.pwned = true;
}
pub fn id(&mut self, id: usize) {
self.id = id;
}
pub fn findings(&mut self, findings: Vec<String>) {
self.findings = findings;
}
pub fn add_port(&mut self, port: &str) {
if let Ok(port) = port.trim().parse() {
if !self.open_ports.contains(&port) && port != 0 {
self.open_ports.push(port);
}
}
}
pub fn add_user(&mut self, user: User) {
self.users.push(user);
}
pub fn add_finding(&mut self, finding: String) {
self.findings.push(finding);
}
pub fn save_config_file(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> {
let mut host_file = File::create(path)?;
let mut out_string = format!(
"
ip: {}
hostname: {}
control_port: {}
pwned: {}
id: {}",
self.ip, self.hostname, self.control_port, self.pwned, self.id
);
if !self.findings.is_empty() {
out_string.push_str(&format!("\nfindings: "));
out_string.push_str(&self.findings.join(","));
}
if !self.open_ports.is_empty() {
out_string.push_str("\nopen_ports: ");
out_string.push_str(
&self
.open_ports
.iter()
.map(|port| port.to_string())
.collect::<Vec<String>>()
.join(","),
);
}
write!(host_file, "{}", out_string)?;
host_file.sync_all()?;
Ok(())
}
pub fn check_port(&mut self, port: u16, tx: Sender<ToolMessage>) -> Result<(), Box<dyn Error>> {
if let Ok(ip) = self.ip.parse::<IpAddr>() {
let address = SocketAddr::new(ip, port);
if let Ok(_) = TcpStream::connect_timeout(&address, Duration::from_secs(3)) {
self.add_port(&format!("{}", port));
let _ = tx.send(ToolMessage::Output((
0,
format!("[success] {} is open on {}", port, self.ip),
)));
}
} else {
let _ = tx.send(ToolMessage::Output((
0,
format!("[error] {} is not a valid ip address!", self.ip),
)));
return Err("not valid IP".into());
}
return Ok(());
}
pub fn get_shodan(&mut self, key: String, tx: Sender<ToolMessage>) {
let client = ShodanClient::new(key);
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
if let Ok(data) = client.host_info(&self.ip).await {
if let Ok(new_data) = serde_json::from_str::<ShodanData>(&data.to_string()) {
if let Some(ports) = new_data.ports {
ports.iter().for_each(|p| {
self.add_port(&p.to_string());
});
}
if let Some(vulns) = new_data.vulns {
vulns.iter().for_each(|v| {
self.findings.push(format!("shodan: {}", v));
});
}
} else {
let _ = tx.send(ToolMessage::Output((
0,
"[error] parsing shodand data!".to_string(),
)));
}
} else {
let _ = tx.send(ToolMessage::Output((
0,
"[error] getting shodand data!".to_string(),
)));
}
});
let _ = tx.send(ToolMessage::Output((
0,
format!("shodan_function finished for {}", self.ip),
)));
}
}
#[derive(Clone, Debug)]
pub struct User {
pub name: String,
pub password: Option<String>,
pub hash: Option<String>,
pub ticket: Option<PathBuf>,
pub compromised: bool,
}
impl User {
pub fn new() -> Self {
Self {
name: String::new(),
password: None,
hash: None,
ticket: None,
compromised: false,
}
}
}
+140 -1051
View File
File diff suppressed because it is too large Load Diff
+241
View File
@@ -0,0 +1,241 @@
use crate::Project;
use crate::host::Host;
use crate::project::ToolCommand;
use ipnet::IpNet;
use rhai::{AST, Engine};
use std::collections::HashMap;
use std::fs::{read_dir, read_to_string};
use std::net::IpAddr;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
pub struct ModuleLoader {
pub engine: Arc<Engine>,
pub commands: HashMap<String, ToolCommand>,
pub asts: HashMap<String, AST>,
}
impl ModuleLoader {
pub fn new() -> Self {
let mut engine = Engine::new();
engine
.register_type_with_name::<PathBuf>("PathBuf")
.register_get_set(
"display",
|p: &mut PathBuf| p.to_string_lossy().into_owned(),
|p: &mut PathBuf, s: String| *p = PathBuf::from(s),
);
engine
.register_type_with_name::<Host>("Host")
.register_get_set(
"ip",
|h: &mut Host| h.ip.clone(),
|h: &mut Host, val: String| h.ip = val,
)
.register_get_set(
"hostname",
|h: &mut Host| h.hostname.clone(),
|h: &mut Host, val: String| h.hostname = val,
)
.register_get_set(
"pwned",
|h: &mut Host| h.pwned,
|h: &mut Host, val: bool| h.pwned = val,
)
.register_fn("add_port", Host::add_port)
.register_fn("add_finding", Host::add_finding);
engine
.register_type_with_name::<Project>("Project")
.register_get_set(
"name",
|p: &mut Project| p.name.clone(),
|p: &mut Project, val: String| p.name = val,
)
.register_get_set(
"org_name",
|p: &mut Project| p.org_name.clone(),
|p: &mut Project, val: String| p.org_name = val,
)
.register_get_set(
"current",
|p: &mut Project| p.current.clone(),
|p: &mut Project, val: bool| p.current = val,
)
.register_get_set(
"notes",
|p: &mut Project| p.notes.clone(),
|p: &mut Project, val: PathBuf| p.notes = val,
)
.register_get_set(
"files",
|p: &mut Project| p.files.clone(),
|p: &mut Project, val: PathBuf| p.files = val,
)
.register_fn("add_host", Project::add_host);
engine.register_fn("get_host", |hosts: &mut Vec<Host>, index: i64| {
hosts.get(index as usize).cloned().unwrap_or_else(Host::new)
});
engine.register_fn("new_host", Host::new);
engine.register_fn("open_file", |path: &str| {
std::fs::read_to_string(path).unwrap_or_else(|_| String::new())
});
engine.register_fn("add_port", |host: &mut Host, port: i64| {
host.add_port(format!("{}", port).as_str());
});
engine.register_fn("expand_target", |s: &str| -> rhai::Array {
let mut results = rhai::Array::new();
if let Ok(net) = s.trim().parse::<IpNet>() {
for host in net.hosts() {
results.push(host.to_string().into());
}
} else if let Ok(ip) = s.trim().parse::<IpAddr>() {
results.push(ip.to_string().into());
} else if s.contains(".") {
results.push(s.to_string().into());
}
results
});
engine.register_fn("filter_ips", |s: &str| -> rhai::Array {
let mut results = rhai::Array::new();
if let Ok(net) = s.trim().parse::<IpNet>() {
results.push(net.addr().to_string().trim().into());
} else if let Ok(ip) = s.trim().parse::<IpAddr>() {
results.push(ip.to_string().trim().into());
} else if s.contains(".") {
results.push(s.to_string().trim().into());
}
results
});
engine.register_fn("write_file", |path: &str, contents: &str| {
std::fs::write(path, contents).is_ok()
});
engine.register_fn("find_file", |path: PathBuf, filename: String| -> String {
for entry in walkdir::WalkDir::new(path).max_depth(50) {
if let Ok(entry) = entry {
if entry
.file_name()
.to_string_lossy()
.contains(filename.as_str())
{
return entry.path().display().to_string();
}
}
}
String::new()
});
engine.register_fn("execute", |cmd: &str| -> String {
let out_string;
if let Ok(output) = Command::new("sh").arg("-c").arg(cmd).output() {
if output.status.success() {
out_string = format!("[success] {}", String::from_utf8_lossy(&output.stdout));
} else {
out_string = format!("[error] {}", String::from_utf8_lossy(&output.stderr));
}
} else {
out_string = "[error] failed to run command".to_string();
}
return out_string;
});
engine.register_fn("db_run", |p: &mut Project, command: String| -> String {
let mut out_string = format!("No distrobox configured for selected project.");
if let Some(db) = p.db.clone() {
out_string = db.run_command(&command);
}
return out_string;
});
Self {
engine: Arc::new(engine),
commands: HashMap::new(),
asts: HashMap::new(),
}
}
pub fn load_all(&mut self, base_path: &PathBuf) -> Result<(), std::io::Error> {
self.commands.clear();
self.asts.clear();
self.load_from_dir(&base_path.join("default"))?;
self.load_from_dir(&base_path.join("custom"))?;
Ok(())
}
fn load_from_dir(&mut self, dir: &PathBuf) -> Result<(), std::io::Error> {
if !dir.exists() {
return Ok(());
}
for entry in read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if let Some((command, ast)) = self.parse_module_directory(&path) {
self.asts.insert(command.name.clone(), ast);
self.commands.insert(command.name.clone(), command);
}
}
}
Ok(())
}
fn parse_module_directory(&self, dir: &PathBuf) -> Option<(ToolCommand, AST)> {
let config_path = dir.join("config.conf");
let help_path = dir.join("help.txt");
let script_path = dir.join("script.rhai");
if !config_path.exists() || !help_path.exists() || !script_path.exists() {
return None;
}
let config_content = read_to_string(&config_path).ok()?;
let mut name = String::new();
let mut output_type = String::new();
let mut args = Vec::new();
let mut calls = String::new();
for line in config_content.lines() {
if let Some((key, val)) = line.split_once(':') {
match key.trim() {
"name" => name = val.trim().to_string(),
"output_type" => output_type = val.trim().to_string(),
"args" => {
args = val
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
}
"calls" => {
calls = val.trim().to_string();
}
_ => {}
}
}
}
if name.is_empty() {
return None;
}
let help = read_to_string(&help_path).unwrap_or_default();
let ast = match self.engine.compile_file(script_path.clone()) {
Ok(compiled_ast) => compiled_ast,
Err(e) => {
eprintln!("[error] compiling script in {:?}: {}", script_path, e);
return None;
}
};
let command = ToolCommand {
name,
path: script_path,
help,
args,
output_type,
finished: false,
result: false,
calls,
};
Some((command, ast))
}
}
+488
View File
@@ -0,0 +1,488 @@
use crate::Host;
use crate::ToolMessage;
use crate::User;
use ipnet::IpNet;
use std::collections::HashMap;
use std::error::Error;
use std::fs::{File, create_dir_all, read_dir, read_to_string};
use std::io::{BufRead, BufReader, Write};
use std::net::IpAddr;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::sync::mpsc::Sender;
#[derive(Clone, Debug)]
pub struct Project {
pub org_name: String,
pub config_folder: PathBuf,
pub name: String,
pub notes: PathBuf,
pub files: PathBuf,
pub hosts: Vec<Host>,
pub current: bool,
pub db: Option<DistroBox>,
pub scope: Vec<String>,
}
impl Project {
pub fn new() -> Self {
Self {
org_name: String::new(),
config_folder: PathBuf::new(),
name: String::new(),
notes: PathBuf::new(),
files: PathBuf::new(),
hosts: Vec::new(),
current: false,
db: None,
scope: Vec::new(),
}
}
pub fn org_name(&mut self, name: String) {
self.org_name = name;
}
pub fn notes(&mut self, path: PathBuf) {
self.notes = path;
}
pub fn files(&mut self, path: PathBuf) {
self.files = path;
}
pub fn hosts(&mut self, hosts: Vec<Host>) {
self.hosts = hosts;
}
pub fn add_host(&mut self, mut host: Host) {
let mut new_id = 0;
self.hosts.iter().for_each(|h| {
if h.id > new_id {
new_id = h.id.clone();
}
});
new_id += 1;
if !self.hosts.iter().any(|h| h.ip == host.ip) {
host.id = new_id;
self.hosts.push(host);
}
}
pub fn config_folder(&mut self, path: PathBuf) {
self.config_folder = path;
}
pub fn add_scope(&mut self, host: String) {
if let Ok(ip) = host.parse::<IpAddr>() {
self.scope.push(ip.to_string())
} else if let Ok(net) = host.parse::<IpNet>() {
net.hosts().into_iter().for_each(|ip| {
self.scope.push(ip.to_string());
});
} else {
self.scope.push(host);
}
}
pub fn load_config(&mut self, display: bool) -> Result<(), Box<dyn Error>> {
let config_contents = read_to_string(&self.config_folder)?;
for line in config_contents.lines() {
let parts: Vec<&str> = line.split(": ").collect();
if parts.len() == 2 {
match parts[0].trim() {
"org_name" => self.org_name(parts[1].trim().to_string()),
"name" => self.name = parts[1].trim().to_string(),
"notes" => self.notes(PathBuf::from(parts[1].trim())),
"files" => self.files(PathBuf::from(parts[1].trim())),
"stage" => {
if parts[1].trim() == "current" {
self.current = true;
}
}
"scope" => {
let ips: Vec<&str> = parts[1].split(",").collect();
ips.iter().for_each(|ip| {
self.add_scope(ip.trim().to_string());
});
}
_ => {}
}
}
}
if display {
println!("{} | {} config loaded!", self.org_name, self.name);
println!("loading hosts...");
}
let mut conf_folder = self.config_folder.clone();
conf_folder.pop();
let mut hosts_folder = conf_folder.clone();
let mut users_folder = conf_folder.clone();
hosts_folder.push("hosts");
users_folder.push("users");
let mut users = HashMap::new();
if users_folder.exists() {
for entry in read_dir(users_folder)? {
let entry = entry?;
let user_config_string = read_to_string(entry.path())?;
let mut new_user = User::new();
for line in user_config_string.lines() {
if line.contains(": ") {
let (setting, data) = line.split_once(": ").unwrap();
match setting.trim() {
"username" => new_user.name = data.trim().to_string(),
"password" => new_user.password = Some(data.trim().to_string()),
"hash" => new_user.hash = Some(data.trim().to_string()),
"compromised" => new_user.compromised = data.trim().parse().unwrap(),
"ticket" => new_user.ticket = Some(PathBuf::from(data.trim())),
_ => {}
}
}
}
users.insert(new_user.name.clone(), new_user);
}
}
if hosts_folder.exists() {
read_dir(hosts_folder)?.into_iter().for_each(|res| {
if let Ok(entry) = res {
let host_path = entry.path();
let mut new_host = Host::new();
println!("loading {}...", host_path.display());
if let Ok(host_conf_text) = read_to_string(host_path) {
host_conf_text.lines().into_iter().for_each(|line| {
if line.contains(": ") {
let (setting, data) = line.split_once(": ").unwrap();
match setting.trim() {
"ip" => new_host.ip = data.trim().to_string(),
"hostname" => new_host.hostname = data.trim().to_string(),
"control_port" => {
new_host.control_port = data.trim().parse().unwrap();
}
"pwned" => {
new_host.pwned = data.trim().parse().unwrap();
}
"id" => new_host.id = data.trim().parse().unwrap(),
"findings" => {
new_host.findings = data
.trim()
.split(",")
.map(|finding| finding.to_string())
.collect::<Vec<String>>()
}
"open_ports" => {
new_host.open_ports = data
.trim()
.split(",")
.into_iter()
.map(|port| port.parse().unwrap_or_default())
.collect::<Vec<usize>>()
}
"users" => {
let names: Vec<&str> = data.trim().split(",").collect();
names.into_iter().for_each(|name| {
if let Some(user) = users.get(name) {
new_host.users.push(user.clone());
}
});
}
_ => {}
}
}
});
self.add_host(new_host);
}
}
});
}
if display {
println!("{} | {} loaded!", self.org_name, self.name);
}
return Ok(());
}
pub fn save_config(&mut self) -> Result<(), Box<dyn Error>> {
let mut conf_folder = self.config_folder.clone();
conf_folder.pop();
if !conf_folder.exists() {
match create_dir_all(&conf_folder) {
Ok(_) => {}
Err(e) => {
return Err(
format!("save_config: couldn't create project conf folder! {e}").into(),
);
}
}
}
let mut hosts_folder = conf_folder.clone();
hosts_folder.push("hosts");
let mut users_folder = conf_folder.clone();
users_folder.push("users");
if !hosts_folder.exists() {
create_dir_all(&hosts_folder)?;
}
if !users_folder.exists() {
create_dir_all(&users_folder)?;
}
let mut file = File::create(&self.config_folder)?;
let mut out_string = format!(
"org_name: {}\nname: {}\nnotes: {}\nfiles: {}\nscope: {}\n",
self.org_name,
self.name,
self.notes.display(),
self.files.display(),
self.scope.join(","),
);
if self.current {
out_string.push_str("stage: current");
} else {
out_string.push_str("stage: upcoming");
}
file.write_all(out_string.as_bytes())?;
self.hosts
.iter_mut()
.try_for_each(|h| -> Result<(), Box<dyn Error>> {
let mut host_path = hosts_folder.clone();
host_path.push(format!("{}.conf", h.id));
h.save_config_file(host_path)
})?;
return Ok(());
}
}
impl std::fmt::Display for Project {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} | {}", self.org_name, self.name)
}
}
impl PartialEq for Project {
fn eq(&self, other: &Self) -> bool {
format!("{} | {}", self.org_name, self.name)
== format!("{} | {}", other.org_name, other.name)
}
}
impl Eq for Project {}
#[derive(Clone, Debug)]
pub struct ToolCommand {
pub name: String,
pub path: PathBuf,
pub help: String,
pub args: Vec<String>,
pub output_type: String,
pub finished: bool,
pub result: bool,
pub calls: String,
}
#[derive(Debug, Clone)]
pub struct DistroBox {
pub name: String,
pub volumes: Vec<String>,
pub created: bool,
pub template: String,
}
impl DistroBox {
pub fn create(&mut self, tx: Sender<ToolMessage>, rid: usize) -> Result<(), Box<dyn Error>> {
if let Err(e) = self.stop_template(tx.clone(), rid) {
tx.send(ToolMessage::Output((
rid,
format!("[error] stopping template box! {e}"),
)))?;
return Ok(());
}
let mut create_command = Command::new("distrobox");
create_command
.arg("create")
.arg("--clone")
.arg(self.template.clone())
.arg("--name")
.arg(self.name.clone());
let mut added_volumes = Vec::new();
for volume in &self.volumes {
let folder_name = volume.split("/").last().unwrap();
if !added_volumes.contains(&String::from(folder_name)) {
create_command
.arg("--volume")
.arg(format!("{}:/{}:rw", volume, folder_name));
added_volumes.push(folder_name.to_string());
}
}
create_command.stdout(Stdio::piped());
create_command.stderr(Stdio::piped());
let mut child = create_command.spawn()?;
if let Some(stdout) = child.stdout.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stdout);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
if let Some(stderr) = child.stderr.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stderr);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
let status = child.wait()?;
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("[error] creating distrobox!"),
)));
}
self.created = true;
return Ok(());
}
pub fn stop_template(
&mut self,
tx: Sender<ToolMessage>,
rid: usize,
) -> Result<(), Box<dyn Error>> {
let mut stop_command = Command::new("distrobox");
stop_command
.arg("stop")
.arg(self.template.clone())
.arg("--yes");
stop_command.stdin(Stdio::piped());
stop_command.stdout(Stdio::piped());
stop_command.stderr(Stdio::piped());
let mut child = stop_command.spawn()?;
if let Some(stdout) = child.stdout.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stdout);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
if let Some(stderr) = child.stderr.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stderr);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
let status = child.wait()?;
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("[error] stopping template distrobox!"),
)));
}
return Ok(());
}
pub fn stop(&mut self, tx: Sender<ToolMessage>, rid: usize) -> Result<(), Box<dyn Error>> {
let mut stop_command = Command::new("distrobox");
stop_command.arg("stop").arg(self.name.clone()).arg("--yes");
stop_command.stdin(Stdio::piped());
stop_command.stdout(Stdio::piped());
stop_command.stderr(Stdio::piped());
let mut child = stop_command.spawn()?;
if let Some(stdout) = child.stdout.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stdout);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
}
});
}
if let Some(stderr) = child.stderr.take() {
let tx_out = tx.clone();
std::thread::spawn(move || {
let reader = BufReader::new(stderr);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
}
});
}
let status = child.wait()?;
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("[error] stopping distrobox!"),
)));
}
return Ok(());
}
pub fn destroy(&mut self, tx: Sender<ToolMessage>, rid: usize) -> Result<(), Box<dyn Error>> {
let mut destroycmd = Command::new("distrobox");
destroycmd.arg("rm").arg(self.name.clone()).arg("-f");
let res = destroycmd.status()?;
if !res.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("[error] destroying distrobox!"),
)));
let _ = tx.send(ToolMessage::Output((
rid,
String::from("pleas try destroying manually:"),
)));
let _ = tx.send(ToolMessage::Output((
rid,
format!("distrobox rm {} -f", self.name),
)));
}
self.created = false;
return Ok(());
}
pub fn launch_terminal(&self, cmd: String) {
if let Some((terminal_cmd, args)) = cmd.split_once(" ") {
let mut term_cmd = Command::new(terminal_cmd);
args.split(" ").into_iter().for_each(|arg| {
if arg == "|||COMMAND|||" {
term_cmd
.arg("distrobox")
.arg("enter")
.arg(self.name.clone());
} else if arg.contains("ENV_NAME=") {
let (_, env_name) = arg.split_once("=").unwrap();
unsafe { std::env::set_var(env_name, self.name.clone()) };
} else {
term_cmd.arg(arg);
}
});
let _ = term_cmd.spawn();
}
}
pub fn run_command(&self, cmd: &str) -> String {
let output = Command::new("distrobox")
.arg("enter")
.arg("-e")
.arg(format!("\"{}\"", cmd))
.output();
match output {
Ok(output) => {
if output.status.success() {
return String::from_utf8_lossy(&output.stdout).to_string();
} else {
return format!("[error] {}", String::from_utf8_lossy(&output.stdout));
}
}
Err(e) => {
return format!("[error] {e}");
}
}
}
}
-6
View File
@@ -3,8 +3,6 @@ use anyhow::Context;
use dns_lookup::lookup_host; use dns_lookup::lookup_host;
use headless_chrome::{Browser, LaunchOptions}; use headless_chrome::{Browser, LaunchOptions};
use reqwest::StatusCode; use reqwest::StatusCode;
use std::net::SocketAddr;
use trust_dns_resolver::lookup::Ipv4Lookup;
#[derive(Clone)] #[derive(Clone)]
pub enum BusterTarget { pub enum BusterTarget {
@@ -153,7 +151,3 @@ pub fn rustbuster(target: BusterTarget) -> Option<String> {
} }
} }
} }
pub fn shodan(ip: String) -> String {
return "todo".to_string();
}
+3 -4
View File
@@ -1,4 +1,3 @@
use keyring::Entry;
use rcgen::generate_simple_self_signed; use rcgen::generate_simple_self_signed;
use std::error::Error; use std::error::Error;
use std::fs; use std::fs;
@@ -54,7 +53,7 @@ pub struct Server {
} }
pub async fn start_server(server: Arc<Mutex<Server>>) -> Result<(), Box<dyn Error>> { pub async fn start_server(server: Arc<Mutex<Server>>) -> Result<(), Box<dyn Error>> {
let mut lock = server.lock().unwrap(); let lock = server.lock().unwrap();
let mut name_path = lock.certificate_path.clone(); let mut name_path = lock.certificate_path.clone();
name_path.pop(); name_path.pop();
name_path.push("names.txt"); name_path.push("names.txt");
@@ -494,7 +493,7 @@ where
messages.push(format!("OUTPUT|{}", text)); messages.push(format!("OUTPUT|{}", text));
} }
ClientAction::Collab(text) => { ClientAction::Collab(text) => {
println!("todo"); println!("todo, {}", text);
} }
} }
} }
@@ -565,7 +564,7 @@ where
messages.push(format!("OUTPUT|{}", text)); messages.push(format!("OUTPUT|{}", text));
} }
ClientAction::Collab(text) => { ClientAction::Collab(text) => {
println!("todo"); println!("todo, {}", text);
} }
} }
} }
-40
View File
@@ -1,6 +1,4 @@
use crate::*; use crate::*;
use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce, aead::Aead};
use rand::{self, Rng, TryRng};
use rayon::spawn; use rayon::spawn;
pub fn generate_code( pub fn generate_code(
@@ -48,44 +46,6 @@ pub fn generate_code(
return Ok(()); return Ok(());
} }
/*fn generate_obfuscated_code(
address: String,
password: String,
config_file: PathBuf,
files: PathBuf,
target: String,
tx: Sender<ToolMessage>,
cert_text: String,
) -> Result<(), Box<dyn Error>> {
let mut source_path = config_file.clone();
source_path.pop();
source_path.push("victim/src/main.rs");
let mut source = read_to_string(&source_path)?;
source = source
.replace("|||SERVER|||", &address)
.replace("|||PASSWORD|||", &password)
.replace("|||CERT|||", &cert_text);
files_base.pop();
files_base.pop();
files_base.push("Cargo.toml");
let cargo_string = read_to_string(source_path)?;
let mut files_base = files.clone();
files_base.push("victim/src");
create_dir_all(&files_base)?;
files_base.push("main.rs");
let mut src_file = File::create(&files_base)?;
let mut key = [0u8; 32];
rand::rng().fill_bytes(&mut key);
let mut iv = [0u8; 16];
rand::rng().fill_bytes(&mut iv);
let cipher = Aes256Gcm::new_from_slice(&key)?;
let nonce = Nonce::try_from(&iv[..])?;
let mut ciphertext = cipher.encrypt(&nonce, source.as_bytes())?;
Ok(())
}*/
fn compile_victim(mut cargo_command: Command, tx: Sender<ToolMessage>) { fn compile_victim(mut cargo_command: Command, tx: Sender<ToolMessage>) {
let mut out = "Error compiling output!".to_string(); let mut out = "Error compiling output!".to_string();
if let Ok(output) = cargo_command.output() { if let Ok(output) = cargo_command.output() {