diff --git a/Cargo.lock b/Cargo.lock index 3fb7e7d..4b3dc44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "cc" version = "1.0.83" @@ -436,6 +442,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wag" version = "1.0.0" dependencies = [ + "base64", "clap", "rand", "regex_generate", diff --git a/Cargo.toml b/Cargo.toml index de740dd..5f8fc51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ rpath = false winres = "0" [dependencies] +base64 = { version = "0.22.1" } windows = { version = "0.52", features = [ "Win32_System_Pipes", "Win32_Foundation", @@ -33,6 +34,7 @@ windows = { version = "0.52", features = [ "Win32_UI_Shell", "Win32_System_IO", "Win32_System_Services", + "Win32_System_SystemServices", ] } clap = { version = "4", features = ["derive"] } rand = "0" diff --git a/README.md b/README.md index f083ce4..b512d4a 100644 --- a/README.md +++ b/README.md @@ -59,3 +59,16 @@ wag.exe ``` Example can be found here [cli_help](./docs/cli_help.md) + +# Actions + + - [X] Alternate data stream + - [X] BYOVD: load a driver + - [X] file drop from executable + - [X] mutex + - [X] named pipe + - [X] ppid spoofing + - [ ] Stealer browers information (only open file) + - [ ] Stealer cryto wallet (only open file) + - [ ] Stealer file of interrest + - [ ] WMI action \ No newline at end of file diff --git a/docs/cli_help.md b/docs/cli_help.md index c2a6d0c..507527b 100644 --- a/docs/cli_help.md +++ b/docs/cli_help.md @@ -6,93 +6,104 @@ SPDX-License-Identifier: GPL-3.0-or-later # Ads -`wag ads -f file_full_path -a ads -d data` +`wag ads -f fullpath -a ads -d data` -| Type | ads | data | -| -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| ZoneTransfer 0 | Zone.Identifier | 5b5a6f6e655472616e736665725d0d0a5a6f6e6549643d300d0a526566657272657255726c3d633a5c77696e646f77735c7761672e7a69700d0a | -| ZoneTransfer 1 | Zone.Identifier | 5b5a6f6e655472616e736665725d0d0a5a6f6e6549643d310d0a526566657272657255726c3d2f2f7376725f41442f7761672e7a69700d0a | -| ZoneTransfer 2 | Zone.Identifier | 5b5a6f6e655472616e736665725d0d0a5a6f6e6549643d320d0a526566657272657255726c3d687474703a2f2f6d79736974652e6f72672f7761672e7a69700d0a | -| ZoneTransfer 3 | Zone.Identifier | 5b5a6f6e655472616e736665725d0d0a5a6f6e6549643d330d0a526566657272657255726c3d68747470733a2f2f736f6d65736974652e636f6d2f7761672e7a69700d0a | -| ZoneTransfer 4 | Zone.Identifier | 5b5a6f6e655472616e736665725d0d0a5a6f6e6549643d340d0a526566657272657255726c3d687474703a2f2f6d616c776172652e6261642f7761672e7a69700d0a | -| Sysmon | sysmon | 4920616D20746865206265737420746F20686964652066726F6D207379736D6F6E" | +* fullpath: regex of the full path +* ads: name of the stream +* data: base64 of the data to write + +| Type | ads | data | +| -------------- | --------------- | -------------------------------------------------------------------------------------------- | +| ZoneTransfer 0 | Zone.Identifier | W1pvbmVUcmFuc2Zlcl0NClpvbmVJZD0wDQpSZWZlcnJlclVybD1jOlx3aW5kb3dzXHdhZy56aXANCg== | +| ZoneTransfer 1 | Zone.Identifier | W1pvbmVUcmFuc2Zlcl0NClpvbmVJZD0xDQpSZWZlcnJlclVybD0vL3N2cl9BRC93YWcuemlwDQo= | +| ZoneTransfer 2 | Zone.Identifier | W1pvbmVUcmFuc2Zlcl0NClpvbmVJZD0yDQpSZWZlcnJlclVybD1odHRwOi8vbXlzaXRlLm9yZy93YWcuemlwDQo= | +| ZoneTransfer 3 | Zone.Identifier | W1pvbmVUcmFuc2Zlcl0NClpvbmVJZD0zDQpSZWZlcnJlclVybD1odHRwczovL3NvbWVzaXRlLmNvbS93YWcuemlwDQo= | +| ZoneTransfer 4 | Zone.Identifier | W1pvbmVUcmFuc2Zlcl0NClpvbmVJZD00DQpSZWZlcnJlclVybD1odHRwOi8vbWFsd2FyZS5iYWQvd2FnLnppcA0K | +| Sysmon | sysmon | SSBhbSB0aGUgYmVzdCB0byBoaWRlIGZyb20gc3lzbW9u | # File ## magicbytes -| Type | Hex | -| ---- | ---------------------------------------------------- | -| Exe | 4D5A | -| Zip | 504B0304 | -| Vmdk | 4B444D | -| Iso | 4344303031 | -| Txt | 412073696d706c6520746578742066696c65 | -| Ps1 | 77726974652d686f73742022574147207761732048657265220a | +| Type | Hex | +| ---- | ------------------------------------ | +| Exe | TVo= | +| Zip | UEsDBA== | +| Vmdk | S0RN | +| Iso | Q0QwMDE= | +| Txt | QSBzaW1wbGUgdGV4dCBmaWxl | +| Ps1 | d3JpdGUtaG9zdCAiV0FHIHdhcyBIZXJlIgo= | ## well known File -`wag file-create -f fullpath -m Magicbyte_Hex` -`wag file-create -v cmd_var -p cmd_path -m Magicbyte_Hex` - -| name | Admin | Magicbyte | fullpath | cmd_var | cmd_path | -| -------------- | ----- | --------- | -------------------------------------------- | ------------ | -------------------------------------- | -| NPPSpy | true | Exe | `C:/Windows/System32/NPPSpy\.dll` | | | -| SafetyKatz | false | Zip | | SystemRoot | `Temp\\debug\.bin` | -| SmallSieve_txt | false | Txt | | LocalAppData | `MicrosoftWindowsOutlookDataPlus\.txt` | -| SmallSieve_exe | false | Exe | | AppData | `OutlookMicrosift\\index\.exe` | -| SNAKE_jpsetup | false | Exe | | TEMP | `jpsetup\.exe` | -| SNAKE_jpinst | false | Exe | | TEMP | `jpinst\\.exe` | -| SNAKE_Comadmin | true | Exe | `C:\\Windows\\System32\\Com\\Comadmin\.dat` | | | -| COLDSTEEL_exe | false | Exe | `C:\\users\\public\\Documents\\dllhost\.exe` | | | -| COLDSTEEL_dll | false | Exe | | APPDATA | `newdev\.dll` | -| temp_ps1_12 | false | Ps1 | | SystemRoot | `temp\[0-9a-f]{12}\.ps1` | +`wag file-create -f fullpath -m Magicbyte_Hex ` + +* fullpath: regex of the full path +* Magicbyte_Hex: base64 of the magicbytes to write +* admin: can use `--admin` to check if run as administrator + +| Type | Admin | Magicbyte | fullpath | +| -------------- | ----- | --------- | -------------------------------------------------------- | +| NPPSpy | true | Exe | `C:/Windows/System32/NPPSpy\.dll` | +| SafetyKatz | false | Zip | *SystemRoot* + `Temp\\debug\.bin` | +| SmallSieve_txt | false | Txt | *LocalAppData* + `MicrosoftWindowsOutlookDataPlus\.txt` | +| SmallSieve_exe | false | Exe | *AppData* + `OutlookMicrosift\\index\.exe` | +| SNAKE_jpsetup | false | Exe | *TEMP* + `jpsetup\.exe` | +| SNAKE_jpinst | false | Exe | *TEMP* + `jpinst\\.exe` | +| SNAKE_Comadmin | true | Exe | `C:\\Windows\\System32\\Com\\Comadmin\.dat` | +| COLDSTEEL_exe | false | Exe | `C:\\users\\public\\Documents\\dllhost\.exe` | +| COLDSTEEL_dll | false | Exe | *APPDATA* + `newdev\.dll` | +| temp_ps1_12 | false | Ps1 | *SystemRoot* + `temp\[0-9a-f]{12}\.ps1` | + +Remark: You need to convert the environment variable into a correct regular expression. # Named pipe -`wag name-pipe -n "regex"` - -| name | regex | | -| ------------------ | -------------------------------------------------- | ------------------------------ | -| CSExec | `\\csexecsvc` | | -| psexec | `\\psexec` | | -| psexec | `\\PAExec` | | -| psexec | `\\remcom` | | -| psexec | `\\csexec` | | -| psexec | `\\PSEXESVC` | | -| Cobal_strike | `\\mojo\\.5688\\.8052\\.(?:183894939787088877 | 35780273329370473)[0-9a-f]{2}` | -| Cobal_strike | `\\wkssvc_?[0-9a-f]{2}` | | -| Cobal_strike | `\\ntsvcs[0-9a-f]{2}` | | -| Cobal_strike | `\\DserNamePipe[0-9a-f]{2}` | | -| Cobal_strike | `\\SearchTextHarvester[0-9a-f]{2}` | | -| Cobal_strike | `\\mypipe-(?:f | h)[0-9a-f]{2}` | -| Cobal_strike | `\\windows\\.update\\.manager[0-9a-f]{2,3}` | | -| Cobal_strike | `\\ntsvcs_[0-9a-f]{2}` | | -| Cobal_strike | `\\scerpc_?[0-9a-f]{2}` | | -| Cobal_strike | `\\PGMessagePipe[0-9a-f]{2}` | | -| Cobal_strike | `\\MsFteWds[0-9a-f]{2}` | | -| Cobal_strike | `\\f4c3[0-9a-f]{2}` | | -| Cobal_strike | `\\fullduplex_[0-9a-f]{2}` | | -| Cobal_strike | `\\msrpc_[0-9a-f]{4}` | | -| Cobal_strike | `\\win\\msrpc_[0-9a-f]{2}` | | -| Cobal_strike | `\\f53f[0-9a-f]{2}` | | -| Cobal_strike | `\\rpc_[0-9a-f]{2}` | | -| Cobal_strike | `\\spoolss_[0-9a-f]{2}` | | -| Cobal_strike | `\\Winsock2\\CatalogChangeListener-[0-9a-f]{3}-0,` | | -| DiagTrackEoP | `thisispipe` | | -| EfsPotato | `\\pipe\\srvsvc` | | -| Credential_Dumping | `\\cachedump` | | -| Credential_Dumping | `\\lsadump` | | -| Credential_Dumping | `\\wceservicepipe` | | -| Koh | `\\imposecost` | | -| Koh | `\\imposingcost` | | -| PowerShell | `\\PSHost` | | -| ADFS | `\\MICROSOFT##WID\\tsql\\query` | | +`wag name-pipe -n name` + +* name: named pipe name as a regex + +| Type | name | +| ------------------ | -------------------------------------------------- | +| CSExec | `\\csexecsvc` | +| psexec | `\\psexec` | +| psexec | `\\PAExec` | +| psexec | `\\remcom` | +| psexec | `\\csexec` | +| psexec | `\\PSEXESVC` | +| Cobal_strike | `\\wkssvc_?[0-9a-f]{2}` | +| Cobal_strike | `\\ntsvcs[0-9a-f]{2}` | +| Cobal_strike | `\\DserNamePipe[0-9a-f]{2}` | +| Cobal_strike | `\\SearchTextHarvester[0-9a-f]{2}` | +| Cobal_strike | `\\windows\\.update\\.manager[0-9a-f]{2,3}` | +| Cobal_strike | `\\ntsvcs_[0-9a-f]{2}` | +| Cobal_strike | `\\scerpc_?[0-9a-f]{2}` | +| Cobal_strike | `\\PGMessagePipe[0-9a-f]{2}` | +| Cobal_strike | `\\MsFteWds[0-9a-f]{2}` | +| Cobal_strike | `\\f4c3[0-9a-f]{2}` | +| Cobal_strike | `\\fullduplex_[0-9a-f]{2}` | +| Cobal_strike | `\\msrpc_[0-9a-f]{4}` | +| Cobal_strike | `\\win\\msrpc_[0-9a-f]{2}` | +| Cobal_strike | `\\f53f[0-9a-f]{2}` | +| Cobal_strike | `\\rpc_[0-9a-f]{2}` | +| Cobal_strike | `\\spoolss_[0-9a-f]{2}` | +| Cobal_strike | `\\Winsock2\\CatalogChangeListener-[0-9a-f]{3}-0,` | +| DiagTrackEoP | `thisispipe` | +| EfsPotato | `\\pipe\\srvsvc` | +| Credential_Dumping | `\\cachedump` | +| Credential_Dumping | `\\lsadump` | +| Credential_Dumping | `\\wceservicepipe` | +| Koh | `\\imposecost` | +| Koh | `\\imposingcost` | +| PowerShell | `\\PSHost` | +| ADFS | `\\MICROSOFT##WID\\tsql\\query` | # Mutex -`wag mutex -n "regex"` +`wag mutex -n name` + +* name: mutex name as a regex -| name | regex | +| Type | name | | ---------- | ------------------ | | avoslocker | `Cheic0WaZie6zeiy` | diff --git a/src/main.rs b/src/main.rs index 96123f5..d5f43e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,10 @@ mod cli; mod malware; +mod windows; -use cli::Arguments; use clap::Parser; +use cli::Arguments; fn banner() { let banner: &str = " diff --git a/src/malware.rs b/src/malware.rs index 2c4de67..ad1cf64 100644 --- a/src/malware.rs +++ b/src/malware.rs @@ -8,4 +8,3 @@ pub mod mutex; pub mod namepipe; pub mod ppid; pub mod service; -pub mod tools; diff --git a/src/malware/ads.rs b/src/malware/ads.rs index f592a6a..1e71a1a 100644 --- a/src/malware/ads.rs +++ b/src/malware/ads.rs @@ -6,11 +6,9 @@ // // Last update 20240224 -use crate::malware::tools::{ - hex_to_bytes, regex_to_string, EXIST_ALL_GOOD, EXIST_CLI_ERROR, EXIST_TEST_ERROR, -}; - +use base64::engine::{general_purpose, Engine}; use clap::Parser; +use regex_generate::{Generator, DEFAULT_MAX_REPEAT}; use std::path::Path; #[derive(Parser)] @@ -19,24 +17,17 @@ pub struct ADS { short = 'f', long, required = true, - default_value = "", help = "Full path filename (regex)" )] filename: String, - #[clap( - short = 'a', - long, - required = true, - default_value = "", - help = "ADS to use" - )] + #[clap(short = 'a', long, required = true, help = "ADS to use")] ads: String, #[clap( short = 'd', long, - required = true, - default_value = "", - help = "Data to write in HEX" + required = false, + default_value = "V2VsY29tZSB0byB0aGUgV0FH", + help = "Data to write in base64" )] data: String, } @@ -79,23 +70,42 @@ impl ADS { println!("Alternate Data Stream"); if self.filename.len() > 0 { - let fullname: String = regex_to_string(&self.filename); + let mut generator: Generator = + match Generator::new(&self.filename, rand::thread_rng(), DEFAULT_MAX_REPEAT) { + Ok(generator) => generator, + Err(_) => { + println!("Regex expressions are malformed."); - let header: Option> = hex_to_bytes(&self.data); - let payload: Vec = match header { - Some(data) => data, - None => vec![70, 114, 97, 99, 107, 49, 49, 51], - }; + return 1; + } + }; + let mut buffer: Vec = vec![]; + generator.generate(&mut buffer).unwrap(); + let fullname: String = match String::from_utf8(buffer) { + Ok(string) => string, + Err(_) => { + println!("Filename contains non-utf8 characters."); + return 1; + } + }; let barrow_ads: String = self.ads.to_string(); + let payload: Vec = match general_purpose::STANDARD.decode(self.data.as_str()) { + Ok(decoded) => decoded, + Err(_) => { + println!("Could not decode the data."); + + return 1; + } + }; let ret_ads: bool = create_ads(fullname, barrow_ads, payload); if ret_ads == true { - return EXIST_ALL_GOOD; + return 0; } else { - return EXIST_TEST_ERROR; + return 1; } } - EXIST_CLI_ERROR + return 1; } } diff --git a/src/malware/file.rs b/src/malware/file.rs index 8f1df88..add13e8 100644 --- a/src/malware/file.rs +++ b/src/malware/file.rs @@ -18,14 +18,12 @@ You can use `SET | more` or `Get-ChildItem Env:` to get the list */ -use crate::malware::tools::{ - hex_to_bytes, process_is_admin, regex_to_string, EXIST_ALL_GOOD, EXIST_TEST_ERROR, -}; +use crate::windows::users::is_administrator; +use base64::engine::{general_purpose, Engine}; use clap::Parser; -use std::env; -use std::ffi::OsString; +use regex_generate::{Generator, DEFAULT_MAX_REPEAT}; use std::io::Result as IOResult; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::time::Duration; use std::{thread, time}; @@ -34,33 +32,16 @@ pub struct FileCreate { #[clap( short = 'f', long, - required = false, - default_value = "", + required = true, help = "Full path filename (regex)" )] filename: String, - #[clap( - short = 'v', - long, - required = false, - default_value = "", - help = "Use the CMD variable" - )] - cmd_var: String, - #[clap( - short = 'p', - long, - required = false, - default_value = "", - help = "Use the CMD path" - )] - cmd_path: String, #[clap( short = 'm', long, - required = true, - default_value = "", - help = "MagicBytes name to use with module manual " + required = false, + default_value = "V2VsY29tZSB0byB0aGUgV0FH", + help = "MagicBytes name to use with module manual in base64" )] magicbyte: String, #[clap( @@ -107,35 +88,59 @@ fn create_file(fullpath: String, hex_data: Vec) -> bool { impl FileCreate { pub fn run(&self) -> i32 { - if self.admin && !process_is_admin() { + if self.admin + && !match is_administrator() { + Ok(is_admin) => is_admin, + Err(error) => { + println!( + "Could not check if the user is an administrator or not.\nError: {}", + error + ); + + return 1; + } + } + { println!("Need to have Administrator right to create the file"); - return EXIST_TEST_ERROR; + return 1; } - let header: Option> = hex_to_bytes(&self.magicbyte); - let payload: Vec = match header { - Some(data) => data, - None => vec![70, 114, 97, 99, 107, 49, 49, 51], + let mut generator: Generator = + match Generator::new(&self.filename, rand::thread_rng(), DEFAULT_MAX_REPEAT) { + Ok(generator) => generator, + Err(_) => { + println!("Regex expressions are malformed."); + + return 1; + } + }; + let mut buffer: Vec = vec![]; + generator.generate(&mut buffer).unwrap(); + let fullname: String = match String::from_utf8(buffer) { + Ok(string) => string, + Err(_) => { + println!("Filename contains non-utf8 characters."); + + return 1; + } }; - let fullname: String; - if self.filename.len() > 0 { - fullname = regex_to_string(&self.filename); - } else { - let filename: String = regex_to_string(&self.cmd_path); - let var_path: OsString = env::var_os(&self.cmd_var).unwrap(); - let full_path: PathBuf = Path::new(&var_path).join(filename); - fullname = String::from(full_path.to_string_lossy()); - } - println!("Create a file on disk"); + let payload: Vec = match general_purpose::STANDARD.decode(self.magicbyte.as_str()) { + Ok(decoded) => decoded, + Err(_) => { + println!("Could not decode the data."); + + return 1; + } + }; let ret: bool = create_file(fullname, payload); if ret == true { - return EXIST_ALL_GOOD; + return 0; } else { - return EXIST_TEST_ERROR; + return 1; } } } diff --git a/src/malware/mutex.rs b/src/malware/mutex.rs index ff8467f..e186a04 100644 --- a/src/malware/mutex.rs +++ b/src/malware/mutex.rs @@ -6,21 +6,19 @@ // // Last update 20240224 +use clap::Parser; +use regex_generate::{Generator, DEFAULT_MAX_REPEAT}; +use std::{thread, time}; use windows::core::{Result as WindowsResult, PCSTR}; use windows::Win32::Foundation::{CloseHandle, HANDLE}; use windows::Win32::System::Threading::CreateMutexA; -use crate::malware::tools::{regex_to_string, EXIST_ALL_GOOD}; -use clap::Parser; -use std::{thread, time}; - #[derive(Parser)] pub struct Mutex { #[clap( short = 'n', long, required = true, - default_value = "wag", help = "Regex of the Mutex to Create" )] name: String, @@ -38,10 +36,31 @@ fn create_mutex(name: &String, wait: u64) { impl Mutex { pub fn run(&self) -> i32 { println!("Create Mutex"); - let full_payload: String; - full_payload = regex_to_string(&self.name); - println!("Create the Mutex : {}", full_payload); - create_mutex(&full_payload, 2000); - return EXIST_ALL_GOOD; + + let mut generator: Generator = + match Generator::new(&self.name, rand::thread_rng(), DEFAULT_MAX_REPEAT) { + Ok(generator) => generator, + Err(_) => { + println!("Regex expressions are malformed."); + + return 1; + } + }; + let mut buffer: Vec = vec![]; + generator.generate(&mut buffer).unwrap(); + let payload: String = match String::from_utf8(buffer) { + Ok(string) => string, + Err(_) => { + println!("Filename contains non-utf8 characters."); + + return 1; + } + }; + + println!("Create the Mutex : {}", payload); + + create_mutex(&payload, 2000); + + return 0; } } diff --git a/src/malware/namepipe.rs b/src/malware/namepipe.rs index 6fe0178..d025143 100644 --- a/src/malware/namepipe.rs +++ b/src/malware/namepipe.rs @@ -6,12 +6,12 @@ // // Last update 20240224 +use regex_generate::{Generator, DEFAULT_MAX_REPEAT}; use windows::core::{Result as WindowsResult, PCSTR}; use windows::Win32::Foundation::{CloseHandle, HANDLE}; use windows::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX; use windows::Win32::System::Pipes::{CreateNamedPipeA, PIPE_TYPE_MESSAGE}; -use crate::malware::tools::{regex_to_string, EXIST_ALL_GOOD}; use clap::Parser; use std::{thread, time}; @@ -21,7 +21,6 @@ pub struct NamePipe { short = 'n', long, required = true, - default_value = "wag", help = "Regex of the PipeName to Create" )] name: String, @@ -50,10 +49,31 @@ fn create_name_pipe(name: &String, wait: u64) { impl NamePipe { pub fn run(&self) -> i32 { println!("Create NamePipe"); - let full_payload: String; - full_payload = regex_to_string(&self.name); - println!("Create the namepipe : {}", full_payload); - create_name_pipe(&full_payload, 2000); - return EXIST_ALL_GOOD; + + let mut generator: Generator = + match Generator::new(&self.name, rand::thread_rng(), DEFAULT_MAX_REPEAT) { + Ok(generator) => generator, + Err(_) => { + println!("Regex expressions are malformed."); + + return 1; + } + }; + let mut buffer: Vec = vec![]; + generator.generate(&mut buffer).unwrap(); + let payload: String = match String::from_utf8(buffer) { + Ok(string) => string, + Err(_) => { + println!("Filename contains non-utf8 characters."); + + return 1; + } + }; + + println!("Create the namepipe : {}", payload); + + create_name_pipe(&payload, 2000); + + return 0; } } diff --git a/src/malware/ppid.rs b/src/malware/ppid.rs index ac6eb64..11df1b8 100644 --- a/src/malware/ppid.rs +++ b/src/malware/ppid.rs @@ -6,7 +6,6 @@ // // Last update 20240224 -use crate::malware::tools::{EXIST_ALL_GOOD, EXIST_TEST_ERROR}; use clap::Parser; use rand::prelude::SliceRandom; @@ -32,6 +31,7 @@ pub struct PPID { #[clap( short = 'e', long, + required = true, help = "Full path to the executable eg: c:\\temp..." )] executable: String, @@ -131,10 +131,10 @@ impl PPID { let result: bool = create_ppid(&self.executable); if result { println!("All good "); - return EXIST_ALL_GOOD; + return 0; } else { println!("Sorry get a error"); - return EXIST_TEST_ERROR; + return 1; } } } diff --git a/src/malware/service.rs b/src/malware/service.rs index 1a928f9..ca5e589 100644 --- a/src/malware/service.rs +++ b/src/malware/service.rs @@ -6,6 +6,9 @@ // // Last update 20240224 +use crate::windows::users::is_administrator; +use clap::Parser; +use std::{thread, time}; use windows::core::{Result as WindowsResult, PCWSTR}; use windows::Win32::Security::SC_HANDLE; use windows::Win32::System::Services::{ @@ -13,17 +16,29 @@ use windows::Win32::System::Services::{ ENUM_SERVICE_TYPE, SC_MANAGER_ALL_ACCESS, SERVICE_CONTROL_STOP, SERVICE_ERROR, SERVICE_START_TYPE, SERVICE_STATUS, }; -use crate::malware::tools::{process_is_admin, EXIST_ALL_GOOD, EXIST_TEST_ERROR}; -use clap::Parser; -use std::{thread, time}; #[derive(Parser)] pub struct BYOVD { - #[clap(short = 'n', long, help = "Internal Name of the service")] + #[clap( + short = 'n', + long, + required = true, + help = "Internal Name of the service" + )] internal: String, - #[clap(short = 'd', long, help = "Displayed Name of the service")] + #[clap( + short = 'd', + long, + required = true, + help = "Displayed Name of the service" + )] display: String, - #[clap(short = 'p', long, help = "Full path to the driver eg: c:\\temp...")] + #[clap( + short = 'p', + long, + required = true, + help = "Full path to the driver eg: c:\\temp..." + )] path: String, } @@ -101,8 +116,19 @@ impl BYOVD { pub fn run(&self) -> i32 { println!("Bring Your Own Vulnerable Driver"); - if process_is_admin() == false { - return EXIST_TEST_ERROR; + if !match is_administrator() { + Ok(is_admin) => is_admin, + Err(error) => { + println!( + "Could not check if the user is an administrator or not.\nError: {}", + error + ); + + return 1; + } + } { + println!("Need to have Administrator right to create the service"); + return 1; } // Todo check path is valid or not :) @@ -110,10 +136,10 @@ impl BYOVD { let result: bool = create_driver_service(&self.internal, &self.display, &self.path); if result { println!("All good "); - return EXIST_ALL_GOOD; + return 0; } else { println!("Sorry get a error"); - return EXIST_TEST_ERROR; + return 1; } } } diff --git a/src/malware/tools.rs b/src/malware/tools.rs deleted file mode 100644 index 23d37c8..0000000 --- a/src/malware/tools.rs +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The WAG development team -// -// SPDX-License-Identifier: GPL-3.0-or-later - -pub const EXIST_ALL_GOOD: i32 = 0; -pub const EXIST_CLI_ERROR: i32 = 10; -pub const EXIST_TEST_ERROR: i32 = 11; - -use std::collections::HashSet; - -use regex_generate::{Generator, DEFAULT_MAX_REPEAT}; -use windows::Win32::UI::Shell::IsUserAnAdmin; - -pub fn hex_to_bytes(s: &str) -> Option> { - if s.len() % 2 == 0 { - (0..s.len()) - .step_by(2) - .map(|i| { - s.get(i..i + 2) - .and_then(|sub| u8::from_str_radix(sub, 16).ok()) - }) - .collect() - } else { - None - } -} - -pub fn regex_to_string(name: &String) -> String { - let mut gen: Generator = - Generator::new(name, rand::thread_rng(), DEFAULT_MAX_REPEAT).unwrap(); - let mut buffer: Vec = vec![]; - gen.generate(&mut buffer).unwrap(); - let output: String = String::from_utf8(buffer).unwrap(); - - return output; -} - -pub fn process_is_admin() -> bool { - return unsafe { IsUserAnAdmin().into() }; -} - -pub fn pretty_print_hashset(title: String, data: HashSet) { - println!("{} :", title); - println!("----------------"); - for name in data { - println!("👉 {}", name); - } - println!("----------------"); -} diff --git a/src/windows.rs b/src/windows.rs new file mode 100644 index 0000000..3ac4988 --- /dev/null +++ b/src/windows.rs @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 The WAG development team +// +// SPDX-License-Identifier: GPL-3.0-or-later + +pub mod users; diff --git a/src/windows/users.rs b/src/windows/users.rs new file mode 100644 index 0000000..a8b149f --- /dev/null +++ b/src/windows/users.rs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 The WAG development team +// +// SPDX-License-Identifier: GPL-3.0-or-later + +use windows::{ + core::Error as WindowsError, + Win32::{ + Foundation::{BOOL, PSID}, + Security::{ + AllocateAndInitializeSid, CheckTokenMembership, FreeSid, SECURITY_NT_AUTHORITY, + }, + System::SystemServices::{DOMAIN_ALIAS_RID_ADMINS, SECURITY_BUILTIN_DOMAIN_RID}, + }, +}; + +pub fn is_administrator() -> Result { + let is_admin: *mut BOOL = &mut BOOL::from(false); + let mut administrators_group: PSID = PSID::default(); + + unsafe { + AllocateAndInitializeSid( + &SECURITY_NT_AUTHORITY, + 2, + SECURITY_BUILTIN_DOMAIN_RID as u32, + DOMAIN_ALIAS_RID_ADMINS as u32, + 0, + 0, + 0, + 0, + 0, + 0, + &mut administrators_group, + )?; + + let result: Result<(), WindowsError> = + CheckTokenMembership(None, administrators_group, is_admin); + + FreeSid(administrators_group); + + result?; + } + + return Ok(unsafe { (*is_admin).into() }); +}