From 99813ae2a39b2f15b2bbe97562b5a26d771ee048 Mon Sep 17 00:00:00 2001 From: harald Date: Thu, 10 Sep 2026 12:27:57 +0200 Subject: [PATCH] feat(security): add Win32 VirtualLock memory protection, CFA error diagnostics, and ShellBag OpSec --- README.md | 21 +++++++++++++++++++++ src/carrier.rs | 11 +++++++++++ src/mount.rs | 12 ++++++++++-- src/storage.rs | 20 +++++++++++++++++++- src/vfs.rs | 15 +++++++++++++++ src/windows.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ba15ba..200534a 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,27 @@ Sanctum schützt vertrauliche Daten vor unbeabsichtigten Windows-Spuren: 1. **Anti-Leak Dateifilter**: Unterdrückt das Anlegen von `Thumbs.db`, `desktop.ini`, Office-Sperrdateien (`~$*`), temporären Dateien (`*.tmp`) und NTFS Alternate Data Streams (`:Zone.Identifier`). 2. **Inactivity Auto-Lock**: Erkennt Inaktivität anhand echter Lese-/Schreibzugriffe und trennt den Container automatisch nach Erreichen des Timeouts. 3. **Session Lock Detection**: Reagiert über `WTSRegisterSessionNotification` sofort auf Windows-Sitzungssperren (`Win + L`) oder Abmeldungen und schließt den Container blitzschnell ab. +4. **RAM-Paging-Schutz (`VirtualLock`)**: Verriegelt sensible Schlüsselstrukturen (`DEK`) im physischen RAM, um das Auslagern in `pagefile.sys` oder `swapfile.sys` durch den Windows Memory Manager zu verhindern. +5. **DFIR ShellBag-Schutz**: Unterdrückt das automatische Öffnen des Windows Explorers für den Hidden Vault, um zu verhindern, dass vertrauliche Ordnernamen persistent in `UsrClass.dat` protokolliert werden. + +--- + +## 🔧 Windows-Kompatibilität & AV/EDR-Tuning + +### Windows Defender SmartScreen ("Unbekannter Herausgeber") +Wird die Binärdatei über einen Webbrowser heruntergeladen, versieht Windows sie mit dem *Mark-of-the-Web* (MotW). Da Open-Source-Projekte über kein kostenpflichtiges EV-Code-Signing-Zertifikat verfügen, zeigt Windows beim ersten Start den blauen SmartScreen-Dialog: +- **Einmalige Freigabe im Dialog:** Klick auf **"Weitere Informationen"** $\to$ **"Trotzdem ausführen"**. +- **Schnellfreigabe via PowerShell:** + ```powershell + Unblock-File .\sanctum.exe + ``` + +### Windows Defender "Überwachter Ordnerzugriff" (Controlled Folder Access - CFA) +Ist der erweiterte Ransomware-Schutz von Windows aktiv, kann Defender den Schreibzugriff auf Containerdateien in Standard-Benutzerordnern (`Dokumente`, `Desktop`) mit Fehler 5 (*Zugriff verweigert*) blockieren: +- **Abhilfe:** Erlauben Sie `sanctum.exe` in den Windows-Sicherheitseinstellungen (*Viren- & Bedrohungsschutz* $\to$ *Ransomware-Schutz verwalten* $\to$ *App durch überwachten Ordnerzugriff zulassen*), oder speichern Sie Container in einem separaten Verzeichnis (z. B. `C:\Sanctum\`). + +### I/O-Performance-Tuning +Windows Defender scannt standardmäßig jeden modifizierten 1-MB-Chunk in Echtzeit. Durch das Hinzufügen der Dateiendung `*.sanctum` zu den Defender-Ausschlüssen lässt sich der I/O-Durchsatz bei großen Kopiervorgängen um 30–50% steigern. --- diff --git a/src/carrier.rs b/src/carrier.rs index d64b216..ed84ecd 100644 --- a/src/carrier.rs +++ b/src/carrier.rs @@ -345,6 +345,13 @@ impl CarrierFsInner { } } +impl Drop for CarrierFsInner { + fn drop(&mut self) { + crate::windows::unlock_memory(self.dek_outer.as_ptr(), 32); + crate::windows::unlock_memory(self.dek_inner.as_ptr(), 32); + } +} + /// WebDAV-Filesystem-Treiber für den steganografischen Alibi-Carrier (Hidden Vault). #[derive(Clone)] pub struct CarrierFs { @@ -380,6 +387,10 @@ impl CarrierFs { .map(|d| d.as_secs()) .unwrap_or(0); + // Forensischer RAM-Paging-Schutz via VirtualLock + crate::windows::lock_memory(dek_outer.as_ptr(), 32); + crate::windows::lock_memory(dek_inner.as_ptr(), 32); + let inner = CarrierFsInner { db, carrier_node_id, diff --git a/src/mount.rs b/src/mount.rs index 4f2f7a3..c12a669 100644 --- a/src/mount.rs +++ b/src/mount.rs @@ -239,9 +239,17 @@ pub async fn mount_container( return Err(e); } - // Optional automatisch im Windows Explorer öffnen + // Optional automatisch im Windows Explorer öffnen (mit DFIR ShellBag-Schutz für Hidden Vault) if open_explorer { - let _ = crate::windows::open_in_explorer(drive_letter); + if vault_id == 1 { + println!( + " {} {}", + ui::yellow("[!]"), + ui::dim("OpSec-Schutz: Explorer-Auto-Open für Hidden Vault unterdrückt (verhindert persistente ShellBag-Spuren in UsrClass.dat).") + ); + } else { + let _ = crate::windows::open_in_explorer(drive_letter); + } } // System-Tray Initialisierung diff --git a/src/storage.rs b/src/storage.rs index e03dfea..5b10098 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -158,7 +158,25 @@ fn current_timestamp() -> u64 { impl Database { /// Öffnet oder erstellt die Container-Datenbank und initialisiert die Pragmas. pub fn open>(path: P) -> Result { - let conn = Connection::open(path)?; + let path_ref = path.as_ref(); + let conn = match Connection::open(path_ref) { + Ok(c) => c, + Err(e) => { + let err_str = e.to_string(); + if err_str.contains("Access is denied") + || err_str.contains("permission denied") + || err_str.contains("os error 5") + { + bail!( + "Zugriff auf '{}' verweigert (OS Fehler 5 / Access Denied).\n\ + [!] Möglicherweise blockiert durch Windows Defender 'Überwachter Ordnerzugriff' (Controlled Folder Access).\n\ + [i] Abhilfe: Fügen Sie 'sanctum.exe' in den Windows-Sicherheitseinstellungen (Viren- & Bedrohungsschutz -> Ransomware-Schutz -> Überwachter Ordnerzugriff) als erlaubte App hinzu, oder platzieren Sie den Container außerhalb geschützter Benutzerordner.", + path_ref.display() + ); + } + return Err(e.into()); + } + }; let db = Self { conn: Arc::new(Mutex::new(conn)), }; diff --git a/src/vfs.rs b/src/vfs.rs index dcddd7b..e2fd490 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -469,6 +469,12 @@ impl SanctumFs { None }; + // Forensischer RAM-Paging-Schutz via VirtualLock (verhindert Auslagerung in pagefile.sys) + crate::windows::lock_memory(dek_arc.as_ptr(), 32); + if let Some(ref c_dek) = carrier_dek_arc { + crate::windows::lock_memory(c_dek.as_ptr(), 32); + } + Self { db, dek: dek_arc, @@ -550,6 +556,15 @@ impl SanctumFs { } } +impl Drop for SanctumFs { + fn drop(&mut self) { + crate::windows::unlock_memory(self.dek.as_ptr(), 32); + if let Some(ref c_dek) = self.carrier_dek { + crate::windows::unlock_memory(c_dek.as_ptr(), 32); + } + } +} + impl DavFileSystem for SanctumFs { fn open<'a>( &'a self, diff --git a/src/windows.rs b/src/windows.rs index b1e7d57..3457861 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -527,10 +527,56 @@ pub fn start_console_ctrl_monitor( } } +/// Verriegelt einen Speicherbereich im physischen RAM (verhindert Paging in pagefile.sys / swapfile.sys). +pub fn lock_memory(ptr: *const u8, len: usize) -> bool { + #[cfg(windows)] + { + extern "system" { + fn VirtualLock(lpaddress: *const std::ffi::c_void, dwsize: usize) -> i32; + } + if ptr.is_null() || len == 0 { + return false; + } + unsafe { VirtualLock(ptr as *const std::ffi::c_void, len) != 0 } + } + #[cfg(not(windows))] + { + let _ = (ptr, len); + false + } +} + +/// Entriegelt einen zuvor mit `lock_memory` geschützten Speicherbereich im RAM. +pub fn unlock_memory(ptr: *const u8, len: usize) -> bool { + #[cfg(windows)] + { + extern "system" { + fn VirtualUnlock(lpaddress: *const std::ffi::c_void, dwsize: usize) -> i32; + } + if ptr.is_null() || len == 0 { + return false; + } + unsafe { VirtualUnlock(ptr as *const std::ffi::c_void, len) != 0 } + } + #[cfg(not(windows))] + { + let _ = (ptr, len); + false + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_virtual_lock_memory_lifecycle() { + let buffer = vec![0x42u8; 4096]; + let _ = lock_memory(buffer.as_ptr(), buffer.len()); + let _ = unlock_memory(buffer.as_ptr(), buffer.len()); + assert_eq!(buffer[0], 0x42); + } + #[test] fn test_find_next_available_drive() { let drive = find_next_available_drive().expect("Find next drive");