diff --git a/.github/scripts/ci.sh b/.github/scripts/ci.sh index 05cba03..576fe06 100755 --- a/.github/scripts/ci.sh +++ b/.github/scripts/ci.sh @@ -78,7 +78,7 @@ lint_check() { echo "[*] Agent code" pushd $AGENT_CODE &> /dev/null - local _cmd="cargo build -p genconfig && cargo clippy --color always --all-features --all-targets -- -D warnings" + local _cmd="cargo build -p genconfig && cargo clippy --workspace --color always --all-features --all-targets -- -D warnings" echo "current directory: $PWD" echo "command: $_cmd" eval $_cmd diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a91e735..2d118e2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -58,7 +58,7 @@ jobs: - name: Lint agent code run: | cargo build -p genconfig - cargo clippy --color always --all-features --all-targets -- -D warnings + cargo clippy --workspace --color always --all-features --all-targets -- -D warnings - name: Check agent code formatting run: | diff --git a/Payload_Type/thanatos/agent/Cargo.lock b/Payload_Type/thanatos/agent/Cargo.lock index ac4a9c4..082da97 100644 --- a/Payload_Type/thanatos/agent/Cargo.lock +++ b/Payload_Type/thanatos/agent/Cargo.lock @@ -44,6 +44,29 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -86,6 +109,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -103,7 +135,18 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", ] [[package]] @@ -182,7 +225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -203,6 +246,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" name = "ffiwrappers" version = "0.2.0" dependencies = [ + "bindgen", "bitflags 2.4.2", "generic-array 1.0.0", "hex-literal", @@ -264,6 +308,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.14.3" @@ -288,7 +338,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -348,12 +398,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -372,12 +444,28 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -567,6 +655,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.28" @@ -577,7 +671,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -628,6 +722,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.49" @@ -649,7 +749,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -783,7 +883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets", + "windows-targets 0.52.0", ] [[package]] @@ -792,7 +892,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -801,7 +910,22 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -810,51 +934,93 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.0" diff --git a/Payload_Type/thanatos/agent/Cargo.toml b/Payload_Type/thanatos/agent/Cargo.toml index 2df0f0f..dede30e 100644 --- a/Payload_Type/thanatos/agent/Cargo.toml +++ b/Payload_Type/thanatos/agent/Cargo.toml @@ -53,6 +53,13 @@ features = [ "Win32_System_SystemInformation", "Win32_Security_Cryptography", "Win32_System_WindowsProgramming", + "Win32_System_LibraryLoader", + "Win32_System_Threading", + "Win32_System_SystemServices", + "Wdk", + "Wdk_System", + "Wdk_System_SystemServices", + "Wdk_System_SystemInformation", ] ##### WORKSPACE SETTINGS ##### diff --git a/Payload_Type/thanatos/agent/ffiwrappers/Cargo.toml b/Payload_Type/thanatos/agent/ffiwrappers/Cargo.toml index 9341bce..99c9da8 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/Cargo.toml +++ b/Payload_Type/thanatos/agent/ffiwrappers/Cargo.toml @@ -15,3 +15,6 @@ windows.workspace = true [target.'cfg(target_os = "linux")'.dependencies] libc.workspace = true + +[build-dependencies] +bindgen = "0.69.4" diff --git a/Payload_Type/thanatos/agent/ffiwrappers/build.rs b/Payload_Type/thanatos/agent/ffiwrappers/build.rs new file mode 100644 index 0000000..e679347 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/build.rs @@ -0,0 +1,55 @@ +use std::path::{Path, PathBuf}; + +use bindgen::Bindings; + +fn generate_bindings(p: impl AsRef) -> (String, Bindings) { + let mut a = p.as_ref().ancestors().skip(1); + let includes = a.next().unwrap().to_string_lossy(); + let includes = includes.trim_start_matches(r"\\?\"); + + let bindings = bindgen::Builder::default() + .clang_arg(format!("-I{}", includes)) + .header(p.as_ref().to_string_lossy()) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .unwrap_or_else(|_| { + panic!( + "Failed to generate bindings for {}", + p.as_ref().to_string_lossy() + ) + }); + + let name = p + .as_ref() + .components() + .last() + .unwrap() + .as_os_str() + .to_string_lossy() + .to_string(); + let mut name = name.trim_end_matches(".h").to_string(); + name.push_str(".rs"); + + (name, bindings) +} + +fn main() { + let target_os = + std::env::var("CARGO_CFG_TARGET_OS").expect("Failed to get 'CARGO_CFG_TARGET_OS'"); + + let header_dir = Path::new("cffiheaders").join(target_os); + let header_dir = std::fs::read_dir(header_dir).expect("Failed to read header dir"); + + for header_path in header_dir.flatten() { + let header_path = header_path.path().canonicalize().unwrap(); + + if header_path.is_file() && header_path.to_string_lossy().ends_with(".h") { + let (name, bindings) = generate_bindings(header_path); + let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join(&name); + + bindings + .write_to_file(out_path) + .unwrap_or_else(|_| panic!("Failed to write bindings for {name}")); + } + } +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/basetsd.h b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/basetsd.h new file mode 100644 index 0000000..a8dbf33 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/basetsd.h @@ -0,0 +1,9 @@ +#pragma once + +#if defined(_WIN64) +typedef unsigned __int64 ULONG_PTR; +#else +typedef unsigned long ULONG_PTR; +#endif + +typedef ULONG_PTR SIZE_T; diff --git a/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/windef.h b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/windef.h new file mode 100644 index 0000000..31497ff --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/windef.h @@ -0,0 +1,7 @@ +#pragma once + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef unsigned long ULONG; +typedef unsigned short USHORT; diff --git a/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winnt.h b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winnt.h new file mode 100644 index 0000000..407167b --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winnt.h @@ -0,0 +1,91 @@ +#pragma once + +#include "include/basetsd.h" +#include "include/windef.h" + +#define FLS_MAXIMUM_AVAILABLE 4080 + +typedef void *PVOID; +typedef PVOID HANDLE; + +typedef BYTE BOOLEAN; + +typedef unsigned short wchar_t; +typedef wchar_t WCHAR; +typedef WCHAR *PWCHAR; +typedef WCHAR *PWSTR; + +typedef long LONG; + +#if !defined(_M_IX86) +typedef unsigned __int64 ULONGLONG; +#else +typedef double ULONGLONG; +#endif + +#if !defined(_M_IX86) +typedef __int64 LONGLONG; +#else +typedef double LONGLONG; +#endif + +typedef struct _LIST_ENTRY { + struct _LIST_ENTRY *Flink; + struct _LIST_ENTRY *Blink; +} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER, PRLIST_ENTRY; + +typedef union _LARGE_INTEGER { + struct { + DWORD LowPart; + LONG HighPart; + } DUMMYSTRUCTNAME; + struct { + DWORD LowPart; + LONG HighPart; + } u; + LONGLONG QuadPart; +} LARGE_INTEGER; + +typedef union _ULARGE_INTEGER { + struct { + DWORD LowPart; + DWORD HighPart; + } DUMMYSTRUCTNAME; + struct { + DWORD LowPart; + DWORD HighPart; + } u; + ULONGLONG QuadPart; +} ULARGE_INTEGER; + +typedef struct _RTL_CRITICAL_SECTION_DEBUG { + WORD Type; + WORD CreatorBackTraceIndex; + struct _RTL_CRITICAL_SECTION *CriticalSection; + LIST_ENTRY ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD Flags; + WORD CreatorBackTraceIndexHigh; + WORD Identifier; +} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, + *PRTL_RESOURCE_DEBUG; + +#pragma pack(push, 8) + +typedef struct _RTL_CRITICAL_SECTION { + PRTL_CRITICAL_SECTION_DEBUG DebugInfo; + + // + // The following three fields control entering and exiting the critical + // section for the resource + // + + LONG LockCount; + LONG RecursionCount; + HANDLE OwningThread; // from the thread's ClientId->UniqueThread + HANDLE LockSemaphore; + ULONG_PTR SpinCount; // force size on 64-bit systems when packed +} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION; + +#pragma pack(pop) diff --git a/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winternl.h b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winternl.h new file mode 100644 index 0000000..07f387f --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/include/winternl.h @@ -0,0 +1,10 @@ +#pragma once + +#include "include/windef.h" +#include "include/winnt.h" + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; diff --git a/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/ntdll.h b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/ntdll.h new file mode 100644 index 0000000..ca92673 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/cffiheaders/windows/ntdll.h @@ -0,0 +1,226 @@ +//! ntdll.h header file for generating C FFI Rust types. +//! header file sourced from https://github.com/x64dbg/x64dbg +//! https://github.com/x64dbg/x64dbg/blob/development/src/dbg/ntdll/ntdll.h + +#define GDI_HANDLE_BUFFER_SIZE32 34 +#define GDI_HANDLE_BUFFER_SIZE64 60 + +#include "include/basetsd.h" +#include "include/windef.h" +#include "include/winnt.h" +#include "include/winternl.h" + +#ifndef _WIN64 +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 +#else +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 +#endif + +typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; +typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; +typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; + +typedef struct _RTL_DRIVE_LETTER_CURDIR { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + UNICODE_STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _CURDIR { + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR, *PCURDIR; + +#define RTL_MAX_DRIVE_LETTERS 32 + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PWCHAR Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; + PVOID PackageDependencyData; + ULONG ProcessGroupId; + ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + HANDLE SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _PEB { + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union { + BOOLEAN BitField; + struct { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN IsPackagedProcess : 1; + BOOLEAN IsAppContainer : 1; + BOOLEAN IsProtectedProcessLight : 1; + BOOLEAN IsLongPathAwareProcess : 1; + } s1; + } u1; + + HANDLE Mutant; + + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + PVOID AtlThunkSListPtr; + PVOID IFEOKey; + union { + ULONG CrossProcessFlags; + struct { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ProcessPreviouslyThrottled : 1; + ULONG ProcessCurrentlyThrottled : 1; + ULONG ReservedBits0 : 25; + } s2; + } u2; + union { + PVOID KernelCallbackTable; + PVOID UserSharedInfoPtr; + } u3; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + PVOID ApiSetMap; + ULONG TlsExpansionCounter; + PVOID TlsBitmap; + ULONG TlsBitmapBits[2]; + + PVOID ReadOnlySharedMemoryBase; + PVOID SharedData; // HotpatchInformation + PVOID *ReadOnlyStaticServerData; + + PVOID AnsiCodePageData; // PCPTABLEINFO + PVOID OemCodePageData; // PCPTABLEINFO + PVOID UnicodeCaseTableData; // PNLSTABLEINFO + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + SIZE_T HeapSegmentReserve; + SIZE_T HeapSegmentCommit; + SIZE_T HeapDeCommitTotalFreeThreshold; + SIZE_T HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + PVOID *ProcessHeaps; // PHEAP + + PVOID GdiSharedHandleTable; + PVOID ProcessStarterHelper; + ULONG GdiDCAttributeList; + + PRTL_CRITICAL_SECTION LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + ULONG_PTR ActiveProcessAffinityMask; + GDI_HANDLE_BUFFER GdiHandleBuffer; + PVOID PostProcessInitRoutine; + + PVOID TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + PVOID pShimData; + PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA + + UNICODE_STRING CSDVersion; + + PVOID ActivationContextData; // ACTIVATION_CONTEXT_DATA + PVOID ProcessAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP + PVOID SystemDefaultActivationContextData; // ACTIVATION_CONTEXT_DATA + PVOID SystemAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP + + SIZE_T MinimumStackCommit; + + PVOID *FlsCallback; + LIST_ENTRY FlsListHead; + PVOID FlsBitmap; + ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + ULONG FlsHighIndex; + + PVOID WerRegistrationData; + PVOID WerShipAssertPtr; + PVOID pUnused; // pContextData + PVOID pImageHeaderHash; + union { + ULONG TracingFlags; + struct { + ULONG HeapTracingEnabled : 1; + ULONG CritSecTracingEnabled : 1; + ULONG LibLoaderTracingEnabled : 1; + ULONG SpareTracingBits : 29; + } s3; + } u4; + ULONGLONG CsrServerReadOnlySharedMemoryBase; + PVOID TppWorkerpListLock; + LIST_ENTRY TppWorkerpList; + PVOID WaitOnAddressHashTable[128]; + PVOID TelemetryCoverageHeader; // REDSTONE3 + ULONG CloudFileFlags; +} PEB, *PPEB; diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/errors.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/errors.rs index 523b77a..0e9652e 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/errors.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/errors.rs @@ -8,6 +8,7 @@ pub enum FfiError { GaiError(EaiError), NonNullPointer, CanonNameNotFound, + StringParseError, #[cfg(target_os = "linux")] NoGroupMembership, @@ -23,6 +24,17 @@ impl FfiError { pub fn from_windows_error(e: windows::core::Error) -> Self { Self::OsError(e.code().0) } + + #[cfg(target_os = "windows")] + pub fn os_error() -> Self { + use windows::Win32::Foundation::GetLastError; + + if let Err(e) = unsafe { GetLastError() } { + Self::from_windows_error(e) + } else { + Self::OsError(0) + } + } } #[derive(Debug)] diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/lib.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/lib.rs index 2a13739..b12321e 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/lib.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/lib.rs @@ -7,3 +7,7 @@ pub mod linux; #[cfg(target_os = "windows")] pub mod windows; + +mod internal { + pub trait SealedTrait {} +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/alghandle.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/alghandle.rs index be89eed..1c104e8 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/alghandle.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/alghandle.rs @@ -17,14 +17,14 @@ use super::{ }; #[repr(transparent)] -pub struct BCryptAlgHandle { +pub struct BCryptAlgHandle { handle: BCRYPT_ALG_HANDLE, _marker: PhantomData, - _algorithm: PhantomData, + _algorithm: PhantomData, } -impl BCryptAlgHandle { - pub fn new() -> BCryptAlgHandle { +impl BCryptAlgHandle { + pub fn new() -> BCryptAlgHandle { let mut handle = BCRYPT_ALG_HANDLE::default(); // Possible return/error values are documented here: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider#return-value @@ -52,7 +52,7 @@ impl BCryptAlgHandle { match unsafe { BCryptOpenAlgorithmProvider( &mut handle, - T::ALGID, + A::ALG, MS_PRIMITIVE_PROVIDER, BCRYPT_OPEN_ALGORITHM_PROVIDER_FLAGS(0), ) @@ -74,14 +74,14 @@ impl BCryptAlgHandle { } } -impl Default for BCryptAlgHandle { +impl Default for BCryptAlgHandle { fn default() -> Self { Self::new() } } -impl BCryptAlgHandle { - pub fn create_hash(&mut self) -> BCryptHashHandle { +impl BCryptAlgHandle { + pub fn create_hash(&mut self) -> BCryptHashHandle { let mut hash_handle = BCRYPT_HASH_HANDLE::default(); // Possible return/error values are documented here: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptcreatehash#return-value @@ -130,7 +130,7 @@ impl BCryptAlgHandle { } } -impl Drop for BCryptAlgHandle { +impl Drop for BCryptAlgHandle { fn drop(&mut self) { let _ = unsafe { BCryptCloseAlgorithmProvider(self.handle, 0) }; } diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/algorithms.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/algorithms.rs index b9e90e4..a631d76 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/algorithms.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/algorithms.rs @@ -4,12 +4,12 @@ use windows::{core::PCWSTR, Win32::Security::Cryptography::BCRYPT_SHA256_ALGORIT pub struct Sha256; -impl super::internal::Private for Sha256 {} +impl crate::internal::SealedTrait for Sha256 {} impl HashAlgorithm for Sha256 { type LEN = U32; } impl Algorithm for Sha256 { - const ALGID: PCWSTR = BCRYPT_SHA256_ALGORITHM; + const ALG: PCWSTR = BCRYPT_SHA256_ALGORITHM; } diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/hash.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/hash.rs index 43dedc7..5e89e29 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/hash.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/hash.rs @@ -8,15 +8,15 @@ use windows::Win32::Security::Cryptography::{ use super::{BCryptAlgHandle, HashAlgorithm}; #[repr(transparent)] -pub struct BCryptHashHandle { +pub struct BCryptHashHandle { pub(super) handle: BCRYPT_HASH_HANDLE, pub(super) _marker: PhantomData, - pub(super) _algorithm: PhantomData, + pub(super) _algorithm: PhantomData, } -impl BCryptHashHandle { - pub fn new() -> BCryptHashHandle { - let mut alg_handle = BCryptAlgHandle::::new(); +impl BCryptHashHandle { + pub fn new() -> BCryptHashHandle { + let mut alg_handle = BCryptAlgHandle::::new(); alg_handle.create_hash() } @@ -36,8 +36,8 @@ impl BCryptHashHandle { let _ = unsafe { BCryptHashData(self.handle, data, 0) }; } - pub fn finish_hash(self) -> GenericArray { - let mut output: GenericArray = Default::default(); + pub fn finish_hash(self) -> GenericArray { + let mut output: GenericArray = Default::default(); // Possible return/error values are documented here: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptfinishhash // Error assertions: @@ -57,13 +57,13 @@ impl BCryptHashHandle { } } -impl Default for BCryptHashHandle { +impl Default for BCryptHashHandle { fn default() -> Self { Self::new() } } -impl Drop for BCryptHashHandle { +impl Drop for BCryptHashHandle { fn drop(&mut self) { let _ = unsafe { BCryptDestroyHash(self.handle) }; } @@ -75,7 +75,7 @@ mod tests { use super::BCryptHashHandle; - const WORD: &'static str = "hello"; + const WORD: &str = "hello"; const EXPECTED: [u8; 32] = hex_literal::hex!("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/mod.rs index fcb9bfb..fdfc4a7 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/mod.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/bcrypt/mod.rs @@ -9,18 +9,14 @@ pub use hash::BCryptHashHandle; pub mod algorithms; -mod internal { - pub trait Private {} -} - -pub trait Algorithm: internal::Private { - const ALGID: PCWSTR; +pub trait Algorithm: crate::internal::SealedTrait { + const ALG: PCWSTR; } // So until generic constant expressions are stabilized, unfortunately have to use `generic_array`. // Each hash algorithm length is a type instead of a constant. This is rather annoying // since it requires using the numeric type mappings from typenum. // Please Rust stabilize generic const exprs, thanks :) -pub trait HashAlgorithm: Algorithm + internal::Private { +pub trait HashAlgorithm: Algorithm + crate::internal::SealedTrait { type LEN: ArrayLength; } diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/cffiheaders/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/cffiheaders/mod.rs new file mode 100644 index 0000000..831553f --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/cffiheaders/mod.rs @@ -0,0 +1,5 @@ +pub mod ntdll { + #![allow(unused, non_snake_case, non_camel_case_types, non_upper_case_globals)] + #![allow(clippy::all)] + include!(concat!(env!("OUT_DIR"), "/ntdll.rs")); +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/mod.rs index 43808d3..4dd3a25 100644 --- a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/mod.rs +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/mod.rs @@ -1,3 +1,10 @@ +use windows::Win32::{ + Foundation::{HMODULE, MAX_PATH}, + System::LibraryLoader::GetModuleFileNameA, +}; + +use crate::errors::FfiError; + mod hostname; pub use hostname::hostname; @@ -8,3 +15,22 @@ mod domain; pub use domain::domain; pub mod bcrypt; +mod cffiheaders; +pub mod peb; +pub mod processthreadsapi; +pub mod sysinfoapi; +pub mod winnt; + +pub fn process_name() -> Result { + let mut process_path = [0u8; MAX_PATH as usize]; + let path_len = unsafe { GetModuleFileNameA(HMODULE(0), &mut process_path) }; + + let process_path = std::str::from_utf8(&process_path[..path_len as usize]) + .map_err(|_| FfiError::StringParseError)?; + + process_path + .split('\\') + .last() + .ok_or(FfiError::StringParseError) + .map(|s| s.to_string()) +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/peb.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/peb.rs new file mode 100644 index 0000000..a335ca9 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/peb.rs @@ -0,0 +1,43 @@ +use super::cffiheaders::ntdll; + +pub struct Peb(&'static ntdll::PEB); + +#[allow(unused_assignments)] +fn current_peb() -> *const ntdll::PEB { + let mut ppeb: *const ntdll::PEB = std::ptr::null(); + #[cfg(target_arch = "x86_64")] + unsafe { + std::arch::asm!("mov {x}, gs:[0x60]", x = out(reg) ppeb) + }; + + ppeb +} + +impl Peb { + pub fn new() -> Peb { + let ppeb = current_peb(); + Peb(unsafe { &*ppeb }) + } + + pub const fn os_major_version(&self) -> u32 { + self.0.OSMajorVersion + } + + pub const fn os_minor_version(&self) -> u32 { + self.0.OSMinorVersion + } + + pub const fn os_build_number(&self) -> u16 { + self.0.OSBuildNumber + } + + pub const fn os_platform_id(&self) -> u32 { + self.0.OSPlatformId + } +} + +impl Default for Peb { + fn default() -> Self { + Self::new() + } +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/mod.rs new file mode 100644 index 0000000..13d098a --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/mod.rs @@ -0,0 +1,7 @@ +pub mod process; +pub mod token; + +pub mod prelude { + pub use super::process::traits::*; + pub use super::token::traits::*; +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/access.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/access.rs new file mode 100644 index 0000000..c3f8e33 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/access.rs @@ -0,0 +1,29 @@ +use windows::Win32::System::Threading::{ + PROCESS_ACCESS_RIGHTS, PROCESS_ALL_ACCESS, PROCESS_CREATE_PROCESS, PROCESS_CREATE_THREAD, + PROCESS_DUP_HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, +}; + +use super::traits::*; + +macro_rules! impl_access_rights { + ($s:ident, $rights:ident) => { + pub struct $s; + impl $crate::internal::SealedTrait for $s {} + impl ProcessAccessRights for $s { + const RIGHTS: PROCESS_ACCESS_RIGHTS = $rights; + } + }; +} + +impl_access_rights!(ProcessAllAccess, PROCESS_ALL_ACCESS); +impl_access_rights!(ProcessCreateProcess, PROCESS_CREATE_PROCESS); +impl_access_rights!(ProcessCreateThread, PROCESS_CREATE_THREAD); +impl_access_rights!(ProcessDupHandle, PROCESS_DUP_HANDLE); +impl_access_rights!(ProcessQueryInformation, PROCESS_QUERY_INFORMATION); +impl_access_rights!( + ProcessQueryLimitedInformation, + PROCESS_QUERY_LIMITED_INFORMATION +); + +impl ProcessQueryTokenRights for ProcessAllAccess {} +impl ProcessQueryTokenRights for ProcessQueryInformation {} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/mod.rs new file mode 100644 index 0000000..4184e51 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/process/mod.rs @@ -0,0 +1,86 @@ +pub mod access; + +use crate::errors::FfiError; + +use self::access::ProcessAllAccess; + +use super::token::{traits::TokenAccessRights, CurrentToken, Token, TokenInner}; +use std::marker::PhantomData; + +use traits::*; +use windows::Win32::{ + Foundation::{CloseHandle, HANDLE}, + System::Threading::{OpenProcess, OpenProcessToken}, +}; + +pub mod traits { + use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS; + + pub trait ProcessAccessRights: crate::internal::SealedTrait { + const RIGHTS: PROCESS_ACCESS_RIGHTS; + } + + pub trait ProcessQueryTokenRights: ProcessAccessRights {} +} + +#[repr(transparent)] +struct ProcessInner { + handle: HANDLE, + _access: PhantomData, +} + +#[repr(transparent)] +pub struct CurrentProcess(ProcessInner); + +impl CurrentProcess { + pub const fn new() -> CurrentProcess { + CurrentProcess(ProcessInner { + handle: HANDLE(-1), + _access: PhantomData, + }) + } + pub const fn token(&self) -> CurrentToken { + CurrentToken::new() + } +} + +#[repr(transparent)] +pub struct Process(ProcessInner); + +impl Process { + pub fn open(pid: u32) -> Result, FfiError> { + Ok(Self(ProcessInner { + handle: unsafe { + OpenProcess(AccessRights::RIGHTS, false, pid) + .map_err(FfiError::from_windows_error)? + }, + _access: PhantomData, + })) + } +} + +impl Process { + pub const fn current_process() -> CurrentProcess { + CurrentProcess::new() + } +} + +impl Process { + pub fn token(&self) -> Result, FfiError> { + let mut token_handle = HANDLE(0); + + unsafe { OpenProcessToken(self.0.handle, TokenRights::MASK, &mut token_handle) } + .map_err(FfiError::from_windows_error)?; + + Ok(Token(TokenInner { + handle: token_handle, + _access: PhantomData, + })) + } +} + +impl Drop for Process { + fn drop(&mut self) { + let _ = unsafe { CloseHandle(self.0.handle) }; + } +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/access.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/access.rs new file mode 100644 index 0000000..c80ab52 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/access.rs @@ -0,0 +1,25 @@ +use super::traits::*; +use windows::Win32::Security::{TOKEN_ACCESS_MASK, TOKEN_QUERY, TOKEN_QUERY_SOURCE}; + +macro_rules! impl_access_rights { + ($s:ident, $rights:ident) => { + pub struct $s; + impl $crate::internal::SealedTrait for $s {} + impl TokenAccessRights for $s { + const MASK: windows::Win32::Security::TOKEN_ACCESS_MASK = $rights; + } + }; +} + +impl_access_rights!(TokenQuery, TOKEN_QUERY); +impl TokenQueryRights for TokenQuery {} +impl_access_rights!(TokenQuerySource, TOKEN_QUERY_SOURCE); +impl TokenQuerySourceRights for TokenQuerySource {} + +pub struct TokenCurrent; +impl crate::internal::SealedTrait for TokenCurrent {} +impl TokenAccessRights for TokenCurrent { + const MASK: TOKEN_ACCESS_MASK = TOKEN_ACCESS_MASK(TOKEN_QUERY.0 | TOKEN_QUERY_SOURCE.0); +} +impl TokenQueryRights for TokenCurrent {} +impl TokenQuerySourceRights for TokenCurrent {} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/mod.rs new file mode 100644 index 0000000..509b8c1 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/processthreadsapi/token/mod.rs @@ -0,0 +1,118 @@ +pub mod access; + +use std::marker::PhantomData; + +use access::TokenCurrent; +use windows::{ + core::HRESULT, + Win32::{ + Foundation::HANDLE, + Security::{GetTokenInformation, TokenIntegrityLevel}, + }, +}; + +use traits::*; + +use crate::{errors::FfiError, windows::winnt::SidAndAttributes}; + +pub mod traits { + use crate::internal::SealedTrait; + use windows::Win32::Security::TOKEN_ACCESS_MASK; + + pub trait TokenAccessRights: SealedTrait { + const MASK: TOKEN_ACCESS_MASK; + } + + pub trait TokenQueryRights: TokenAccessRights {} + pub trait TokenQuerySourceRights: TokenAccessRights {} +} + +#[repr(transparent)] +pub(crate) struct TokenInner { + pub handle: HANDLE, + pub _access: PhantomData, +} + +#[repr(transparent)] +pub struct CurrentToken(TokenInner); + +#[repr(transparent)] +pub struct Token(pub(crate) TokenInner); + +impl CurrentToken { + pub const fn new() -> CurrentToken { + CurrentToken(TokenInner { + handle: HANDLE(-4), + _access: PhantomData, + }) + } + + pub fn integrity_level(&self) -> Result { + self.0.integrity_level() + } +} + +impl Token { + pub const fn current_token() -> CurrentToken { + CurrentToken::new() + } +} + +impl Token { + pub fn integrity_level(&self) -> Result { + self.0.integrity_level() + } +} + +impl TokenInner { + pub fn integrity_level(&self) -> Result { + let mut token_info_length = 0u32; + + match unsafe { + GetTokenInformation( + self.handle, + TokenIntegrityLevel, + None, + token_info_length, + &mut token_info_length, + ) + } { + Err(e) if e.code() == HRESULT(0x8007007Au32 as i32) => (), + Err(e) => return Err(FfiError::from_windows_error(e)), + _ => unreachable!(), + }; + + let mut token_information = vec![0u8; token_info_length as usize]; + + unsafe { + GetTokenInformation( + self.handle, + TokenIntegrityLevel, + Some(token_information.as_mut_ptr().cast()), + token_information.len() as u32, + &mut token_info_length, + ) + } + .map_err(FfiError::from_windows_error)?; + + Ok(SidAndAttributes(token_information)) + } +} + +#[cfg(test)] +mod tests { + use windows::Win32::Security::SID; + + use super::CurrentToken; + + #[test] + fn test_integrity() { + let token = CurrentToken::new(); + let integrity = token.integrity_level().unwrap(); + + let sid = integrity.sid(); + + let psid = sid.sid.as_ptr() as *const SID; + unsafe { dbg!(*psid) }; + } +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/sysinfoapi.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/sysinfoapi.rs new file mode 100644 index 0000000..2188a30 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/sysinfoapi.rs @@ -0,0 +1,216 @@ +use std::{ffi::c_void, mem::MaybeUninit}; + +use windows::{ + Wdk::System::SystemServices::RtlGetVersion, + Win32::System::{ + SystemInformation::{ + GetNativeSystemInfo, GetProductInfo, OSVERSIONINFOEXW, OSVERSIONINFOW, OS_PRODUCT_TYPE, + PROCESSOR_ARCHITECTURE, PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_ARM, + PROCESSOR_ARCHITECTURE_ARM64, PROCESSOR_ARCHITECTURE_IA64, + PROCESSOR_ARCHITECTURE_INTEL, PRODUCT_CORE, PRODUCT_DATACENTER_EVALUATION_SERVER, + PRODUCT_DATACENTER_SERVER, PRODUCT_DATACENTER_SERVER_CORE, + PRODUCT_DATACENTER_SERVER_CORE_V, PRODUCT_DATACENTER_SERVER_V, PRODUCT_EDUCATION, + PRODUCT_ENTERPRISE, PRODUCT_ENTERPRISE_EVALUATION, PRODUCT_ENTERPRISE_SERVER, + PRODUCT_ENTERPRISE_SERVER_CORE, PRODUCT_ENTERPRISE_SERVER_CORE_V, + PRODUCT_ENTERPRISE_SERVER_V, PRODUCT_HOME_BASIC, PRODUCT_PROFESSIONAL, + PRODUCT_PRO_WORKSTATION, PRODUCT_STANDARD_EVALUATION_SERVER, PRODUCT_STANDARD_SERVER, + PRODUCT_STANDARD_SERVER_CORE_V, PRODUCT_STANDARD_SERVER_V, SYSTEM_INFO, + }, + SystemServices::{PRODUCT_PRO_FOR_EDUCATION, PRODUCT_STANDARD_SERVER_CORE}, + }, +}; + +#[repr(u16)] +pub enum ProcessorArchitecture { + Amd64 = PROCESSOR_ARCHITECTURE_AMD64.0, + Arm = PROCESSOR_ARCHITECTURE_ARM.0, + Arm64 = PROCESSOR_ARCHITECTURE_ARM64.0, + Ia64 = PROCESSOR_ARCHITECTURE_IA64.0, + Intel = PROCESSOR_ARCHITECTURE_INTEL.0, + Other(PROCESSOR_ARCHITECTURE), +} + +impl ProcessorArchitecture { + pub const fn from_value(value: u16) -> ProcessorArchitecture { + match PROCESSOR_ARCHITECTURE(value) { + PROCESSOR_ARCHITECTURE_AMD64 => Self::Amd64, + PROCESSOR_ARCHITECTURE_ARM => Self::Arm, + PROCESSOR_ARCHITECTURE_ARM64 => Self::Arm64, + PROCESSOR_ARCHITECTURE_IA64 => Self::Ia64, + PROCESSOR_ARCHITECTURE_INTEL => Self::Intel, + o => Self::Other(o), + } + } +} + +#[derive(Debug)] +#[repr(u32)] +pub enum ProductType { + Other(u32), + Core = PRODUCT_CORE.0, + Professional = PRODUCT_PROFESSIONAL.0, + Education = PRODUCT_EDUCATION.0, + Enterprise = PRODUCT_ENTERPRISE.0, + ProWorkstation = PRODUCT_PRO_WORKSTATION.0, + ProForEducation = PRODUCT_PRO_FOR_EDUCATION, + EnterpriseEvaluation = PRODUCT_ENTERPRISE_EVALUATION.0, + DatacenterServer = PRODUCT_DATACENTER_SERVER.0, + DatacenterEvaluationServer = PRODUCT_DATACENTER_EVALUATION_SERVER.0, + DatacenterServerCore = PRODUCT_DATACENTER_SERVER_CORE.0, + DatacenterServerCoreV = PRODUCT_DATACENTER_SERVER_CORE_V.0, + DatacenterServerV = PRODUCT_DATACENTER_SERVER_V.0, + EnterpriseServer = PRODUCT_ENTERPRISE_SERVER.0, + EnterpriseServerCore = PRODUCT_ENTERPRISE_SERVER_CORE.0, + EnterpriseServerCoreV = PRODUCT_ENTERPRISE_SERVER_CORE_V.0, + EnterpriseServerV = PRODUCT_ENTERPRISE_SERVER_V.0, + HomeBasic = PRODUCT_HOME_BASIC.0, + StandardServer = PRODUCT_STANDARD_SERVER.0, + StandardEvaluationServer = PRODUCT_STANDARD_EVALUATION_SERVER.0, + StandardServerCore = PRODUCT_STANDARD_SERVER_CORE, + StandardServerCoreV = PRODUCT_STANDARD_SERVER_CORE_V.0, + StandardServerV = PRODUCT_STANDARD_SERVER_V.0, +} + +impl ProductType { + pub const fn from_value(value: OS_PRODUCT_TYPE) -> ProductType { + match value { + PRODUCT_CORE => Self::Core, + PRODUCT_DATACENTER_EVALUATION_SERVER => Self::DatacenterEvaluationServer, + PRODUCT_DATACENTER_SERVER => Self::DatacenterServer, + PRODUCT_DATACENTER_SERVER_CORE => Self::DatacenterServerCore, + PRODUCT_DATACENTER_SERVER_CORE_V => Self::DatacenterServerCoreV, + PRODUCT_DATACENTER_SERVER_V => Self::DatacenterServerV, + PRODUCT_EDUCATION => Self::Education, + PRODUCT_ENTERPRISE => Self::Enterprise, + PRODUCT_ENTERPRISE_EVALUATION => Self::EnterpriseEvaluation, + PRODUCT_ENTERPRISE_SERVER => Self::EnterpriseServer, + PRODUCT_ENTERPRISE_SERVER_CORE => Self::EnterpriseServerCore, + PRODUCT_ENTERPRISE_SERVER_CORE_V => Self::EnterpriseServerCoreV, + PRODUCT_ENTERPRISE_SERVER_V => Self::EnterpriseServerV, + PRODUCT_HOME_BASIC => Self::HomeBasic, + OS_PRODUCT_TYPE(PRODUCT_PRO_FOR_EDUCATION) => Self::ProForEducation, + PRODUCT_PRO_WORKSTATION => Self::ProWorkstation, + PRODUCT_PROFESSIONAL => Self::Professional, + PRODUCT_STANDARD_SERVER => Self::StandardServer, + PRODUCT_STANDARD_EVALUATION_SERVER => Self::StandardEvaluationServer, + OS_PRODUCT_TYPE(PRODUCT_STANDARD_SERVER_CORE) => Self::StandardServerCore, + PRODUCT_STANDARD_SERVER_CORE_V => Self::StandardServerCoreV, + PRODUCT_STANDARD_SERVER_V => Self::StandardServerV, + _ => Self::Other(value.0), + } + } +} + +#[repr(transparent)] +pub struct SystemInfo(SYSTEM_INFO); + +impl SystemInfo { + pub fn new() -> SystemInfo { + let mut info: MaybeUninit = MaybeUninit::zeroed(); + unsafe { GetNativeSystemInfo(info.as_mut_ptr()) }; + Self(unsafe { info.assume_init() }) + } + + pub const fn processor_architecture(&self) -> ProcessorArchitecture { + ProcessorArchitecture::from_value(unsafe { + self.0.Anonymous.Anonymous.wProcessorArchitecture.0 + }) + } + + pub const fn page_size(&self) -> u32 { + self.0.dwPageSize + } + + pub const fn minimum_application_address(&self) -> *mut c_void { + self.0.lpMinimumApplicationAddress + } + + pub const fn maximum_application_address(&self) -> *mut c_void { + self.0.lpMaximumApplicationAddress + } + + pub const fn active_processor_mask(&self) -> usize { + self.0.dwActiveProcessorMask + } + + pub const fn number_of_processors(&self) -> u32 { + self.0.dwNumberOfProcessors + } + + pub const fn processor_type(&self) -> u32 { + self.0.dwProcessorType + } + + pub const fn allocation_granularity(&self) -> u32 { + self.0.dwAllocationGranularity + } + + pub const fn processor_level(&self) -> u32 { + self.0.dwAllocationGranularity + } + + pub const fn processor_revision(&self) -> u16 { + self.0.wProcessorRevision + } +} + +impl Default for SystemInfo { + fn default() -> Self { + Self::new() + } +} + +#[repr(transparent)] +pub struct OsVersionInfo(OSVERSIONINFOEXW); + +impl OsVersionInfo { + pub fn new() -> OsVersionInfo { + let mut version_buffer = OSVERSIONINFOEXW::default(); + unsafe { + RtlGetVersion(&mut version_buffer as *mut OSVERSIONINFOEXW as *mut OSVERSIONINFOW) + }; + OsVersionInfo(version_buffer) + } + + pub const fn major_version(&self) -> u32 { + self.0.dwMajorVersion + } + + pub const fn minor_version(&self) -> u32 { + self.0.dwMinorVersion + } + + pub const fn build_number(&self) -> u32 { + self.0.dwBuildNumber + } + + pub const fn service_pack_major(&self) -> u16 { + self.0.wServicePackMajor + } + + pub const fn service_pack_minor(&self) -> u16 { + self.0.wServicePackMinor + } + + pub fn product_type(&self) -> ProductType { + let mut returned_product_type = OS_PRODUCT_TYPE(0u32); + + let _ = unsafe { + GetProductInfo( + self.major_version(), + self.minor_version(), + self.service_pack_major() as u32, + self.service_pack_minor() as u32, + &mut returned_product_type, + ) + }; + + ProductType::from_value(returned_product_type) + } +} + +impl Default for OsVersionInfo { + fn default() -> Self { + Self::new() + } +} diff --git a/Payload_Type/thanatos/agent/ffiwrappers/src/windows/winnt/mod.rs b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/winnt/mod.rs new file mode 100644 index 0000000..2d15b11 --- /dev/null +++ b/Payload_Type/thanatos/agent/ffiwrappers/src/windows/winnt/mod.rs @@ -0,0 +1,37 @@ +use std::{marker::PhantomData, ptr::NonNull}; + +use windows::Win32::Security::{SID, SID_AND_ATTRIBUTES}; + +pub struct SidAndAttributes(pub(crate) Vec); + +impl SidAndAttributes { + pub fn sid(&self) -> SidRef<'_> { + let sid_and_attributes: *const SID_AND_ATTRIBUTES = self.0.as_ptr().cast(); + + SidRef { + sid: unsafe { + NonNull::new((*sid_and_attributes).Sid.0 as *mut SID).unwrap_unchecked() + }, + _marker: PhantomData, + } + } +} + +#[repr(transparent)] +pub struct SidRef<'a> { + pub(crate) sid: NonNull, + _marker: PhantomData<&'a SID>, +} + +impl SidRef<'_> { + pub const fn identifier_authority(&self) -> [u8; 6] { + unsafe { self.sid.as_ref().IdentifierAuthority.Value } + } + + pub const fn subauthorities(&self) -> &[u32] { + let subauthority_count = unsafe { self.sid.as_ref().SubAuthorityCount } as usize; + unsafe { + std::slice::from_raw_parts(self.sid.as_ref().SubAuthority.as_ptr(), subauthority_count) + } + } +} diff --git a/Payload_Type/thanatos/agent/thanatos/src/guardrails.rs b/Payload_Type/thanatos/agent/thanatos/src/guardrails.rs index 7d54b40..9c22a60 100644 --- a/Payload_Type/thanatos/agent/thanatos/src/guardrails.rs +++ b/Payload_Type/thanatos/agent/thanatos/src/guardrails.rs @@ -1,10 +1,6 @@ #![allow(unused)] -#[cfg(target_os = "linux")] -use crate::native::linux::system; - -#[cfg(target_os = "windows")] -use crate::native::windows::system; +use crate::native::system; #[cfg(feature = "crypto-system")] use cryptolib::hash::system::Sha256; diff --git a/Payload_Type/thanatos/agent/thanatos/src/lib.rs b/Payload_Type/thanatos/agent/thanatos/src/lib.rs index 15a49c2..a956e5e 100644 --- a/Payload_Type/thanatos/agent/thanatos/src/lib.rs +++ b/Payload_Type/thanatos/agent/thanatos/src/lib.rs @@ -7,6 +7,7 @@ mod agent; mod guardrails; mod logging; mod native; +mod os; mod proto; pub fn entrypoint() { diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/checkininfo.rs b/Payload_Type/thanatos/agent/thanatos/src/native/checkininfo.rs index 2fd58e2..4de5e43 100644 --- a/Payload_Type/thanatos/agent/thanatos/src/native/checkininfo.rs +++ b/Payload_Type/thanatos/agent/thanatos/src/native/checkininfo.rs @@ -1,12 +1,10 @@ -use crate::proto::checkin::CheckinInfo; - -#[cfg(target_os = "linux")] -use crate::native::linux::system; +use crate::native::system; +use crate::proto::checkin::{checkin_info::PlatformInfo, CheckinInfo}; #[cfg(target_os = "windows")] -use crate::native::windows::system; - pub fn get_checkininfo(uuid: String) -> CheckinInfo { + use crate::proto::checkin::WindowsInfo; + CheckinInfo { uuid, user: system::username().ok(), @@ -14,8 +12,11 @@ pub fn get_checkininfo(uuid: String) -> CheckinInfo { domain: system::domain().ok(), pid: Some(std::process::id()), architecture: system::architecture().into(), - platform: system::platform(), - integrity_level: system::integrity_level().ok(), + platform_info: Some(PlatformInfo::Windows(WindowsInfo { + build: system::build_number(), + product: Some(system::product()), + })), + integrity_level: system::integrity_level(), process_name: system::process_name().ok(), ips: Vec::new(), } diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/linux/mod.rs b/Payload_Type/thanatos/agent/thanatos/src/native/linux/mod.rs deleted file mode 100644 index ac77f63..0000000 --- a/Payload_Type/thanatos/agent/thanatos/src/native/linux/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod system; diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/mod.rs b/Payload_Type/thanatos/agent/thanatos/src/native/mod.rs index d360608..64260fc 100644 --- a/Payload_Type/thanatos/agent/thanatos/src/native/mod.rs +++ b/Payload_Type/thanatos/agent/thanatos/src/native/mod.rs @@ -1,7 +1,2 @@ -#[cfg(target_os = "linux")] -pub mod linux; - -#[cfg(target_os = "windows")] -pub mod windows; - pub mod checkininfo; +pub mod system; diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/system.rs b/Payload_Type/thanatos/agent/thanatos/src/native/system.rs new file mode 100644 index 0000000..4e18cf9 --- /dev/null +++ b/Payload_Type/thanatos/agent/thanatos/src/native/system.rs @@ -0,0 +1,31 @@ +#[cfg(target_os = "windows")] +pub use crate::os::windows::{ + build_number, domain, hostname, integrity_level, process_name, product, username, +}; + +#[cfg(target_os = "linux")] +pub use crate::os::linux::{ + domain, hostname, integrity_level, os_release, platform, process_name, username, +}; + +use crate::proto::checkin::Architecture; + +pub fn architecture() -> Architecture { + #[cfg(target_arch = "x86_64")] + let mut arch = Architecture::X8664; + + #[cfg(target_arch = "x86")] + let mut arch = Architecture::X86; + + #[cfg(target_os = "linux")] + if let Some(new_arch) = crate::os::linux::architecture() { + arch = new_arch; + } + + #[cfg(target_os = "windows")] + if let Some(new_arch) = crate::os::windows::architecture() { + arch = new_arch; + } + + arch +} diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/windows/mod.rs b/Payload_Type/thanatos/agent/thanatos/src/native/windows/mod.rs deleted file mode 100644 index 018a515..0000000 --- a/Payload_Type/thanatos/agent/thanatos/src/native/windows/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod system { - pub use ffiwrappers::windows::domain; - pub use ffiwrappers::windows::hostname; - pub use ffiwrappers::windows::username; -} diff --git a/Payload_Type/thanatos/agent/thanatos/src/native/linux/system.rs b/Payload_Type/thanatos/agent/thanatos/src/os/linux/mod.rs similarity index 66% rename from Payload_Type/thanatos/agent/thanatos/src/native/linux/system.rs rename to Payload_Type/thanatos/agent/thanatos/src/os/linux/mod.rs index 3a8dd3a..2dea67d 100644 --- a/Payload_Type/thanatos/agent/thanatos/src/native/linux/system.rs +++ b/Payload_Type/thanatos/agent/thanatos/src/os/linux/mod.rs @@ -14,14 +14,10 @@ use ffiwrappers::{ }, }; -use crate::proto::checkin::Architecture; +mod platform; +pub use platform::platform; -#[derive(Default, Debug)] -pub struct OsReleaseInfo { - name: String, - version: String, - pretty_name: Option, -} +use crate::proto::checkin::Architecture; pub fn hostname() -> Result { let h = ffiwrappers::linux::gethostname().map_err(ThanatosError::FFIError)?; @@ -58,42 +54,6 @@ pub fn domain() -> Result { Ok(s.collect::>().join(".")) } -// TODO: Make this return an enum value for the container environment and return -// it as a separate field in the initial check in -pub fn check_container_environment() -> Option<&'static str> { - if let Ok(readdir) = std::fs::read_dir("/") { - for entry in readdir.flatten() { - if entry.file_name() == ".dockerenv" { - return Some("Docker"); - } - } - } - - if let Ok(readdir) = std::fs::read_dir("/run") { - for entry in readdir.flatten() { - if entry.file_name() == ".containerenv" { - return Some("Container"); - } - } - } - - None -} - -// TODO: Return this into a separate initial check in field. -// Parse /proc/self/mountinfo for selinux detection instead of looking for /sys/fs/selinux -pub fn check_selinux() -> bool { - if let Ok(readdir) = std::fs::read_dir("/sys/fs") { - for entry in readdir.flatten() { - if entry.file_name() == "selinux" { - return true; - } - } - } - - false -} - pub fn os_release() -> Result { let f = std::fs::File::open("/etc/os-release").map_err(ThanatosError::IoError)?; let reader = BufReader::new(f); @@ -131,58 +91,12 @@ pub fn os_release() -> Result { Ok(release_info) } -// TODO: Split up platform values into separate check in fields and create the platform -// string server side. Also grab the architecture from the initial check in instead -// of embedding it into this string -pub fn platform() -> String { - let distro = os_release() - .map(|os_info| { - os_info - .pretty_name - .unwrap_or_else(|| format!("{} {}", os_info.name, os_info.version)) - }) - .unwrap_or_else(|_| "Linux".to_string()); - - let utsname = uname::UtsName::new(); - - let mut platform_name = match utsname { - Ok(utsname) => format!( - "{} kernel {} {}", - distro, - utsname.release(), - utsname.machine() - ) - .to_string(), - Err(_) => distro, - }; - - if check_selinux() { - platform_name.push_str(" (SELinux)"); - } - - if let Some(runtime) = check_container_environment() { - platform_name.push_str(&format!(" ({runtime})")); - } - - platform_name -} - -pub fn architecture() -> Architecture { - #[cfg(target_arch = "x86_64")] - let mut arch = Architecture::X8664; - - #[cfg(target_arch = "x86")] - let mut arch = Architecture::X86; - - if let Ok(utsname) = uname::UtsName::new() { - match utsname.machine() { - "x86_64" => arch = Architecture::X8664, - "x86" => arch = Architecture::X86, - _ => (), - } +pub fn architecture() -> Option { + match uname::UtsName::new().ok()?.machine() { + "x86_64" => Some(Architecture::X8664), + "x86" => Some(Architecture::X86), + _ => None, } - - arch } pub fn integrity_level() -> Result { diff --git a/Payload_Type/thanatos/agent/thanatos/src/os/linux/platform.rs b/Payload_Type/thanatos/agent/thanatos/src/os/linux/platform.rs new file mode 100644 index 0000000..aa0837f --- /dev/null +++ b/Payload_Type/thanatos/agent/thanatos/src/os/linux/platform.rs @@ -0,0 +1,71 @@ +// TODO: Make this return an enum value for the container environment and return +// it as a separate field in the initial check in +fn check_container_environment() -> Option<&'static str> { + if let Ok(readdir) = std::fs::read_dir("/") { + for entry in readdir.flatten() { + if entry.file_name() == ".dockerenv" { + return Some("Docker"); + } + } + } + + if let Ok(readdir) = std::fs::read_dir("/run") { + for entry in readdir.flatten() { + if entry.file_name() == ".containerenv" { + return Some("Container"); + } + } + } + + None +} + +// TODO: Return this into a separate initial check in field. +// Parse /proc/self/mountinfo for selinux detection instead of looking for /sys/fs/selinux +fn check_selinux() -> bool { + if let Ok(readdir) = std::fs::read_dir("/sys/fs") { + for entry in readdir.flatten() { + if entry.file_name() == "selinux" { + return true; + } + } + } + + false +} + +// TODO: Split up platform values into separate check in fields and create the platform +// string server side. Also grab the architecture from the initial check in instead +// of embedding it into this string +pub fn platform() -> String { + let distro = os_release() + .map(|os_info| { + os_info + .pretty_name + .unwrap_or_else(|| format!("{} {}", os_info.name, os_info.version)) + }) + .unwrap_or_else(|_| "Linux".to_string()); + + let utsname = uname::UtsName::new(); + + let mut platform_name = match utsname { + Ok(utsname) => format!( + "{} kernel {} {}", + distro, + utsname.release(), + utsname.machine() + ) + .to_string(), + Err(_) => distro, + }; + + if check_selinux() { + platform_name.push_str(" (SELinux)"); + } + + if let Some(runtime) = check_container_environment() { + platform_name.push_str(&format!(" ({runtime})")); + } + + platform_name +} diff --git a/Payload_Type/thanatos/agent/thanatos/src/os/mod.rs b/Payload_Type/thanatos/agent/thanatos/src/os/mod.rs new file mode 100644 index 0000000..84bd478 --- /dev/null +++ b/Payload_Type/thanatos/agent/thanatos/src/os/mod.rs @@ -0,0 +1,5 @@ +#[cfg(target_os = "windows")] +pub mod windows; + +#[cfg(target_os = "linux")] +pub mod linux; diff --git a/Payload_Type/thanatos/agent/thanatos/src/os/windows/mod.rs b/Payload_Type/thanatos/agent/thanatos/src/os/windows/mod.rs new file mode 100644 index 0000000..35d95d0 --- /dev/null +++ b/Payload_Type/thanatos/agent/thanatos/src/os/windows/mod.rs @@ -0,0 +1,28 @@ +use errors::ThanatosError; +use ffiwrappers::windows::processthreadsapi::token::CurrentToken; +pub use ffiwrappers::windows::{domain, hostname, sysinfoapi, username}; + +mod platform; +pub use platform::{build_number, product}; + +use crate::proto::checkin::Architecture; + +pub fn process_name() -> Result { + ffiwrappers::windows::process_name().map_err(ThanatosError::FFIError) +} + +pub fn architecture() -> Option { + let system_info = sysinfoapi::SystemInfo::new(); + match system_info.processor_architecture() { + sysinfoapi::ProcessorArchitecture::Amd64 => Some(Architecture::X8664), + sysinfoapi::ProcessorArchitecture::Intel => Some(Architecture::X86), + _ => None, + } +} + +pub fn integrity_level() -> Option { + let token = CurrentToken::new(); + let sid = token.integrity_level().ok()?; + let rid = sid.sid().subauthorities().first()?.to_owned(); + Some(rid >> 12) +} diff --git a/Payload_Type/thanatos/agent/thanatos/src/os/windows/platform.rs b/Payload_Type/thanatos/agent/thanatos/src/os/windows/platform.rs new file mode 100644 index 0000000..49ca690 --- /dev/null +++ b/Payload_Type/thanatos/agent/thanatos/src/os/windows/platform.rs @@ -0,0 +1,52 @@ +use ffiwrappers::windows::sysinfoapi::{OsVersionInfo, ProductType}; + +use crate::proto::checkin::windows_info::Product; + +macro_rules! product_mapping { + ($v:ident, $($field:ident),*) => { + match $v { + ffiwrappers::windows::sysinfoapi::ProductType::Other(o) => $crate::proto::checkin::windows_info::Product::Other(o), + $(ffiwrappers::windows::sysinfoapi::ProductType::$field => $crate::proto::checkin::windows_info::Product::ProductType($crate::proto::checkin::WindowsProductType::$field.into()),)* + } + }; +} + +impl From for Product { + fn from(value: ProductType) -> Self { + product_mapping!( + value, + Core, + Professional, + Education, + Enterprise, + ProWorkstation, + ProForEducation, + EnterpriseEvaluation, + DatacenterServer, + DatacenterEvaluationServer, + DatacenterServerCore, + DatacenterServerCoreV, + DatacenterServerV, + EnterpriseServer, + EnterpriseServerCore, + EnterpriseServerCoreV, + EnterpriseServerV, + HomeBasic, + StandardServer, + StandardEvaluationServer, + StandardServerCore, + StandardServerCoreV, + StandardServerV + ) + } +} + +pub fn build_number() -> u32 { + let osversion = OsVersionInfo::new(); + osversion.build_number() +} + +pub fn product() -> Product { + let osversion = OsVersionInfo::new(); + osversion.product_type().into() +} diff --git a/Payload_Type/thanatos/mythic/protos/msg/checkin.proto b/Payload_Type/thanatos/mythic/protos/msg/checkin.proto index fbb7909..12c9579 100644 --- a/Payload_Type/thanatos/mythic/protos/msg/checkin.proto +++ b/Payload_Type/thanatos/mythic/protos/msg/checkin.proto @@ -5,17 +5,67 @@ package msg.checkin; message CheckinInfo { string uuid = 1; repeated string ips = 2; - string platform = 3; - optional string user = 4; - optional string host = 5; - optional uint32 pid = 6; - Architecture architecture = 7; - optional string domain = 8; - optional uint32 integrity_level = 9; - optional string process_name = 10; + optional string user = 3; + optional string host = 4; + optional uint32 pid = 5; + Architecture architecture = 6; + optional string domain = 7; + optional uint32 integrity_level = 8; + optional string process_name = 9; + oneof platform_info { + WindowsInfo windows = 11; + LinuxInfo linux = 12; + } } enum Architecture { X86 = 0; X86_64 = 1; } + +message LinuxInfo { + string distro = 1; + string kernel = 2; + bool selinux = 3; + ContainerEnv container = 4; +} + +enum ContainerEnv { + NONE = 0; + DOCKER = 1; + CONTAINER = 2; +} + +message WindowsInfo { + uint32 build = 1; + oneof product { + WindowsProductType product_type = 2; + uint32 other = 3; + } +} + +enum WindowsProductType { + UNKNOWN = 0; + CORE = 0x00000065; + PROFESSIONAL = 0x00000030; + EDUCATION = 0x00000079; + ENTERPRISE = 0x00000004; + PRO_WORKSTATION = 0x000000A1; + PRO_FOR_EDUCATION = 0x000000A4; + ENTERPRISE_EVALUATION = 0x00000048; + DATACENTER_SERVER = 0x00000008; + DATACENTER_EVALUATION_SERVER = 0x00000050; + DATACENTER_SERVER_CORE = 0x0000000C; + DATACENTER_SERVER_CORE_V = 0x00000027; + DATACENTER_SERVER_V = 0x00000025; + ENTERPRISE_SERVER = 0x0000000A; + ENTERPRISE_SERVER_CORE = 0x0000000E; + ENTERPRISE_SERVER_CORE_V = 0x00000029; + ENTERPRISE_SERVER_V = 0x00000026; + HOME_BASIC = 0x00000002; + STANDARD_SERVER = 0x00000007; + STANDARD_EVALUATION_SERVER = 0x0000004F; + STANDARD_SERVER_CORE = 0x0000000D; + STANDARD_SERVER_CORE_V = 0x00000028; + STANDARD_SERVER_V = 0x00000024; +}