added code to generate victim binaries, implemented client -> server

authentication and fixed a bug in the ui where not all output would be
displayed.
This commit is contained in:
2026-08-14 16:23:40 -05:00
parent 7b1f9f9d4e
commit 2b3a787fb6
9 changed files with 1197 additions and 343 deletions
+207 -81
View File
@@ -1,5 +1,6 @@
use fs_extra::dir::{CopyOptions, copy};
use ipnet::IpNet;
use keyring::Entry;
use ratatui::crossterm::event;
use rhai::{AST, Dynamic, Engine, Scope};
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
@@ -18,6 +19,7 @@ use std::time::{self, Instant};
pub mod funcs;
pub mod server;
pub mod victim;
enum AppEvent {
Key(event::KeyEvent),
@@ -81,12 +83,14 @@ pub struct AppState {
pub curent_intput: String,
pub module_loader: ModuleLoader,
pub output_scroll: u16,
pub output_follow: bool,
prompt: Prompt,
pub info_scroll: u16,
pub server_broker_running: bool,
pub app_state_running: bool,
pub remoting: bool,
pub help: Vec<String>,
pub name: String,
}
impl AppState {
@@ -112,6 +116,7 @@ impl AppState {
curent_intput: String::new(),
module_loader: ModuleLoader::new(),
output_scroll: 0,
output_follow: true,
prompt: Prompt {
action: None,
responses: Vec::new(),
@@ -124,6 +129,7 @@ impl AppState {
app_state_running: true,
remoting: false,
help: Vec::new(),
name: String::new(),
},
main_rx,
)
@@ -137,23 +143,28 @@ impl AppState {
if parts.len() == 2 {
match parts[0].trim() {
"servers" => {
let addresses: Vec<&str> = parts[1].trim().split(", ").collect();
for address in addresses {
let new_server = Server {
address: address.trim().to_string(),
connected: false,
timer: Duration::from_secs(60),
last_check: time::Instant::now(),
message_que: Vec::new(),
action_que: Vec::new(),
client_id: 0,
selected_client: 0,
config: None,
};
self.servers.push(Arc::new(Mutex::new(new_server)));
for server_data in parts[1].trim().split(", ").collect::<Vec<&str>>() {
if let Some((address, name)) = server_data.split_once("|") {
let new_server = Server {
address: address.trim().to_string(),
connected: false,
timer: Duration::from_secs(60),
last_check: time::Instant::now(),
message_que: Vec::new(),
action_que: Vec::new(),
client_id: 0,
selected_client: 0,
config: None,
logged_in: false,
password: String::new(),
name: name.to_string(),
cert_text: String::new(),
};
self.servers.push(Arc::new(Mutex::new(new_server)));
}
}
self.config
.insert("servers".to_string(), line.trim().to_string());
.insert("servers".to_string(), parts[1].trim().to_string());
}
_ => {
if parts[0].len() > 1 {
@@ -293,15 +304,62 @@ impl AppState {
if let Err(e) = self.new_project(org.to_string(), name.to_string(), rid) {
tx.send(ToolMessage::Output((
rid,
format!("Error making {}-{}: {e}", org, name),
format!("[error] making {}-{}: {e}", org, name),
)))?;
} else {
tx.send(ToolMessage::Output((
rid,
format!("{}-{} created successfully!", org, name),
format!("[success] {}-{} created!", org, name),
)))?;
}
}
"generate_victim" => {
let mut success = false;
if let Some(args) = command_args {
if args.split(" ").into_iter().count() == 1 {
let mut address = String::new();
let mut password = String::new();
let config_file = self.config_file.clone();
let files = self.projects[self.selected_project].files.clone();
let mut cert_text = String::new();
if let Ok(lock) = self.servers[self.selected_server].lock() {
address = lock.address.clone();
password = lock.password.clone();
cert_text = lock.cert_text.clone();
}
match victim::generate_code(
address,
password,
config_file,
files,
args,
self.main_tx.clone(),
cert_text,
) {
Ok(_) => {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
"[Success] Victim code generated! Compilation thread spawned!"
.to_string(),
)));
success = true;
}
Err(e) => {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!("[error] generating victim code: {e}"),
)));
}
}
}
}
if !success {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!("Usage: generate_victim (windows/linux)"),
)));
}
}
"current_project" | "cp" => {
let mut out_vec = Vec::new();
out_vec.push(format!(
@@ -345,13 +403,13 @@ impl AppState {
Ok(_) => {
self.main_tx.send(ToolMessage::Output((
rid,
String::from("project promoted successfully!"),
String::from("[success] project promoted!"),
)))?;
}
Err(e) => {
self.main_tx.send(ToolMessage::Output((
rid,
format!("Error promoting project: {}", e),
format!("[error] promoting project: {}", e),
)))?;
}
}
@@ -477,6 +535,10 @@ impl AppState {
client_id: 0,
selected_client: 0,
config: None,
logged_in: false,
password: String::new(),
name: String::new(),
cert_text: String::new(),
};
self.servers.push(Arc::new(Mutex::new(new_server)));
self.prompt.reset();
@@ -491,12 +553,11 @@ impl AppState {
.send(ToolMessage::Input((0, self.prompt.execute_command.clone())));
let _ = self.main_tx.send(ToolMessage::EndPrompt);
} else {
let mut query = self.prompt.prompts[0].clone();
if self.prompt.responses.len() > 0
&& self.prompt.responses.len() < self.prompt.prompts.len()
{
query = self.prompt.prompts[self.prompt.responses.len() - 1].clone();
let mut prompt_index = 0;
if self.prompt.responses.len() > 0 {
prompt_index = self.prompt.num_responses - self.prompt.responses.len();
}
let query = self.prompt.prompts[prompt_index].clone();
let _ = self.main_tx.send(ToolMessage::Output((rid, query)));
}
}
@@ -507,16 +568,16 @@ impl AppState {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"Server ID:{} Address:{} ClientID:{} Sleep:{} Last:{} - Currently Selected",
id, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs()
"Server ID:{} Name:{} Address:{} ClientID:{} Sleep:{} Last:{} Loggedin: {} - Currently Selected",
id, s.name, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs(), s.logged_in
),
)));
} else {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"Server ID:{} Address:{} ClientID:{} Sleep:{} Last:{}",
id, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs()
"Server ID:{} Name:{} Address:{} ClientID:{} Sleep:{} Last:{}, loggedin: {}",
id, s.name, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs(), s.logged_in
),
)));
}
@@ -563,7 +624,7 @@ impl AppState {
} else {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
String::from("error selecting server: no prompt responses found!"),
String::from("[error] selecting server: no prompt responses found!"),
)));
}
let _ = self.main_tx.send(ToolMessage::EndPrompt);
@@ -573,11 +634,14 @@ impl AppState {
if let Ok(mut server) = server.lock() {
server.message_que.push("TEST|NONE".to_string());
} else {
self.output.push(String::from("error getting server lock!"));
let _ = self.main_tx.send(ToolMessage::Output((
rid,
"[error] getting server lock!".to_string(),
)));
}
} else {
self.output
.push(String::from("Error getting selected server!"));
.push(String::from("[error] getting selected server!"));
}
}
"server_sleep" => {
@@ -590,20 +654,22 @@ impl AppState {
.push(format!("Server timer set to {} seconds", seconds));
} else {
self.output
.push(String::from("error locking server object!"));
.push(String::from("[error] locking server object!"));
}
} else {
self.output
.push(String::from("error not a valid server selected!"));
.push(String::from("[error] not a valid server selected!"));
}
} else {
self.output
.push(String::from("invalid second count provided!"));
}
} else {
self.output.push(String::from(
"Error no seconds provided! please use server_sleep number_of_seconds",
));
let _ = self.main_tx.send(ToolMessage::Output((
rid,
"[error] no seconds provided! please use server_sleep number_of_seconds"
.to_string(),
)));
}
}
"save_all" => {
@@ -617,11 +683,14 @@ impl AppState {
if let Ok(mut server) = server.lock() {
server.message_que.push("LIST_CLIENTS|NONE".to_string());
} else {
self.output.push(String::from("error getting server lock!"));
let _ = self.main_tx.send(ToolMessage::Output((
rid,
"[error] getting server lock!".to_string(),
)));
}
} else {
self.output
.push(String::from("Error getting selected server!"));
.push(String::from("[error] getting selected server!"));
}
}
"select_client" => {
@@ -639,7 +708,7 @@ impl AppState {
} else {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("error parsing client id from arg: {}", arg),
format!("[error] parsing client id from arg: {}", arg),
)));
}
}
@@ -658,7 +727,7 @@ impl AppState {
server.selected_client = 0;
let _ = self
.main_tx
.send(ToolMessage::Output((0, format!("client deselected!"))));
.send(ToolMessage::Output((rid, format!("client deselected!"))));
}
}
}
@@ -692,7 +761,7 @@ impl AppState {
"show_config" | "sc" => {
let _ = self
.main_tx
.send(ToolMessage::Output((0, "Current Config:".to_string())));
.send(ToolMessage::Output((rid, "Current Config:".to_string())));
self.config.iter().for_each(|setting| {
let _ = self.main_tx.send(ToolMessage::Output((
0,
@@ -714,7 +783,7 @@ impl AppState {
} else {
let _ = self.main_tx.send(ToolMessage::Output((
0,
"Error Unknown setting! {key}, {value}".to_string(),
"[error] Unknown setting! {key}, {value}".to_string(),
)));
return Ok(());
}
@@ -725,12 +794,12 @@ impl AppState {
self.save_config()?;
let _ = self.main_tx.send(ToolMessage::Output((
0,
"Setting changed successfully!".to_string(),
"[success]Setting changed!".to_string(),
)));
} else {
let _ = self.main_tx.send(ToolMessage::Output((
0,
"Error malformed command!".to_string(),
"[error] malformed command!".to_string(),
)));
let _ = self.main_tx.send(ToolMessage::Output((
0,
@@ -742,9 +811,23 @@ impl AppState {
self.module_loader.commands.iter().for_each(|module| {
let _ = self
.main_tx
.send(ToolMessage::Output((0, format!("{}", module.0))));
.send(ToolMessage::Output((rid, format!("{}", module.0))));
});
}
"server_login" => {
if let Some(pass) = command_args {
let pass = pass.trim().to_string();
if let Some(server) = self.servers.get(self.selected_server) {
if let Ok(mut lock) = server.lock() {
lock.login(pass)?;
let _ = self.main_tx.send(ToolMessage::Output((
rid,
"[success] Logged into Server!".to_string(),
)));
}
}
}
}
"exit" => {
self.save_all()?;
let _ = self.main_tx.send(ToolMessage::AppStateExit);
@@ -756,7 +839,7 @@ impl AppState {
if self.module_loader.asts.get(command_name).is_none() {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("Error command not found: {}", command_name),
format!("[error] command not found: {}", command_name),
)));
return Ok(());
}
@@ -847,7 +930,7 @@ impl AppState {
Err(err) => {
let _ = tx.send(ToolMessage::Output((
rid,
format!("Script Error in execution: {}", err),
format!("Script [error] in execution: {}", err),
)));
}
}
@@ -893,7 +976,7 @@ impl AppState {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"error making project files! {e} on {}",
"[error] making project files! {e} on {}",
project_files.display()
),
)));
@@ -905,7 +988,7 @@ impl AppState {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"error making project notes! {e} on {}",
"[error] making project notes! {e} on {}",
project_notes.display()
),
)));
@@ -917,7 +1000,7 @@ impl AppState {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"error making config folder! {e} on {}",
"[error] making config folder! {e} on {}",
project_conf_folder.display()
),
)));
@@ -939,7 +1022,7 @@ impl AppState {
Err(e) => {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("error copying note template! {e}"),
format!("[error] copying note template! {e}"),
)));
}
}
@@ -949,7 +1032,7 @@ impl AppState {
Err(e) => {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("error copying note template! {e}"),
format!("[error] copying note template! {e}"),
)));
}
}
@@ -988,7 +1071,7 @@ impl AppState {
rid,
String::from("promoting project..."),
)))?;
let return_string = String::from("Project promoted successfully!");
let return_string = String::from("[success] Project promoted!");
let mut new_project = self.projects[self.selected_project].clone();
let new_files_path = PathBuf::from(self.config.get("current_files").unwrap())
.join(format!("{}/{}", new_project.org_name, new_project.name));
@@ -1002,7 +1085,7 @@ impl AppState {
copy(new_project.notes.clone(), new_notes_path.clone(), &options)?;
self.main_tx.send(ToolMessage::Output((
rid,
String::from("folders copied successfully!"),
String::from("[success] folders copied!"),
)))?;
remove_dir_all(&new_project.files)?;
remove_dir_all(&new_project.notes)?;
@@ -1080,6 +1163,10 @@ impl AppState {
client_id: 0,
selected_client: 0,
config: None,
logged_in: false,
password: String::new(),
name: String::new(),
cert_text: String::new(),
};
self.servers.push(Arc::new(Mutex::new(new_server)));
self.prompt.action = None;
@@ -1118,6 +1205,10 @@ pub struct Server {
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 {
@@ -1136,6 +1227,7 @@ impl Server {
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".as_bytes())?;
@@ -1150,27 +1242,50 @@ impl Server {
return Ok(self.client_id);
}
pub fn checkin(&mut self) {
if let Ok(mut stream) = self.tls_connect() {
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();
let payload = format!(
"{}|||{}\n",
self.client_id.clone(),
self.message_que.join("||")
);
self.logged_in = true;
self.message_que.push(format!("SET_NAME|{}", self.name));
return Ok(());
} else {
return Err("[error] logging in!".into());
}
}
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());
});
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;
}
});
}
}
}
}
@@ -1198,6 +1313,13 @@ impl Server {
let tls_stream = StreamOwned::new(connection, tcp);
Ok(tls_stream)
}
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(())
}
}
#[derive(Clone, Debug)]
@@ -1665,10 +1787,14 @@ impl ModuleLoader {
engine.register_fn("write_file", |path: &str, contents: &str| {
std::fs::write(path, contents).is_ok()
});
engine.register_fn("find_scope_file", |path: PathBuf| -> String {
engine.register_fn("find_file", |path: PathBuf, filename: String| -> String {
for entry in walkdir::WalkDir::new(path).max_depth(10) {
if let Ok(entry) = entry {
if entry.file_name().to_string_lossy().contains("scope") {
if entry
.file_name()
.to_string_lossy()
.contains(filename.as_str())
{
return entry.path().display().to_string();
}
}
@@ -1751,7 +1877,7 @@ impl ModuleLoader {
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);
eprintln!("[error] compiling script in {:?}: {}", script_path, e);
return None;
}
};
@@ -1784,7 +1910,7 @@ impl DistroBox {
if let Err(e) = self.stop_template(tx.clone(), rid) {
tx.send(ToolMessage::Output((
rid,
format!("error stopping template box! {e}"),
format!("[error] stopping template box! {e}"),
)))?;
return Ok(());
}
@@ -1832,7 +1958,7 @@ impl DistroBox {
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("error creating distrobox!"),
format!("[error] creating distrobox!"),
)));
}
@@ -1878,7 +2004,7 @@ impl DistroBox {
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("error stopping template distrobox!"),
format!("[error] stopping template distrobox!"),
)));
}
return Ok(());
@@ -1915,7 +2041,7 @@ impl DistroBox {
if !status.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("error stopping distrobox!"),
format!("[error] stopping distrobox!"),
)));
}
return Ok(());
@@ -1928,7 +2054,7 @@ impl DistroBox {
if !res.success() {
let _ = tx.send(ToolMessage::Output((
rid,
format!("Error destroying distrobox!"),
format!("[error] destroying distrobox!"),
)));
let _ = tx.send(ToolMessage::Output((
rid,