feat(security): add Win32 VirtualLock memory protection, CFA error diagnostics, and ShellBag OpSec

This commit is contained in:
2026-09-10 12:27:57 +02:00
parent 90cf4927af
commit 99813ae2a3
6 changed files with 122 additions and 3 deletions
+21
View File
@@ -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`). 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. 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. 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 3050% steigern.
--- ---
+11
View File
@@ -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). /// WebDAV-Filesystem-Treiber für den steganografischen Alibi-Carrier (Hidden Vault).
#[derive(Clone)] #[derive(Clone)]
pub struct CarrierFs { pub struct CarrierFs {
@@ -380,6 +387,10 @@ impl CarrierFs {
.map(|d| d.as_secs()) .map(|d| d.as_secs())
.unwrap_or(0); .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 { let inner = CarrierFsInner {
db, db,
carrier_node_id, carrier_node_id,
+10 -2
View File
@@ -239,9 +239,17 @@ pub async fn mount_container(
return Err(e); 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 { 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 // System-Tray Initialisierung
+19 -1
View File
@@ -158,7 +158,25 @@ fn current_timestamp() -> u64 {
impl Database { impl Database {
/// Öffnet oder erstellt die Container-Datenbank und initialisiert die Pragmas. /// Öffnet oder erstellt die Container-Datenbank und initialisiert die Pragmas.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> { pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
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 { let db = Self {
conn: Arc::new(Mutex::new(conn)), conn: Arc::new(Mutex::new(conn)),
}; };
+15
View File
@@ -469,6 +469,12 @@ impl SanctumFs {
None 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 { Self {
db, db,
dek: dek_arc, 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 { impl DavFileSystem for SanctumFs {
fn open<'a>( fn open<'a>(
&'a self, &'a self,
+46
View File
@@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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] #[test]
fn test_find_next_available_drive() { fn test_find_next_available_drive() {
let drive = find_next_available_drive().expect("Find next drive"); let drive = find_next_available_drive().expect("Find next drive");