diff --git a/Cargo.toml b/Cargo.toml index dd41e90..994cd3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,10 @@ features = ["std"] [target.'cfg(windows)'.dependencies] windows-service = "0.7.0" +windows-core = "0.58.0" zip = "1.1.2" +minidom = "0.15.2" + [target.'cfg(unix)'.dependencies] flate2 = "1.0.27" @@ -40,3 +43,16 @@ tar = "0.4.40" [dev-dependencies] tokio-test = "*" + +[dependencies.windows] +version = "0.58.0" +features = [ + "Win32_Foundation", + "Win32_System_EventLog", + "Win32_System_SystemServices", + "Win32_Security", + "Win32_Security_Authorization", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", + "Win32_Storage_FileSystem" +] \ No newline at end of file diff --git a/config/index_template.json b/config/index_template.json index 67f070f..c4cd88f 100644 --- a/config/index_template.json +++ b/config/index_template.json @@ -60,7 +60,25 @@ "source": { "type": "keyword" }, "parent_id": { "type": "keyword" }, "message": { "type": "keyword" }, - "rule": { "type": "keyword" } + "rule": { "type": "keyword" }, + "operations": { "type": "keyword" }, + "subject_user_sid": { "type": "keyword" }, + "subject_user_name": { "type": "keyword" }, + "subject_domain_name": { "type": "keyword" }, + "subject_logon_id": { "type": "keyword" }, + "object_server": { "type": "keyword" }, + "object_type": { "type": "keyword" }, + "object_name": { "type": "keyword" }, + "handle_id": { "type": "keyword" }, + "transaction_id": { "type": "keyword" }, + "access_list": { "type": "keyword" }, + "access_reason": { "type": "keyword" }, + "access_mask": { "type": "keyword" }, + "privilege_list": { "type": "keyword" }, + "restricted_sid_count": { "type": "keyword" }, + "process_id": { "type": "keyword" }, + "process_name": { "type": "keyword" }, + "resource_attributes": { "type": "keyword" } } }, "settings": { @@ -126,7 +144,25 @@ "source", "parent_id", "message", - "rule" + "rule", + "operations", + "subject_user_sid", + "subject_user_name", + "subject_domain_name", + "subject_logon_id", + "object_server", + "object_type", + "object_name", + "handle_id", + "transaction_id", + "access_list", + "access_reason", + "access_mask", + "privilege_list", + "restricted_sid_count", + "process_id", + "process_name", + "resource_attributes" ] } } diff --git a/src/appconfig.rs b/src/appconfig.rs index e7144cc..be7c5b0 100644 --- a/src/appconfig.rs +++ b/src/appconfig.rs @@ -333,7 +333,7 @@ impl AppConfig { // ------------------------------------------------------------------------ pub fn get_index(&self, raw_path: &str, cwd: &str, array: Array) -> usize { - // Iterate over monitoring paths to match ignore string and ignore event or not + // Iterate over monitoring paths to match the event path. match array.iter().position(|it| { if !cwd.is_empty() && (raw_path.starts_with("./") || raw_path == "." || !raw_path.contains('/')) { utils::match_path(cwd, it["path"].as_str().unwrap()) @@ -406,12 +406,12 @@ impl AppConfig { let mut integrations: Vec = Vec::new(); data.iter().for_each(|info| integrations.push(Integration::new( - String::from(info["name"].as_str().unwrap()), + String::from(info["name"].as_str().unwrap()), info["condition"] - .clone().into_vec().unwrap().iter().map(|element| - String::from(element.as_str().unwrap()) ).collect(), - String::from(info["binary"].as_str().unwrap()), - String::from(info["script"].as_str().unwrap()), + .clone().into_vec().unwrap().iter().map(|element| + String::from(element.as_str().unwrap()) ).collect(), + String::from(info["binary"].as_str().unwrap()), + String::from(info["script"].as_str().unwrap()), String::from(info["parameters"].as_str().unwrap()) )) ); integrations @@ -501,7 +501,7 @@ pub fn get_config_path(system: &str) -> String { String::from(CONFIG_MACOS_PATH) }else{ String::from(CONFIG_LINUX_PATH) - } + } } } diff --git a/src/main.rs b/src/main.rs index 920e2dd..e7ed6b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ mod index; // Single event data management mod event; mod monitorevent; +mod winevent; mod ruleevent; // File reading continuously mod logreader; @@ -36,6 +37,8 @@ mod launcher; mod multiwatcher; mod rotator; mod init; +mod wineventsubscriber; +mod winhandler; // ---------------------------------------------------------------------------- diff --git a/src/monitor.rs b/src/monitor.rs index 5c91d89..8eee058 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -11,6 +11,7 @@ use log::{info, error, debug, warn}; use std::path::Path; // To manage date and time use std::time::{SystemTime, UNIX_EPOCH}; +//use std::ffi::c_void; use time::OffsetDateTime; // To use intersperse() use itertools::Itertools; @@ -36,6 +37,8 @@ use crate::logreader; // integrations checker use crate::launcher; use crate::multiwatcher::MultiWatcher; +use crate::wineventsubscriber; +use crate::winhandler::*; // ---------------------------------------------------------------------------- @@ -83,8 +86,8 @@ fn clean_audit_rules(cfg: &AppConfig){ // Function that monitorize files in loop pub async fn monitor( - tx: mpsc::Sender>, - rx: mpsc::Receiver>, + tx: mpsc::Sender>, + rx: mpsc::Receiver>, cfg: AppConfig, ruleset: Ruleset){ @@ -95,7 +98,7 @@ pub async fn monitor( push_template(destination.as_str(), cfg.clone()).await; let mut watcher = MultiWatcher::new(cfg.clone().events_watcher.as_str(), tx); - + // Iterating over monitor paths and set watcher on each folder to watch. if ! cfg.clone().monitor.is_empty() { for element in cfg.clone().monitor { @@ -129,10 +132,207 @@ pub async fn monitor( None => debug!("Monitoring files under '{}' path.", path) } - match watcher.watch(Path::new(path), RecursiveMode::Recursive) { - Ok(_d) => debug!("Monitoring '{}' path.", path), - Err(e) => warn!("Could not monitor given path '{}', description: {}", path, e) - }; + if utils::get_os() != "windows" { + match watcher.watch(Path::new(path), RecursiveMode::Recursive) { + Ok(_d) => debug!("Monitoring '{}' path.", path), + Err(e) => warn!("Could not monitor given path '{}', description: {}", path, e) + }; + } else { + + + use windows::core::{ PCWSTR, HSTRING, PWSTR }; + use windows::Win32::System::SystemServices::{ + SECURITY_DESCRIPTOR_REVISION, + MAXDWORD + }; + use windows::Win32::Security::{ + PSID, + ACL_REVISION, + ACL_REVISION_DS, + ACL, + InitializeSecurityDescriptor, + InitializeAcl, + GetAce, + AddAce, + SYSTEM_AUDIT_OBJECT_ACE, + IsValidAcl, + ACE_HEADER, + SYSTEM_AUDIT_ACE, + }; + use windows::Win32::Security::Authorization::ConvertStringSidToSidW; + use windows::Win32::Security::Authorization::BuildExplicitAccessWithNameW; + use windows::Win32::Security::Authorization::EXPLICIT_ACCESS_W; + use windows::Win32::Security::Authorization::SET_AUDIT_FAILURE; + use windows::Win32::Security::Authorization::SET_AUDIT_SUCCESS; + use windows::Win32::Security::CONTAINER_INHERIT_ACE; + use windows::Win32::Security::NO_INHERITANCE; + use windows::Win32::Security::Authorization::TRUSTEE_W; + use windows::Win32::Security::Authorization::NO_MULTIPLE_TRUSTEE; + use windows::Win32::Security::Authorization::TRUSTEE_IS_SID; + use windows::Win32::Security::Authorization::TRUSTEE_IS_NAME; + use windows::Win32::Security::Authorization::TRUSTEE_IS_WELL_KNOWN_GROUP; + use windows::Win32::Security::Authorization::GetExplicitEntriesFromAclW; + use windows::Win32::Foundation::GENERIC_ALL; + use windows::Win32::System::SystemServices::ACCESS_SYSTEM_SECURITY; + use windows::Win32::Security::Authorization::ACCESS_MODE; + use windows::Win32::Security::Authorization::SetEntriesInAclW; + use windows::Win32::Storage::FileSystem::FILE_ALL_ACCESS; + use windows::Win32::Security::Authorization::SET_ACCESS; + use windows::Win32::System::SystemServices::SYSTEM_AUDIT_ACE_TYPE; + + let mut sid = PSID::default(); + let mut raw_sidE = HSTRING::from("S-1-1-0\0"); // Everyone + let raw_sid = HSTRING::from("Everyone\0"); // Everyone + + unsafe { + //let mut ptr = *(raw_sid.as_ptr()); + //let array = raw_sid.as_wide(); + //println!("Array: {:?}", array); + //let mut ptr = raw_sid.as_ptr() as *mut _; + let pwstr = PWSTR::from_raw(raw_sid.as_ptr() as *mut _); + //println!("PWSTR: {:?}", pwstr.to_string().unwrap()); + + enable_privilege(); + let handle = get_handle(HSTRING::from("C:\\tmp2\\file.txt\0")); + + // Convert the SID string to a PSID + let _result = ConvertStringSidToSidW(PCWSTR::from_raw(raw_sidE.as_ptr()), &mut sid); + let mut acl = get_sacl_security_info(handle); + + + use std::ffi::c_void; + println!("Getting ACL ACEs..."); + for n in 0..acl.AceCount { + let mut pace: *mut c_void = std::ptr::null_mut(); + GetAce(&acl, n as u32, &mut pace); + + let ace_header: *mut ACE_HEADER = pace as *mut ACE_HEADER; + println!("{:?}", *ace_header); + acl.AclSize += (*ace_header).AceSize; + } + + + + + /*let mut explicit_access = EXPLICIT_ACCESS_W { + grfAccessPermissions: ACCESS_SYSTEM_SECURITY, + grfAccessMode: ACCESS_MODE(SET_AUDIT_SUCCESS.0 | SET_AUDIT_FAILURE.0), + grfInheritance: CONTAINER_INHERIT_ACE, + Trustee: TRUSTEE_W { + pMultipleTrustee: std::ptr::null_mut(), + MultipleTrusteeOperation: NO_MULTIPLE_TRUSTEE, + TrusteeForm: TRUSTEE_IS_NAME, + TrusteeType: TRUSTEE_IS_WELL_KNOWN_GROUP, + ptstrName: pwstr, + }, + };*/ + //let explicit_access = &mut EXPLICIT_ACCESS_W::default(); + //println!(); + //println!("Explicit access: {:?}", explicit_access); + //println!(); + /*BuildExplicitAccessWithNameW( + explicit_access, + pwstr, + ACCESS_SYSTEM_SECURITY, + ACCESS_MODE(SET_AUDIT_SUCCESS.0 | SET_AUDIT_FAILURE.0), + NO_INHERITANCE + );*/ + + let mut explicit_access_failure = EXPLICIT_ACCESS_W::default(); + explicit_access_failure.grfAccessPermissions = GENERIC_ALL.0; + explicit_access_failure.grfAccessMode = SET_AUDIT_FAILURE; + explicit_access_failure.grfInheritance = NO_INHERITANCE; + explicit_access_failure.Trustee.pMultipleTrustee = std::ptr::null_mut(); + explicit_access_failure.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + explicit_access_failure.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + explicit_access_failure.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + explicit_access_failure.Trustee.ptstrName = pwstr; + let mut explicit_access_success = EXPLICIT_ACCESS_W::default(); + explicit_access_success.grfAccessPermissions = GENERIC_ALL.0; + explicit_access_success.grfAccessMode = SET_AUDIT_SUCCESS; + explicit_access_success.grfInheritance = NO_INHERITANCE; + explicit_access_success.Trustee.pMultipleTrustee = std::ptr::null_mut(); + explicit_access_success.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + explicit_access_success.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + explicit_access_success.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + explicit_access_success.Trustee.ptstrName = pwstr; + + let mut explicit_access_array = [explicit_access_failure, explicit_access_success]; + //println!(); + //println!("Explicit access: {:?}", explicit_access); + //println!("PWSTR text: {:?}", explicit_access.Trustee.ptstrName.to_string().unwrap()); + //println!(); + + //let mut new_acl: *mut ACL = &mut ACL::default(); + let mut new_acl: *mut [c_void; 65536] = &mut [const { std::mem::zeroed() }; 65536] as *mut _; + let mut tacl = new_acl as *mut ACL; + println!("The old ACL is: {:?}", acl); + //let array = &mut [*explicit_access]; + //println!("Array: {:?}", array); + let result = SetEntriesInAclW( + Some(&explicit_access_array), + Some(&acl), + &mut tacl + ); + println!("Result = {:?}", result); + + println!("The new ACL is: {:?}", *tacl); + println!("Getting ACL ACEs..."); + //let cacl = *tacl as *mut ACL; + for n in 0..(*tacl).AceCount { + let sacl = *tacl; + let mut pace: *mut c_void = std::ptr::null_mut(); + GetAce(&sacl, n as u32, &mut pace); + + let ace_header: *mut ACE_HEADER = pace as *mut ACE_HEADER; + println!("ACE_HEADER: {:?}", *ace_header); + //acl.AclSize += (*ace_header).AceSize; + } + + + + //println!("Monitor PSI: {:?}", security_info); + //use std::ffi::c_void; + //let mut ace: [u8; 28] = [std::mem::zeroed(); 28]; + //let ace_info: *mut *mut c_void = &mut &mut ace as *mut _ as *mut _; + //GetAce(&acl, 0, ace_info); + //let ptr_ace_struct = *ace_info as *mut SYSTEM_AUDIT_OBJECT_ACE; + //println!("ACE entry: {:?}", * ptr_ace_struct); + + /*let ace = SYSTEM_AUDIT_OBJECT_ACE { + Header: , + Mask: , + Flags: , + ObjectType: , + InheritedObjectType: , + SidStart: , + }; + AddAce(acl, acl.AclRevision, MAXDWORD, &ace, size_of:: as u32);*/ + + /*let mut iacl = ACL::default(); + match InitializeAcl(&mut iacl, (size_of::() as u32) * 100, ACL_REVISION_DS) { + Ok(_v) => println!("ACL initialized!"), + Err(e) => println!("Error initializing ACL, {:?}", e) + }; + println!("Initialized ACL: {:?}", iacl);*/ + + // Initialize a new security descriptor + /*println!("Initializing Security Descriptor"); + match InitializeSecurityDescriptor(security_info, + SECURITY_DESCRIPTOR_REVISION){ + Ok(_v) => println!("Security Descriptor initialized."), + Err(_e) => println!("Error initializing security descriptor") + }; + + println!("Initialized SecDesc: {:?}", security_info);*/ + + //add_security_ace(new_acl, sid); + //set_security_info(handle, new_acl); + // disable_privilege(); + } + + //debug!("Monitoring windows '{}' audit path.", path); + } } } let mut last_position = 0; @@ -173,7 +373,7 @@ pub async fn monitor( // Detect if Audit file is moved or renamed (rotation) watcher.watch(Path::new(logreader::AUDIT_PATH), RecursiveMode::NonRecursive).unwrap(); last_position = utils::get_file_end(logreader::AUDIT_LOG_PATH, 0); - + // Remove auditd rules introduced by FIM // Setting ctrl + C handler let cloned_cfg = cfg.clone(); @@ -184,6 +384,11 @@ pub async fn monitor( } + // ---------------------------------------- + + // Subscribing to windows event channel + wineventsubscriber::subscribe_event_channel(cfg.clone(), ruleset.clone()); + // Main loop, receive any produced event and write it into the events log. loop { for message in &rx { @@ -286,11 +491,11 @@ pub async fn monitor( detailed_operation: event::get_detailed_operation(kind), checksum: hash::get_checksum( String::from(path.to_str().unwrap()), cfg.clone().events_max_file_checksum ), fpid: utils::get_pid(), - system: cfg.clone().system + system: cfg.clone().system, }; debug!("Event processed: {:?}", event); - event.process(cfg.clone(), ruleset.clone()).await; + //event.process(cfg.clone(), ruleset.clone()).await; launcher::check_integrations(event.clone(), cfg.clone()); }else{ debug!("Event ignored/excluded not stored in alerts"); diff --git a/src/monitorevent.rs b/src/monitorevent.rs index 8b3a620..eeea35c 100644 --- a/src/monitorevent.rs +++ b/src/monitorevent.rs @@ -17,20 +17,20 @@ use std::io::Write; pub struct MonitorEvent { - pub id: String, - pub timestamp: String, - pub hostname: String, - pub node: String, - pub version: String, - pub path: PathBuf, - pub size: u64, - pub kind: notify::EventKind, - pub labels: Vec, - pub operation: String, - pub detailed_operation: String, - pub checksum: String, - pub fpid: u32, - pub system: String + pub id: String, + pub timestamp: String, + pub hostname: String, + pub node: String, + pub version: String, + pub path: PathBuf, + pub size: u64, + pub kind: notify::EventKind, + pub labels: Vec, + pub operation: String, + pub detailed_operation: String, + pub checksum: String, + pub fpid: u32, + pub system: String } @@ -59,22 +59,22 @@ impl Event for MonitorEvent { // ------------------------------------------------------------------------ fn clone(&self) -> Self { - MonitorEvent { - id: self.id.clone(), - timestamp: self.timestamp.clone(), - hostname: self.hostname.clone(), - node: self.node.clone(), - version: self.version.clone(), - path: self.path.clone(), - size: self.size, - kind: self.kind, - labels: self.labels.clone(), - operation: self.operation.clone(), - detailed_operation: self.detailed_operation.clone(), - checksum: self.checksum.clone(), - fpid: self.fpid, - system: self.system.clone() - } + MonitorEvent { + id: self.id.clone(), + timestamp: self.timestamp.clone(), + hostname: self.hostname.clone(), + node: self.node.clone(), + version: self.version.clone(), + path: self.path.clone(), + size: self.size, + kind: self.kind, + labels: self.labels.clone(), + operation: self.operation.clone(), + detailed_operation: self.detailed_operation.clone(), + checksum: self.checksum.clone(), + fpid: self.fpid, + system: self.system.clone() + } } // ------------------------------------------------------------------------ @@ -100,7 +100,7 @@ impl Event for MonitorEvent { use time::OffsetDateTime; let current_date = OffsetDateTime::now_utc(); let index = format!("fim-{}-{}-{}", current_date.year(), current_date.month() as u8, current_date.day() ); - + // Splunk endpoint integration if cfg.endpoint_type == "Splunk" { let data = json!({ @@ -441,7 +441,7 @@ mod tests { fn test_process() { let event = create_test_event(); let cfg = AppConfig::new(&utils::get_os(), None); - let ruleset = Ruleset::new(&utils::get_os(), None); + let ruleset = Ruleset::new(&utils::get_os(), None); block_on(event.process(cfg, ruleset)); //block_on(event.process(appconfig::NETWORK_MODE, String::from("test"), cfg.clone())); diff --git a/src/rotator.rs b/src/rotator.rs index 01ec428..81148e8 100644 --- a/src/rotator.rs +++ b/src/rotator.rs @@ -77,7 +77,7 @@ fn compress_tgz_file(filepath: &str) -> Result { let enc = GzEncoder::new(tgz, Compression::default()); let mut tar = tar::Builder::new(enc); let mut file = File::open(filepath).unwrap(); - + match tar.append_file(filename, &mut file){ Ok(()) => Ok(format!("File {} compressed successfully.", filename)), Err(e) => { @@ -106,7 +106,7 @@ fn rotate_file(filepath: &str, iteration: u32, lock: Mutex){ let file_rotated = format!("{}.{}", parent_path.to_str().unwrap(), iteration); - + match copy(filepath, file_rotated.clone()){ Ok(_v) => { debug!("File copied successfully."); @@ -168,19 +168,19 @@ fn rotate_file(filepath: &str, iteration: u32, lock: Mutex){ Err(e) => error!("Error compressing file, error: {}", e) }; } - - + + } // ---------------------------------------------------------------------------- -#[cfg(not(tarpaulin_include))] +//#[cfg(not(tarpaulin_include))] pub fn rotator(cfg: AppConfig){ loop{ let log_size = if Path::new(cfg.clone().log_file.as_str()).exists() { metadata(cfg.clone().log_file).unwrap().len() as usize }else{ 0 }; - + let events_size = if Path::new(cfg.clone().events_file.as_str()).exists() { metadata(cfg.clone().events_file).unwrap().len() as usize }else{ 0 }; @@ -199,7 +199,7 @@ pub fn rotator(cfg: AppConfig){ rotate_file( cfg.clone().events_file.as_str(), - get_iteration(parent_path.to_str().unwrap()), + get_iteration(parent_path.to_str().unwrap()), cfg.clone().get_mutex(cfg.clone().events_lock)); } @@ -217,7 +217,7 @@ pub fn rotator(cfg: AppConfig){ rotate_file( cfg.clone().log_file.as_str(), - get_iteration(parent_path.to_str().unwrap()), + get_iteration(parent_path.to_str().unwrap()), cfg.clone().get_mutex(cfg.clone().log_lock)); } diff --git a/src/winevent.rs b/src/winevent.rs new file mode 100644 index 0000000..fcda308 --- /dev/null +++ b/src/winevent.rs @@ -0,0 +1,555 @@ +// Copyright (C) 2024, Achiefs. + +use crate::event; +use crate::appconfig; +use crate::appconfig::*; +use crate::ruleset::*; + +use event::Event; +use log::*; +use serde_json::{json, to_string}; +use std::path::PathBuf; +use reqwest::Client; +use std::fs::OpenOptions; +use std::time::Duration; +use std::fmt; +use std::io::Write; + +pub struct WinEvent { + pub id: String, + pub timestamp: String, + pub hostname: String, + pub node: String, + pub version: String, + pub path: PathBuf, + pub size: u64, + pub labels: Vec, + pub operations: Vec, + pub checksum: String, + pub fpid: u32, + pub system: String, + + pub subject_user_sid: String, + pub subject_user_name: String, + pub subject_domain_name: String, + pub subject_logon_id: String, + pub object_server: String, + pub object_type: String, + pub object_name: String, + pub handle_id: String, + pub transaction_id: String, + pub access_list: Vec, + pub access_reason: String, + pub access_mask: String, + pub privilege_list: String, + pub restricted_sid_count: String, + pub process_id: String, + pub process_name: String, + pub resource_attributes: String +} + + + +impl Event for WinEvent { + // Get formatted string with all required data + fn format_json(&self) -> String { + let obj = json!({ + "id": self.id.clone(), + "timestamp": self.timestamp.clone(), + "hostname": self.hostname.clone(), + "node": self.node.clone(), + "fpid": self.fpid.clone(), + "version": self.version.clone(), + "labels": self.labels.clone(), + "operations": self.operations.clone(), + "file": String::from(self.path.clone().to_str().unwrap()), + "file_size": self.size.clone(), + "checksum": self.checksum.clone(), + "system": self.system.clone(), + + "subject_user_sid": self.subject_user_sid.clone(), + "subject_user_name": self.subject_user_name.clone(), + "subject_domain_name": self.subject_domain_name.clone(), + "subject_logon_id": self.subject_logon_id.clone(), + "object_server": self.object_server.clone(), + "object_type": self.object_type.clone(), + "object_name": self.object_name.clone(), + "handle_id": self.handle_id.clone(), + "transaction_id": self.transaction_id.clone(), + "access_list": self.access_list.clone(), + "access_reason": self.access_reason.clone(), + "access_mask": self.access_mask.clone(), + "privilege_list": self.privilege_list.clone(), + "restricted_sid_count": self.restricted_sid_count.clone(), + "process_id": self.process_id.clone(), + "process_name": self.process_name.clone(), + "resource_attributes": self.resource_attributes.clone() + }); + to_string(&obj).unwrap() + } + + // ------------------------------------------------------------------------ + + fn clone(&self) -> Self { + WinEvent { + id: self.id.clone(), + timestamp: self.timestamp.clone(), + hostname: self.hostname.clone(), + node: self.node.clone(), + version: self.version.clone(), + path: self.path.clone(), + size: self.size, + labels: self.labels.clone(), + operations: self.operations.clone(), + checksum: self.checksum.clone(), + fpid: self.fpid, + system: self.system.clone(), + + subject_user_sid: self.subject_user_sid.clone(), + subject_user_name: self.subject_user_name.clone(), + subject_domain_name: self.subject_domain_name.clone(), + subject_logon_id: self.subject_logon_id.clone(), + object_server: self.object_server.clone(), + object_type: self.object_type.clone(), + object_name: self.object_name.clone(), + handle_id: self.handle_id.clone(), + transaction_id: self.transaction_id.clone(), + access_list: self.access_list.clone(), + access_reason: self.access_reason.clone(), + access_mask: self.access_mask.clone(), + privilege_list: self.privilege_list.clone(), + restricted_sid_count: self.restricted_sid_count.clone(), + process_id: self.process_id.clone(), + process_name: self.process_name.clone(), + resource_attributes: self.resource_attributes.clone(), + } + } + + // ------------------------------------------------------------------------ + + // Function to write the received events to file + fn log(&self, file: String) { + let mut events_file = OpenOptions::new() + .create(true) + .append(true) + .open(file) + .expect("(log) Unable to open events log file."); + + match writeln!(events_file, "{}", self.format_json() ) { + Ok(_d) => debug!("Event log written"), + Err(e) => error!("Event could not be written, Err: [{}]", e) + } + } + + // ------------------------------------------------------------------------ + + // Function to send events through network + async fn send(&self, cfg: AppConfig) { + use time::OffsetDateTime; + let current_date = OffsetDateTime::now_utc(); + let index = format!("fim-{}-{}-{}", current_date.year(), current_date.month() as u8, current_date.day() ); + + // Splunk endpoint integration + if cfg.endpoint_type == "Splunk" { + let data = json!({ + "source": self.node.clone(), + "sourcetype": "_json", + "event": json!({ + "timestamp": self.timestamp.clone(), + "hostname": self.hostname.clone(), + "node": self.node.clone(), + "fpid": self.fpid.clone(), + "version": self.version.clone(), + "labels": self.labels.clone(), + "operations": self.operations.clone(), + "file": String::from(self.path.clone().to_str().unwrap()), + "file_size": self.size.clone(), + "checksum": self.checksum.clone(), + "system": self.system.clone(), + + "subject_user_sid": self.subject_user_sid.clone(), + "subject_user_name": self.subject_user_name.clone(), + "subject_domain_name": self.subject_domain_name.clone(), + "subject_logon_id": self.subject_logon_id.clone(), + "object_server": self.object_server.clone(), + "object_type": self.object_type.clone(), + "object_name": self.object_name.clone(), + "handle_id": self.handle_id.clone(), + "transaction_id": self.transaction_id.clone(), + "access_list": self.access_list.clone(), + "access_reason": self.access_reason.clone(), + "access_mask": self.access_mask.clone(), + "privilege_list": self.privilege_list.clone(), + "restricted_sid_count": self.restricted_sid_count.clone(), + "process_id": self.process_id.clone(), + "process_name": self.process_name.clone(), + "resource_attributes": self.resource_attributes.clone() + }), + "index": "fim_events" + }); + debug!("Sending received event to Splunk integration, event: {}", data); + let request_url = format!("{}/services/collector/event", cfg.endpoint_address); + let client = Client::builder() + .danger_accept_invalid_certs(cfg.insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .header("Authorization", format!("Splunk {}", cfg.endpoint_token)) + .json(&data) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + // Elastic endpoint integration + } else { + let data = json!({ + "timestamp": self.timestamp.clone(), + "hostname": self.hostname.clone(), + "node": self.node.clone(), + "fpid": self.fpid.clone(), + "version": self.version.clone(), + "labels": self.labels.clone(), + "operations": self.operations.clone(), + "file": String::from(self.path.clone().to_str().unwrap()), + "file_size": self.size.clone(), + "checksum": self.checksum.clone(), + "system": self.system.clone(), + + "subject_user_sid": self.subject_user_sid.clone(), + "subject_user_name": self.subject_user_name.clone(), + "subject_domain_name": self.subject_domain_name.clone(), + "subject_logon_id": self.subject_logon_id.clone(), + "object_server": self.object_server.clone(), + "object_type": self.object_type.clone(), + "object_name": self.object_name.clone(), + "handle_id": self.handle_id.clone(), + "transaction_id": self.transaction_id.clone(), + "access_list": self.access_list.clone(), + "access_reason": self.access_reason.clone(), + "access_mask": self.access_mask.clone(), + "privilege_list": self.privilege_list.clone(), + "restricted_sid_count": self.restricted_sid_count.clone(), + "process_id": self.process_id.clone(), + "process_name": self.process_name.clone(), + "resource_attributes": self.resource_attributes.clone() + }); + let request_url = format!("{}/{}/_doc/{}", cfg.endpoint_address, index, self.id); + let client = Client::builder() + .danger_accept_invalid_certs(cfg.insecure) + .timeout(Duration::from_secs(30)) + .build().unwrap(); + match client + .post(request_url) + .basic_auth(cfg.endpoint_user, Some(cfg.endpoint_pass)) + .json(&data) + .send() + .await { + Ok(response) => debug!("Response received: {:?}", + response.text().await.unwrap()), + Err(e) => debug!("Error on request: {:?}", e) + } + } + + } + + // ------------------------------------------------------------------------ + + // Function to manage event destination + async fn process(&self, cfg: AppConfig, _ruleset: Ruleset) { + //match filter(self, cfg.clone()){ + // true => { + route(self, cfg.clone()).await; + _ruleset.match_rule(cfg, self.path.clone(), self.id.clone()).await; + // }, + // false => debug!("Event discarded, not match monitoring path.") + //} + } + + // ------------------------------------------------------------------------ + + fn get_string(&self, field: String) -> String { + match field.as_str() { + "file" => String::from(self.path.to_str().unwrap()), + "file_size" => self.size.clone().to_string(), + "hostname" => self.hostname.clone(), + "node" => self.node.clone(), + "version" => self.version.clone(), + "operations" => self.operations.clone().iter().map(|x| x.to_string() + ",").collect(), + "checksum" => self.checksum.clone(), + "system" => self.system.clone(), + + "subject_user_sid" => self.subject_user_sid.clone(), + "subject_user_name" => self.subject_user_name.clone(), + "subject_domain_name" => self.subject_domain_name.clone(), + "subject_logon_id" => self.subject_logon_id.clone(), + "object_server" => self.object_server.clone(), + "object_type" => self.object_type.clone(), + "object_name" => self.object_name.clone(), + "handle_id" => self.handle_id.clone(), + "transaction_id" => self.transaction_id.clone(), + "access_list" => self.access_list.clone().iter().map(|x| x.to_string() + ",").collect(), + "access_reason" => self.access_reason.clone(), + "access_mask" => self.access_mask.clone(), + "privilege_list" => self.privilege_list.clone(), + "restricted_sid_count" => self.restricted_sid_count.clone(), + "process_id" => self.process_id.clone(), + "process_name" => self.process_name.clone(), + "resource_attributes" => self.resource_attributes.clone(), + _ => "".to_string() + } + } +} + +// ---------------------------------------------------------------------------- + +impl fmt::Debug for WinEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{ + f.debug_tuple("") + .field(&self.id) + .field(&self.path) + .field(&self.size) + .field(&self.operations) + .field(&self.subject_user_sid) + .field(&self.subject_user_name) + .field(&self.subject_domain_name) + .field(&self.subject_logon_id) + .field(&self.object_server) + .field(&self.object_type) + .field(&self.object_name) + .field(&self.handle_id) + .field(&self.transaction_id) + .field(&self.access_list) + .field(&self.access_reason) + .field(&self.access_mask) + .field(&self.privilege_list) + .field(&self.restricted_sid_count) + .field(&self.process_id) + .field(&self.process_name) + .field(&self.resource_attributes) + .finish() + } +} + +// ---------------------------------------------------------------------------- + +pub fn filter (event: &WinEvent, cfg: AppConfig) -> bool { + let index = cfg.get_index(event.path.to_str().unwrap(), "", cfg.clone().monitor.to_vec()); + index != usize::MAX +} + +// ---------------------------------------------------------------------------- + +pub async fn route(event: &WinEvent, cfg: AppConfig) { + match cfg.get_events_destination().as_str() { + appconfig::BOTH_MODE => { + event.log(cfg.get_events_file()); + event.send(cfg).await; + }, + appconfig::NETWORK_MODE => { + event.send(cfg).await; + }, + _ => event.log(cfg.get_events_file()) + } +} + +// ---------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use crate::winevent::WinEvent; + use crate::event::*; + use crate::utils; + use std::path::PathBuf; + use tokio_test::block_on; + use std::fs; + + // ------------------------------------------------------------------------ + + fn remove_test_file(filename: String) { + fs::remove_file(filename).unwrap() + } + + fn create_test_event() -> WinEvent { + WinEvent { + id: "Test_id".to_string(), + timestamp: "Timestamp".to_string(), + hostname: "Hostname".to_string(), + node: "FIM".to_string(), + version: "x.x.x".to_string(), + path: PathBuf::new(), + size: 0, + labels: Vec::new(), + operations: Vec::new(), + checksum: "UNKNOWN".to_string(), + fpid: 0, + system: "test".to_string(), + + subject_user_sid: "SubjecUserSid".to_string(), + subject_user_name: "SubjectUserName".to_string(), + subject_domain_name: "SubjectDomainName".to_string(), + subject_logon_id: "SubjectLogonId".to_string(), + object_server: "ObjectServer".to_string(), + object_type: "ObjectType".to_string(), + object_name: "ObjectName".to_string(), + handle_id: "HandleId".to_string(), + transaction_id: "TransactionId".to_string(), + access_list: "AccessList".to_string(), + access_reason: "AccessReason".to_string(), + access_mask: "AccessMask".to_string(), + privilege_list: "PrivilegeList".to_string(), + restricted_sid_count: "RestrictedSidCount".to_string(), + process_id: "ProcessId".to_string(), + process_name: "ProcessName".to_string(), + resource_attributes: "ResourceAttributes".to_string() + } + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_clone() { + let event = create_test_event(); + let cloned = event.clone(); + assert_eq!(event.id, cloned.id); + assert_eq!(event.timestamp, cloned.timestamp); + assert_eq!(event.hostname, cloned.hostname); + assert_eq!(event.node, cloned.node); + assert_eq!(event.version, cloned.version); + assert_eq!(event.path, cloned.path); + assert_eq!(event.size, cloned.size); + assert_eq!(event.labels, cloned.labels); + assert_eq!(event.operations, cloned.operations); + assert_eq!(event.checksum, cloned.checksum); + assert_eq!(event.fpid, cloned.fpid); + assert_eq!(event.system, cloned.system); + + assert_eq!(event.subject_user_sid, cloned.subject_user_sid); + assert_eq!(event.subject_user_name, cloned.subject_user_name); + assert_eq!(event.subject_domain_name, cloned.subject_domain_name); + assert_eq!(event.subject_logon_id, cloned.subject_logon_id); + assert_eq!(event.object_server, cloned.object_server); + assert_eq!(event.object_type, cloned.object_type); + assert_eq!(event.object_name, cloned.object_name); + assert_eq!(event.handle_id, cloned.handle_id); + assert_eq!(event.transaction_id, cloned.transaction_id); + assert_eq!(event.access_list, cloned.access_list); + assert_eq!(event.access_reason, cloned.access_reason); + assert_eq!(event.access_mask, cloned.access_mask); + assert_eq!(event.privilege_list, cloned.privilege_list); + assert_eq!(event.restricted_sid_count, cloned.restricted_sid_count); + assert_eq!(event.process_id, cloned.process_id); + assert_eq!(event.process_name, cloned.process_name); + assert_eq!(event.resource_attributes, cloned.resource_attributes); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_create_event() { + let evt = create_test_event(); + assert_eq!(evt.id, "Test_id".to_string()); + assert_eq!(evt.timestamp, "Timestamp".to_string()); + assert_eq!(evt.hostname, "Hostname".to_string()); + assert_eq!(evt.node, "FIM".to_string()); + assert_eq!(evt.version, "x.x.x".to_string()); + assert_eq!(evt.path, PathBuf::new()); + assert_eq!(evt.labels, Vec::::new()); + assert_eq!(evt.operations, Vec::new()); + assert_eq!(evt.fpid, 0); + assert_eq!(evt.system, String::from("test")); + + assert_eq!(evt.subject_user_sid, String::from("SubjecUserSid")); + assert_eq!(evt.subject_user_name, String::from("SubjectUserName")); + assert_eq!(evt.subject_domain_name, String::from("SubjectDomainName")); + assert_eq!(evt.subject_logon_id, String::from("SubjectLogonId")); + assert_eq!(evt.object_server, String::from("ObjectServer")); + assert_eq!(evt.object_type, String::from("ObjectType")); + assert_eq!(evt.object_name, String::from("ObjectName")); + assert_eq!(evt.handle_id, String::from("HandleId")); + assert_eq!(evt.transaction_id, String::from("TransactionId")); + assert_eq!(evt.access_list, Vec::new()); + assert_eq!(evt.access_reason, String::from("AccessReason")); + assert_eq!(evt.access_mask, String::from("AccessMask")); + assert_eq!(evt.privilege_list, String::from("PrivilegeList")); + assert_eq!(evt.restricted_sid_count, String::from("RestrictedSidCount")); + assert_eq!(evt.process_id, String::from("ProcessId")); + assert_eq!(evt.process_name, String::from("ProcessName")); + assert_eq!(evt.resource_attributes, String::from("ResourceAttributes")); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_send() { + let evt = create_test_event(); + let cfg = AppConfig::new(&utils::get_os(), None); + block_on( evt.send(cfg) ); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_send_splunk() { + let evt = create_test_event(); + let cfg = AppConfig::new(&utils::get_os(), Some("test/unit/config/common/test_send_splunk.yml")); + block_on( evt.send(cfg) ); + } + + + // ------------------------------------------------------------------------ + + #[test] + fn test_process() { + let event = create_test_event(); + let cfg = AppConfig::new(&utils::get_os(), None); + let ruleset = Ruleset::new(&utils::get_os(), None); + + block_on(event.process(cfg, ruleset)); + //block_on(event.process(appconfig::NETWORK_MODE, String::from("test"), cfg.clone())); + //block_on(event.process(appconfig::FILE_MODE, String::from("test2"), cfg.clone())); + //block_on(event.process(appconfig::BOTH_MODE, String::from("test3"), cfg.clone())); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_event_fmt(){ + let out = format!("{:?}", create_test_event()); + assert_eq!(out, "(\"Test_id\", \"\", 0, \"CREATE\", \"CREATE_FILE\")"); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_format_json() { + let expected = "{\"checksum\":\"UNKNOWN\",\ + \"file\":\"\",\"file_size\":0,\"fpid\":0,\ + \"hostname\":\"Hostname\",\"id\":\"Test_id\",\"labels\":[],\ + \"node\":\"FIM\",\"operations\":\"CREATE\",\"system\":\"test\",\ + \"timestamp\":\"Timestamp\",\"version\":\"x.x.x\"}"; + assert_eq!(create_test_event().format_json(), expected); + } + + // ------------------------------------------------------------------------ + + #[test] + fn test_log() { + let filename = String::from("test_event.json"); + let evt = create_test_event(); + + evt.log(filename.clone()); + let contents = fs::read_to_string(filename.clone()); + let expected = "{\"checksum\":\"UNKNOWN\",\ + \"file\":\"\",\"file_size\":0,\"fpid\":0,\ + \"hostname\":\"Hostname\",\"id\":\"Test_id\",\"labels\":[],\ + \"node\":\"FIM\",\"operations\":\"CREATE\",\ + \"system\":\"test\",\ + \"timestamp\":\"Timestamp\",\"version\":\"x.x.x\"}\n"; + assert_eq!(contents.unwrap(), expected); + remove_test_file(filename.clone()); + } +} \ No newline at end of file diff --git a/src/wineventsubscriber.rs b/src/wineventsubscriber.rs new file mode 100644 index 0000000..c6e92bf --- /dev/null +++ b/src/wineventsubscriber.rs @@ -0,0 +1,185 @@ +// Copyright (C) 2024, Achiefs. + +use windows::core::*; +use windows::Win32::System::EventLog::*; +use windows::Win32::Foundation::*; +use minidom::Element; +use minidom::NSChoice; +use std::ffi::c_void; +use futures::executor::block_on; +use std::time::{SystemTime, UNIX_EPOCH}; +use std::path::PathBuf; + +use crate::appconfig; +use crate::appconfig::*; +use crate::utils; +use crate::event; +use event::Event; +use crate::winevent::WinEvent; +use crate::ruleset::*; +use crate::hash; + +use log::*; + +// --------------------------------------------------------------------------- + +// Unsafe variables, required to process the event callback. +static mut CONFIG: Option = None; +static mut RULESET: Option = None; + +// ---------------------------------------------------------------------------- + +fn process_access_list(access_list: Vec) -> Vec { + access_list.iter().map(|x| match x.as_str() { + "%%4416" => String::from("ReadData/ListDirectory"), + "%%4417" => String::from("WriteData/AddFile"), + "%%4418" => String::from("AppendData/AddSubdirectory/CreatePipeInstance"), + "%%4419" => String::from("ReadEA/Enumerate SubKeys"), + "%%4420" => String::from("WriteEA"), + "%%4421" => String::from("Execute/Traverse"), + "%%4422" => String::from("DeleteChild"), + "%%4423" => String::from("ReadAttributes"), + "%%4424" => String::from("WriteAttributes"), + "%%1537" => String::from("DELETE"), + "%%1538" => String::from("READ_CONTROL"), + "%%1539" => String::from("WRITE_DAC"), + "%%1540" => String::from("WRITE_OWNER"), + "%%1541" => String::from("SYNCHRONIZE"), + "%%1542" => String::from("ACCESS_SYS_SEC"), + _ => String::from("UNKNOWN") + }).collect() +} + +// ---------------------------------------------------------------------------- + +fn get_element_text(root: Element, attr: &str) -> String { + let event_data = root.get_child("EventData", NSChoice::Any).unwrap(); + let element = event_data.children().find(|&x| x.attr("Name").unwrap() == attr); + match element { + Some(elem) => elem.text(), + None => String::from("UNKNOWN") + } +} + +// ---------------------------------------------------------------------------- + +unsafe extern "system" fn print_event(_action: EVT_SUBSCRIBE_NOTIFY_ACTION, _usercontext: *const c_void, event: EVT_HANDLE) -> u32 { + let buffer_used: *mut u32 = &mut 0; + let property_count: *mut u32 = &mut 0; + let mut buf = [0u8; 4096]; + let ptr = buf.as_mut_ptr() as *mut c_void; + let cfg = CONFIG.clone().unwrap(); + + match EvtRender(None, event, EvtRenderEventXml.0.try_into().unwrap(), 4096, + Some(ptr), buffer_used, property_count){ + Ok(_) => debug!("Event rendered"),//(), + Err(e) => error!("Could not render the received event, err: {}", e) + } + + let num_bytes: usize = (*buffer_used).try_into().unwrap(); + let xml_event = std::slice::from_raw_parts(ptr as *mut u8, num_bytes).to_vec(); + let arr = String::from_utf8(xml_event).unwrap().replace("\0", ""); + let root: Element = arr.parse().unwrap(); + + let timestamp = format!("{:?}", SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis()); + let path = PathBuf::from(get_element_text(root.clone(), "ObjectName")); + let mut access_list_text = get_element_text(root.clone(), "AccessList"); + if access_list_text.ends_with("\n\t\t\t\t") { access_list_text.truncate(access_list_text.len()-5); } + let access_list: Vec = access_list_text.split("\n\t\t\t\t").map(|x| x.to_string()).collect(); + + if path.to_str().unwrap() != "UNKNOWN" { + let index = cfg.clone().get_index(path.to_str().unwrap(), "", cfg.clone().monitor.to_vec()); + let filename = path.file_name().unwrap(); + let parent = match path.is_dir() { + true => path.to_str().unwrap(), + false => path.parent().unwrap().to_str().unwrap() + }; + + if index != usize::MAX { + + if ! cfg.match_ignore(index, filename.to_str().unwrap(), cfg.clone().monitor) && + ! cfg.match_exclude(index, parent, cfg.clone().monitor) && + cfg.match_allowed(index, filename.to_str().unwrap(), cfg.clone().monitor){ + + let labels = cfg.clone().get_labels(index, cfg.clone().monitor); + let evt = WinEvent { + id: utils::get_uuid(), + timestamp, + hostname: utils::get_hostname(), + node: CONFIG.clone().unwrap().node, + version: String::from(appconfig::VERSION), + path: path.clone(), + size: utils::get_file_size(path.clone().to_str().unwrap()), + labels: labels, + operations: process_access_list(access_list.clone()), + checksum: hash::get_checksum( String::from(path.to_str().unwrap()), CONFIG.clone().unwrap().events_max_file_checksum ), + fpid: utils::get_pid(), + system: CONFIG.clone().unwrap().system, + + subject_user_sid: get_element_text(root.clone(), "SubjectUserSid"), + subject_user_name: get_element_text(root.clone(), "SubjectUserName"), + subject_domain_name: get_element_text(root.clone(), "SubjectDomainName"), + subject_logon_id: get_element_text(root.clone(), "SubjectLogonId"), + object_server: get_element_text(root.clone(), "ObjectServer"), + object_type: get_element_text(root.clone(), "ObjectType"), + object_name: get_element_text(root.clone(), "ObjectName"), + handle_id: get_element_text(root.clone(), "HandleId"), + transaction_id: get_element_text(root.clone(), "TransactionId"), + access_list, + access_reason: get_element_text(root.clone(), "AccessReason"), + access_mask: get_element_text(root.clone(), "AccessMask"), + privilege_list: get_element_text(root.clone(), "PrivilegeList"), + restricted_sid_count: get_element_text(root.clone(), "RestrictedSidCount"), + process_id: get_element_text(root.clone(), "ProcessId"), + process_name: get_element_text(root.clone(), "ProcessName"), + resource_attributes: get_element_text(root.clone(), "ResourceAttributes") + }; + + block_on( evt.process(CONFIG.clone().unwrap(), RULESET.clone().unwrap()) ); + } + } + } + + return 0 +} + + + + +pub fn subscribe_event_channel(cfg: AppConfig, ruleset: Ruleset) { + unsafe { + CONFIG = Some(cfg); + RULESET = Some(ruleset); + let callback: EVT_SUBSCRIBE_CALLBACK = Some(print_event); + + let subscribe_future_events = 1; + + // 9007199254740992 = 0x20000000000000 -> Audit Success + // 327942 = 0x50006 -> Write/Add/Read/Extended attr + // 0CCE921D-69AE-11D9-BED3-505054503030 -> File system selector + // 4656 -> Handle to an object requested + // 4663 -> Attempt made to access an object + // 4658 -> Handle to an object closed + // 4660 -> Object deleted + // 4670 -> Permissions on an object were changed + // 4661 -> Handle to an object was requested with intent to delete + + let query = w!(" + Event[ \ + System[band(Keywords, 9007199254740992)] and ( \ + ( ( EventData/Data[@Name='ObjectType'] = 'File' or EventData/Data[@Name='ObjectType'] = 'Directory' ) and \ + ( ( System/EventID = 4656 or System/EventID = 4663 ) and \ + ( EventData[band(Data[@Name='AccessMask'], 327942)] ) ) \ + ) or ( EventData/Data[@Name='SubcategoryGuid'] = '0CCE921D-69AE-11D9-BED3-505054503030' ) or \ + ( System/EventID = 4658 ) or \ + ( System/EventID = 4660 ) or \ + ( System/EventID = 4670 ) or \ + ( System/EventID = 4661 ) + ) \ + ] + "); + + let _handle = EvtSubscribe( None, HANDLE::default(), w!("Security"), + query, None, None, callback, subscribe_future_events); + } +} \ No newline at end of file diff --git a/src/winhandler.rs b/src/winhandler.rs new file mode 100644 index 0000000..c218ed7 --- /dev/null +++ b/src/winhandler.rs @@ -0,0 +1,207 @@ +use windows::core::{ PCWSTR, HSTRING }; +use windows::Win32::Foundation::GENERIC_ALL; +use windows::Win32::Foundation::{ HANDLE, WIN32_ERROR, CloseHandle, + GENERIC_READ, + GENERIC_ACCESS_RIGHTS, + BOOL +}; +use windows::Win32::System::SystemServices::ACCESS_SYSTEM_SECURITY; +use windows::Win32::Storage::FileSystem::CreateFileW; +use windows::Win32::Storage::FileSystem::{ + FILE_SHARE_READ, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, +}; +use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION; +use windows::Win32::Security::{ + PSID, + PSECURITY_DESCRIPTOR, + SECURITY_DESCRIPTOR, + SACL_SECURITY_INFORMATION, + ATTRIBUTE_SECURITY_INFORMATION, + SECURITY_ATTRIBUTES, + ACL_REVISION, + ACL_REVISION_DS, + ACL, + InitializeSecurityDescriptor, + AddAuditAccessAce, + InitializeAcl, + INHERITED_ACE, + AddAuditAccessObjectAce, + ACE_REVISION, + GetSecurityDescriptorSacl, + GetSecurityDescriptorDacl, + IsValidAcl, + GetAce, + SYSTEM_AUDIT_ACE, + ACE_HEADER +}; +use windows::Win32::Security::Authorization::{ + GetSecurityInfo, + SetSecurityInfo, + ConvertStringSidToSidW, + SE_FILE_OBJECT +}; +use windows::Win32::System::Threading::OpenProcessToken; +use windows::Win32::Security::{ + AdjustTokenPrivileges, LookupPrivilegeValueW, + TOKEN_ADJUST_PRIVILEGES, TOKEN_QUERY, TOKEN_PRIVILEGES, + SE_PRIVILEGE_ENABLED, SE_SECURITY_NAME +}; +use windows::Win32::System::Threading::GetCurrentProcess; +use std::mem::zeroed; + +// ---------------------------------------------------------------------------- + +pub unsafe fn enable_privilege() { + // Open process token + let mut token_handle: HANDLE = HANDLE::default(); + match OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &mut token_handle) { + Ok(_v) => { + let mut tp = TOKEN_PRIVILEGES { + PrivilegeCount: 1, + Privileges: [zeroed()], + }; + + // Lookup the privilege value + match LookupPrivilegeValueW(None, SE_SECURITY_NAME, &mut tp.Privileges[0].Luid) { + Ok(_v) => { + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Adjust token privileges + match AdjustTokenPrivileges(token_handle, false, Some(&mut tp), std::mem::size_of::() as u32, None, None) { + Ok(_v) => println!("SeSecurityPrivilege enabled successfully."), + Err(e) => println!("Failed to enable SeSecurityPrivilege. {:?}", e) + } + } + Err(e) => println!("Error on LookupPrivilegeValueW: {:?}", e) + } + let _ = CloseHandle(token_handle); + } + Err(e) => println!("Error on OpenProcessToken: {:?}", e) + } +} + +// ---------------------------------------------------------------------------- + +pub unsafe fn get_handle(path: HSTRING) -> HANDLE { + let security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES::default(); + CreateFileW( + PCWSTR::from_raw(path.as_ptr()), + (GENERIC_READ | GENERIC_ACCESS_RIGHTS(ACCESS_SYSTEM_SECURITY)).0, + FILE_SHARE_READ, + Some(&security_attr), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + None + ).unwrap() +} + +// ---------------------------------------------------------------------------- + +pub unsafe fn get_sacl_security_info(handle: HANDLE) -> ACL { + // Get current security descriptor + let mut psecurity_descriptor: PSECURITY_DESCRIPTOR = + PSECURITY_DESCRIPTOR::default(); + let ptr_mut: *mut *mut ACL = &mut std::ptr::null_mut(); + + + //println!("ACL mut pointer: {:?}", ptr_mut); + //println!("Getting mut pointer info: {:?}", *ptr_mut); + //println!("PSecurity Descriptor: {:?}", psecurity_descriptor); + //println!("ptr ACL : {:?}", *ptr_mut); + println!(); + println!("Getting Security Info..."); + let result = GetSecurityInfo( + handle, + SE_FILE_OBJECT, + SACL_SECURITY_INFORMATION | ATTRIBUTE_SECURITY_INFORMATION, + None, + None, + None, + Some(ptr_mut), + Some(&mut psecurity_descriptor), + ); + if result != WIN32_ERROR(0) { + println!("Failed to get security info: {:?}", result); + } else { + println!("Security Info obtained!"); + let sec_desc = psecurity_descriptor.0 as *mut SECURITY_DESCRIPTOR; + println!("Security Descriptor: {:?}", *sec_desc); + println!(); + } + + let mut acl: ACL = **ptr_mut; + //acl.AceCount = 1; + //acl.AclSize = 64000; + println!("SD -> ACL: {:?}", acl); + println!("SD -> ACL valid: {:?}", IsValidAcl(&acl)); + use std::ffi::c_void; + println!("Getting ACEs..."); + for n in 0..acl.AceCount { + let mut pace: *mut c_void = std::ptr::null_mut(); + GetAce(&acl, n as u32, &mut pace); + let ace_header: *mut ACE_HEADER = pace as *mut ACE_HEADER; + println!("{:?}", *ace_header); + } + println!(); +/* + let mut aclc = ACL::default(); + InitializeAcl(&mut aclc, 64, ACL_REVISION_DS); + println!("SD -> ACLC valid: {:?}", IsValidAcl(&aclc)); + println!("ACLC: {:?}", aclc); + println!("Getting ACEs..."); + for n in 0..aclc.AceCount { + let mut pace: *mut c_void = std::ptr::null_mut(); + GetAce(&aclc, n as u32, &mut pace); + let ace: *mut SYSTEM_AUDIT_ACE = pace as *mut SYSTEM_AUDIT_ACE; + println!("ACE: {:?}", *ace); + } + println!();*/ + + if *ptr_mut != std::ptr::null_mut() { + println!("The ACL contains previous information."); + println!("ACL info: {:?}", **ptr_mut); + println!(); + **ptr_mut + } else { + ACL::default() + } + +} + +// ---------------------------------------------------------------------------- + +pub unsafe fn add_security_ace(mut acl: *mut ACL, sid: PSID) { + // Add audit access ACEs for the desired events + println!("Adding Audit Access Ace"); + match AddAuditAccessObjectAce( + acl, + ACE_REVISION((*acl).AclRevision as u32), + INHERITED_ACE, + GENERIC_ALL.0, + None, + None, + sid, + true, + true){ + Ok(_v) => println!("Added Audit Access Ace."), + Err(e) => println!("Error adding Audit Ace, {:?}", e) + }; +} + +// ---------------------------------------------------------------------------- + +pub unsafe fn set_security_info(handle: HANDLE, mut acl: *mut ACL) { + // Set the new SACL in the security descriptor + let result = SetSecurityInfo(handle, SE_FILE_OBJECT, SACL_SECURITY_INFORMATION, + None, None, None, Some(acl as *mut _ as *mut _), + ); + if result != WIN32_ERROR(0) { + println!("Failed to set security info: {:?}", result); + return; + } else { + println!("Security info set!") + } +} \ No newline at end of file