Compare commits
2
Commits
v0.3.0
...
99813ae2a3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99813ae2a3 | ||
|
|
90cf4927af |
@@ -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 30–50% steigern.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -106,23 +106,32 @@ if (-not $Release) {
|
|||||||
|
|
||||||
|
|
||||||
$UploadUrl = "$GiteaUrl/api/v1/repos/$Owner/$Repo/releases/$($Release.id)/assets"
|
$UploadUrl = "$GiteaUrl/api/v1/repos/$Owner/$Repo/releases/$($Release.id)/assets"
|
||||||
|
$AuthHeader = "Authorization: token $Token"
|
||||||
|
|
||||||
# ZIP Asset hochladen
|
# ZIP Asset hochladen
|
||||||
Write-Host "Lade sanctum-$TagName-windows-x86_64.zip hoch..." -ForegroundColor Cyan
|
Write-Host "Lade sanctum-$TagName-windows-x86_64.zip hoch..." -ForegroundColor Cyan
|
||||||
$ZipResult = & curl.exe -s -X POST "$UploadUrl?name=sanctum-$TagName-windows-x86_64.zip" `
|
$ZipPathNorm = $ZipFile.Replace('\', '/')
|
||||||
-H "Authorization: token $Token" `
|
$ZipUrl = "$UploadUrl?name=sanctum-$TagName-windows-x86_64.zip"
|
||||||
-F "attachment=@$ZipFile"
|
$ZipResult = & curl.exe -s -X POST $ZipUrl -H $AuthHeader -F "attachment=@$ZipPathNorm"
|
||||||
|
|
||||||
Write-Host "[OK] sanctum-$TagName-windows-x86_64.zip hochgeladen." -ForegroundColor Green
|
Write-Host "[OK] sanctum-$TagName-windows-x86_64.zip hochgeladen." -ForegroundColor Green
|
||||||
|
|
||||||
# SHA256SUMS.txt hochladen
|
# SHA256SUMS.txt hochladen
|
||||||
Write-Host "Lade SHA256SUMS.txt hoch..." -ForegroundColor Cyan
|
Write-Host "Lade SHA256SUMS.txt hoch..." -ForegroundColor Cyan
|
||||||
$SumResult = & curl.exe -s -X POST "$UploadUrl?name=SHA256SUMS.txt" `
|
$SumPathNorm = $ChecksumFile.Replace('\', '/')
|
||||||
-H "Authorization: token $Token" `
|
$SumUrl = "$UploadUrl?name=SHA256SUMS.txt"
|
||||||
-F "attachment=@$ChecksumFile"
|
$SumResult = & curl.exe -s -X POST $SumUrl -H $AuthHeader -F "attachment=@$SumPathNorm"
|
||||||
|
|
||||||
Write-Host "[OK] SHA256SUMS.txt hochgeladen." -ForegroundColor Green
|
Write-Host "[OK] SHA256SUMS.txt hochgeladen." -ForegroundColor Green
|
||||||
|
|
||||||
|
# Standalone EXE hochladen
|
||||||
|
$ExeFile = Join-Path $ProjectRoot "target\release\sanctum.exe"
|
||||||
|
if (Test-Path $ExeFile) {
|
||||||
|
Write-Host "Lade sanctum.exe hoch..." -ForegroundColor Cyan
|
||||||
|
$ExePathNorm = $ExeFile.Replace('\', '/')
|
||||||
|
$ExeUrl = "$UploadUrl?name=sanctum.exe"
|
||||||
|
$ExeResult = & curl.exe -s -X POST $ExeUrl -H $AuthHeader -F "attachment=@$ExePathNorm"
|
||||||
|
Write-Host "[OK] sanctum.exe hochgeladen." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Write-Host "`n============================================================" -ForegroundColor Green
|
Write-Host "`n============================================================" -ForegroundColor Green
|
||||||
Write-Host " Release $TagName erfolgreich auf Gitea veroeffentlicht!" -ForegroundColor Green
|
Write-Host " Release $TagName erfolgreich auf Gitea veroeffentlicht!" -ForegroundColor Green
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user