# Lokaler Server für den System-Scanner (nur erreichbar über localhost) param([int]$Port = 8080) $root = $PSScriptRoot function Get-SystemData { $os = Get-CimInstance Win32_OperatingSystem $cs = Get-CimInstance Win32_ComputerSystem $cpu = @(Get-CimInstance Win32_Processor) $load = ($cpu | Measure-Object -Property LoadPercentage -Average).Average $gpu = @(Get-CimInstance Win32_VideoController) $bios = Get-CimInstance Win32_BIOS $disks = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object { [pscustomobject]@{ name = $_.DeviceID; label = $_.VolumeName; fs = $_.FileSystem total = [double]$_.Size; free = [double]$_.FreeSpace } }) $net = @(Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "IPEnabled=true" | ForEach-Object { [pscustomobject]@{ name = $_.Description; mac = $_.MACAddress ip = @($_.IPAddress | Where-Object { $_ }) } }) $procs = @(Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 8 | ForEach-Object { [pscustomobject]@{ name = $_.ProcessName; mem = [double]$_.WorkingSet64 } }) $battery = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue | Select-Object -First 1 [pscustomobject]@{ host = $env:COMPUTERNAME user = $env:USERNAME os = [pscustomobject]@{ name = $os.Caption; version = $os.Version; build = $os.BuildNumber arch = $os.OSArchitecture; boot = $os.LastBootUpTime.ToString("o") } machine = [pscustomobject]@{ maker = $cs.Manufacturer; model = $cs.Model; bios = $bios.SMBIOSBIOSVersion } cpu = [pscustomobject]@{ name = $cpu[0].Name.Trim(); cores = ($cpu | Measure-Object NumberOfCores -Sum).Sum threads = ($cpu | Measure-Object NumberOfLogicalProcessors -Sum).Sum mhz = $cpu[0].MaxClockSpeed; load = [double]$load } ram = [pscustomobject]@{ total = [double]$os.TotalVisibleMemorySize * 1KB free = [double]$os.FreePhysicalMemory * 1KB } gpu = @($gpu | ForEach-Object { [pscustomobject]@{ name = $_.Name; vram = [double]$_.AdapterRAM; driver = $_.DriverVersion } }) disks = $disks network = $net procs = $procs battery = if ($battery) { [pscustomobject]@{ percent = $battery.EstimatedChargeRemaining; status = $battery.BatteryStatus } } else { $null } scanned = (Get-Date).ToString("o") } } # ---------- Sicherheits- und Datenschutz-Audit (nur lesend, ändert nichts) ---------- function Reg($path, $name) { try { (Get-ItemProperty -Path $path -Name $name -ErrorAction Stop).$name } catch { $null } } function Get-SecurityData { $list = New-Object System.Collections.ArrayList # status: ok | warn | bad | unknown weight: Gewicht in der Gesamtwertung function C($id, $cat, $title, $status, $detail, $fix, $w) { [void]$list.Add([pscustomobject]@{ id = $id; cat = $cat; title = $title; status = $status detail = $detail; fix = $fix; weight = $w }) } $needAdmin = "Nicht prüfbar ohne Administratorrechte." $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $elevated = ([Security.Principal.WindowsPrincipal]$identity).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) # --- Schutz & Verschlüsselung --- $mp = $null; try { $mp = Get-MpComputerStatus -ErrorAction Stop } catch {} if ($mp) { if ($mp.RealTimeProtectionEnabled) { C 'av' 'schutz' 'Echtzeitschutz (Virenschutz)' 'ok' 'Microsoft Defender schützt in Echtzeit.' '' 3 } else { C 'av' 'schutz' 'Echtzeitschutz (Virenschutz)' 'bad' 'Der Echtzeitschutz von Defender ist aus (oder ein anderes Virenschutzprogramm ist aktiv).' 'Einstellungen > Datenschutz & Sicherheit > Windows-Sicherheit > Viren- & Bedrohungsschutz > Echtzeitschutz einschalten.' 3 } $age = [int]$mp.AntivirusSignatureAge $st = if ($age -le 3) { 'ok' } elseif ($age -le 7) { 'warn' } else { 'bad' } C 'sig' 'schutz' 'Aktualität der Virendefinitionen' $st "Letzte Aktualisierung vor $age Tag(en)." 'Windows-Sicherheit > Viren- & Bedrohungsschutz > Schutzupdates > Nach Updates suchen.' 2 if ($mp.IsTamperProtected) { C 'tamper' 'schutz' 'Manipulationsschutz' 'ok' 'Defender-Einstellungen sind vor Manipulation durch Schadsoftware geschützt.' '' 1 } else { C 'tamper' 'schutz' 'Manipulationsschutz' 'warn' 'Der Manipulationsschutz ist aus.' 'Windows-Sicherheit > Viren- & Bedrohungsschutz > Einstellungen verwalten > Manipulationsschutz.' 1 } } else { C 'av' 'schutz' 'Virenschutz' 'unknown' 'Der Status von Defender konnte nicht gelesen werden (evtl. anderes Produkt installiert).' 'Windows-Sicherheit öffnen und den Virenschutz prüfen.' 3 } try { $fw = @(Get-NetFirewallProfile -ErrorAction Stop) $off = @($fw | Where-Object { $_.Enabled.ToString() -ne 'True' } | ForEach-Object { $_.Name }) if ($off.Count -eq 0) { C 'fw' 'schutz' 'Firewall' 'ok' 'Aktiv für alle Netzwerkprofile (Domäne, Privat, Öffentlich).' '' 3 } else { C 'fw' 'schutz' 'Firewall' 'bad' ("Ausgeschaltet für: " + ($off -join ', ')) 'Windows-Sicherheit > Firewall- & Netzwerkschutz > Profil wählen > Firewall einschalten.' 3 } } catch { C 'fw' 'schutz' 'Firewall' 'unknown' 'Status konnte nicht gelesen werden.' '' 3 } try { $bl = Get-BitLockerVolume -MountPoint $env:SystemDrive -ErrorAction Stop if ($bl.ProtectionStatus.ToString() -eq 'On') { C 'bitlocker' 'schutz' 'Laufwerksverschlüsselung (BitLocker)' 'ok' "Systemlaufwerk $env:SystemDrive ist verschlüsselt." '' 3 } else { C 'bitlocker' 'schutz' 'Laufwerksverschlüsselung (BitLocker)' 'bad' "Systemlaufwerk $env:SystemDrive ist nicht verschlüsselt. Bei Diebstahl des Geräts sind alle Daten lesbar." 'Einstellungen > Datenschutz & Sicherheit > Geräteverschlüsselung bzw. Systemsteuerung > BitLocker-Laufwerkverschlüsselung.' 3 } } catch { C 'bitlocker' 'schutz' 'Laufwerksverschlüsselung (BitLocker)' 'unknown' $needAdmin 'Server als Administrator starten, um den Status zu prüfen.' 3 } $sb = Reg 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' 'UEFISecureBootEnabled' if ($sb -eq 1) { C 'secboot' 'schutz' 'Secure Boot' 'ok' 'Aktiv. Beim Start werden nur signierte Bootloader geladen.' '' 2 } elseif ($sb -eq 0) { C 'secboot' 'schutz' 'Secure Boot' 'bad' 'Aus. Bootkits werden nicht abgewehrt.' 'Im UEFI/BIOS unter Boot/Security „Secure Boot“ aktivieren.' 2 } else { C 'secboot' 'schutz' 'Secure Boot' 'unknown' 'Status nicht ermittelbar (evtl. Legacy-BIOS).' 'Im UEFI/BIOS prüfen.' 2 } try { if (-not $elevated) { throw "admin" } $tpm = Get-Tpm -ErrorAction Stop if ($tpm.TpmPresent -and $tpm.TpmReady) { C 'tpm' 'schutz' 'Sicherheitschip (TPM)' 'ok' 'TPM ist vorhanden und einsatzbereit.' '' 1 } elseif ($tpm.TpmPresent) { C 'tpm' 'schutz' 'Sicherheitschip (TPM)' 'warn' 'TPM ist vorhanden, aber nicht bereit.' 'tpm.msc öffnen und Status prüfen; ggf. im UEFI aktivieren.' 1 } else { C 'tpm' 'schutz' 'Sicherheitschip (TPM)' 'warn' 'Kein TPM gefunden.' 'Im UEFI/BIOS fTPM/PTT aktivieren.' 1 } } catch { C 'tpm' 'schutz' 'Sicherheitschip (TPM)' 'unknown' $needAdmin '' 1 } $hvci = Reg 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' 'Enabled' if ($hvci -eq 1) { C 'hvci' 'schutz' 'Speicherintegrität (Kernisolierung)' 'ok' 'Aktiv. Schädlicher Code kann sich schwerer im Kernel einnisten.' '' 2 } else { C 'hvci' 'schutz' 'Speicherintegrität (Kernisolierung)' 'warn' 'Nicht aktiv.' 'Windows-Sicherheit > Gerätesicherheit > Kernisolierung > Speicherintegrität einschalten.' 2 } $ss = Reg 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer' 'SmartScreenEnabled' if ($ss -eq 'Off') { C 'smartscreen' 'schutz' 'SmartScreen (Schutz vor schädlichen Downloads)' 'bad' 'SmartScreen ist ausgeschaltet.' 'Windows-Sicherheit > App- & Browsersteuerung > Reputationsbasierter Schutz.' 1 } else { C 'smartscreen' 'schutz' 'SmartScreen (Schutz vor schädlichen Downloads)' 'ok' 'SmartScreen ist aktiv.' '' 1 } # --- Zugang & Angriffsfläche --- $hf = $null try { $hf = Get-HotFix -ErrorAction Stop | Where-Object { $_.InstalledOn } | Sort-Object InstalledOn -Descending | Select-Object -First 1 } catch {} if ($hf) { $days = [int]((Get-Date) - $hf.InstalledOn).TotalDays $st = if ($days -le 35) { 'ok' } elseif ($days -le 90) { 'warn' } else { 'bad' } C 'patch' 'zugang' 'Sicherheitsupdates' $st "Letztes Update ($($hf.HotFixID)) vor $days Tagen installiert. Fehlende Updates sind die häufigste Ursache für ausnutzbare Sicherheitslücken." 'Einstellungen > Windows Update > Nach Updates suchen.' 3 } else { C 'patch' 'zugang' 'Sicherheitsupdates' 'unknown' 'Update-Verlauf nicht lesbar.' 'Einstellungen > Windows Update prüfen.' 3 } $reboot = (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') -or (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending') if ($reboot) { C 'reboot' 'zugang' 'Neustart für Updates ausstehend' 'warn' 'Installierte Updates sind erst nach einem Neustart wirksam.' 'Rechner neu starten.' 1 } else { C 'reboot' 'zugang' 'Neustart für Updates ausstehend' 'ok' 'Kein Neustart nötig.' '' 1 } if ((Reg 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' 'EnableLUA') -eq 1) { C 'uac' 'zugang' 'Benutzerkontensteuerung (UAC)' 'ok' 'Aktiv. Programme brauchen Bestätigung für Admin-Rechte.' '' 3 } else { C 'uac' 'zugang' 'Benutzerkontensteuerung (UAC)' 'bad' 'Ausgeschaltet. Programme können ungefragt Admin-Rechte erlangen.' 'Startmenü > „UAC“ suchen > Einstellungen der Benutzerkontensteuerung ändern.' 3 } $rdp = Reg 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' 'fDenyTSConnections' if ($rdp -eq 1) { C 'rdp' 'zugang' 'Remotedesktop' 'ok' 'Eingehende Remotedesktop-Verbindungen sind blockiert.' '' 2 } else { C 'rdp' 'zugang' 'Remotedesktop' 'bad' 'Fernzugriff ist erlaubt. Ein beliebtes Angriffsziel (Brute-Force, Ransomware).' 'Einstellungen > System > Remotedesktop ausschalten, falls nicht benötigt.' 2 } try { $smb1 = (Get-SmbServerConfiguration -ErrorAction Stop).EnableSMB1Protocol if ($smb1) { C 'smb1' 'zugang' 'Veraltetes SMBv1-Protokoll' 'bad' 'SMBv1 ist aktiv (u. a. von WannaCry ausgenutzt).' 'Windows-Features ein-/ausschalten > „SMB 1.0/CIFS-Dateifreigabeunterstützung“ deaktivieren.' 2 } else { C 'smb1' 'zugang' 'Veraltetes SMBv1-Protokoll' 'ok' 'SMBv1 ist deaktiviert.' '' 2 } } catch { C 'smb1' 'zugang' 'Veraltetes SMBv1-Protokoll' 'unknown' $needAdmin '' 2 } try { if ((Get-LocalUser -Name 'Guest' -ErrorAction Stop).Enabled) { C 'guest' 'zugang' 'Gastkonto' 'warn' 'Das Gastkonto ist aktiviert.' 'Computerverwaltung > Lokale Benutzer > Gast > deaktivieren.' 1 } else { C 'guest' 'zugang' 'Gastkonto' 'ok' 'Deaktiviert.' '' 1 } } catch { C 'guest' 'zugang' 'Gastkonto' 'unknown' 'Nicht lesbar.' '' 1 } $isAdminMember = @($identity.Groups | ForEach-Object { $_.Value }) -contains 'S-1-5-32-544' if ($isAdminMember) { C 'adminuser' 'zugang' 'Täglich genutztes Konto' 'warn' "Das Konto „$env:USERNAME“ ist Administrator. Schadsoftware erhält so leichter weitreichende Rechte." 'Für den Alltag ein Standardkonto verwenden (Einstellungen > Konten > Weitere Benutzer).' 1 } else { C 'adminuser' 'zugang' 'Täglich genutztes Konto' 'ok' 'Standardkonto ohne Administratorrechte.' '' 1 } if ((Reg 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' 'AutoAdminLogon') -eq '1') { C 'autologon' 'zugang' 'Automatische Anmeldung' 'bad' 'Windows meldet sich ohne Passwort an; Zugangsdaten liegen in der Registry.' 'netplwiz öffnen > „Benutzer müssen Name und Kennwort eingeben“ aktivieren.' 2 } else { C 'autologon' 'zugang' 'Automatische Anmeldung' 'ok' 'Aus.' '' 2 } try { $risky = @{ 21 = 'FTP'; 23 = 'Telnet'; 139 = 'NetBIOS'; 445 = 'SMB'; 3389 = 'RDP'; 5900 = 'VNC'; 5985 = 'WinRM' } $listen = @(Get-NetTCPConnection -State Listen -ErrorAction Stop | Where-Object { $_.LocalAddress -notin '127.0.0.1', '::1' }) $ports = @($listen | Select-Object -ExpandProperty LocalPort -Unique) $hit = @($ports | Where-Object { $risky.ContainsKey([int]$_) } | Sort-Object | ForEach-Object { "$_ ($($risky[[int]$_]))" }) if ($hit.Count -eq 0) { C 'ports' 'zugang' 'Offene Netzwerkports' 'ok' "$($ports.Count) Ports lauschen, keiner davon ein bekannter Risiko-Dienst." '' 2 } else { C 'ports' 'zugang' 'Offene Netzwerkports' 'warn' ("Risiko-Dienste lauschen im Netzwerk: " + ($hit -join ', ') + ". Insgesamt $($ports.Count) Ports offen. Die Firewall kann sie trotzdem abschirmen.") 'Nicht benötigte Dienste beenden (services.msc) oder in der Firewall blockieren; Dateifreigaben prüfen.' 2 } } catch { C 'ports' 'zugang' 'Offene Netzwerkports' 'unknown' 'Nicht lesbar.' '' 2 } # --- Datenschutz --- $tele = Reg 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' 'AllowTelemetry' if ($null -eq $tele) { $tele = Reg 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection' 'AllowTelemetry' } if ($null -ne $tele -and $tele -le 1) { C 'telemetry' 'datenschutz' 'Diagnosedaten an Microsoft' 'ok' 'Per Richtlinie auf das Minimum begrenzt.' '' 2 } elseif ($null -ne $tele) { C 'telemetry' 'datenschutz' 'Diagnosedaten an Microsoft' 'warn' 'Erweiterte oder vollständige Diagnosedaten sind erlaubt.' 'Einstellungen > Datenschutz & Sicherheit > Diagnose & Feedback > Optionale Diagnosedaten aus.' 2 } else { C 'telemetry' 'datenschutz' 'Diagnosedaten an Microsoft' 'warn' 'Nicht per Richtlinie begrenzt; Windows sendet mindestens die erforderlichen Diagnosedaten.' 'Einstellungen > Datenschutz & Sicherheit > Diagnose & Feedback: „Optionale Diagnosedaten senden“ ausschalten.' 2 } if ((Reg 'HKCU:\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' 'Enabled') -eq 0) { C 'adid' 'datenschutz' 'Werbe-ID' 'ok' 'Apps dürfen die Werbe-ID nicht verwenden.' '' 1 } else { C 'adid' 'datenschutz' 'Werbe-ID' 'warn' 'Apps dürfen mit der Werbe-ID personalisierte Werbung ausspielen.' 'Einstellungen > Datenschutz & Sicherheit > Allgemein > Werbe-ID ausschalten.' 1 } if ((Reg 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Privacy' 'TailoredExperiencesWithDiagnosticDataEnabled') -eq 0) { C 'tailored' 'datenschutz' 'Maßgeschneiderte Erlebnisse' 'ok' 'Diagnosedaten werden nicht für Empfehlungen genutzt.' '' 1 } else { C 'tailored' 'datenschutz' 'Maßgeschneiderte Erlebnisse' 'warn' 'Diagnosedaten dürfen für personalisierte Tipps und Werbung genutzt werden.' 'Einstellungen > Datenschutz & Sicherheit > Diagnose & Feedback > Maßgeschneiderte Erlebnisse ausschalten.' 1 } if ((Reg 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' 'EnableActivityFeed') -eq 0) { C 'activity' 'datenschutz' 'Aktivitätsverlauf' 'ok' 'Per Richtlinie deaktiviert.' '' 1 } else { C 'activity' 'datenschutz' 'Aktivitätsverlauf' 'warn' 'Windows darf Aktivitäten (Apps, Dateien, Webseiten) aufzeichnen.' 'Einstellungen > Datenschutz & Sicherheit > Aktivitätsverlauf: Speicherung ausschalten.' 1 } foreach ($cap in @(@('location', 'Standortzugriff', 'Einstellungen > Datenschutz & Sicherheit > Standort'), @('microphone', 'Mikrofonzugriff für Apps', 'Einstellungen > Datenschutz & Sicherheit > Mikrofon'), @('webcam', 'Kamerazugriff für Apps', 'Einstellungen > Datenschutz & Sicherheit > Kamera'))) { $v = Reg "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\$($cap[0])" 'Value' if ($v -eq 'Deny') { C $cap[0] 'datenschutz' $cap[1] 'ok' 'Für Apps global gesperrt.' '' 1 } else { C $cap[0] 'datenschutz' $cap[1] 'warn' 'Apps dürfen grundsätzlich zugreifen. Prüfe, welche Apps das wirklich brauchen.' "$($cap[2]): nicht benötigten Apps den Zugriff entziehen." 1 } } if ((Reg 'HKCU:\Software\Microsoft\Clipboard' 'EnableClipboardHistory') -eq 1) { C 'clip' 'datenschutz' 'Zwischenablageverlauf' 'warn' 'Kopierte Inhalte (z. B. Passwörter) bleiben im Verlauf gespeichert.' 'Einstellungen > System > Zwischenablage > Zwischenablageverlauf ausschalten.' 1 } else { C 'clip' 'datenschutz' 'Zwischenablageverlauf' 'ok' 'Aus.' '' 1 } if ((Reg 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Search' 'BingSearchEnabled') -eq 0 -or (Reg 'HKCU:\Software\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions') -eq 1) { C 'bing' 'datenschutz' 'Websuche in der Windows-Suche' 'ok' 'Suchanfragen bleiben lokal.' '' 1 } else { C 'bing' 'datenschutz' 'Websuche in der Windows-Suche' 'warn' 'Eingaben in der Windows-Suche können an Bing gesendet werden.' 'Einstellungen > Datenschutz & Sicherheit > Suchberechtigungen: Cloudinhaltssuche/Suchverlauf ausschalten.' 1 } if ((Reg 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' 'Start_TrackProgs') -eq 0) { C 'trackprogs' 'datenschutz' 'Verfolgung gestarteter Apps' 'ok' 'Aus.' '' 1 } else { C 'trackprogs' 'datenschutz' 'Verfolgung gestarteter Apps' 'warn' 'Windows protokolliert, welche Apps du startest.' 'Einstellungen > Personalisierung > Start: „Häufig verwendete Apps anzeigen“ ausschalten.' 1 } [pscustomobject]@{ elevated = $elevated; scanned = (Get-Date).ToString("o"); checks = @($list) } } $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://localhost:$Port/") $listener.Start() Write-Host "System-Scanner läuft auf http://localhost:$Port/ (Beenden mit Strg+C)" try { while ($listener.IsListening) { $ctx = $listener.GetContext() $path = $ctx.Request.Url.AbsolutePath try { if ($path -eq "/api/system") { $bytes = [Text.Encoding]::UTF8.GetBytes((Get-SystemData | ConvertTo-Json -Depth 6 -Compress)) $ctx.Response.ContentType = "application/json; charset=utf-8" } elseif ($path -eq "/api/security") { $bytes = [Text.Encoding]::UTF8.GetBytes((Get-SecurityData | ConvertTo-Json -Depth 6 -Compress)) $ctx.Response.ContentType = "application/json; charset=utf-8" } elseif ($path -eq "/" -or $path -eq "/index.html") { $bytes = [IO.File]::ReadAllBytes((Join-Path $root "index.html")) $ctx.Response.ContentType = "text/html; charset=utf-8" } elseif ($path -eq "/browsercheck.html") { $bytes = [IO.File]::ReadAllBytes((Join-Path $root "browsercheck.html")) $ctx.Response.ContentType = "text/html; charset=utf-8" } else { $ctx.Response.StatusCode = 404 $bytes = [Text.Encoding]::UTF8.GetBytes("Nicht gefunden") } } catch { $ctx.Response.StatusCode = 500 $bytes = [Text.Encoding]::UTF8.GetBytes("Fehler: $($_.Exception.Message)") } $ctx.Response.ContentLength64 = $bytes.Length $ctx.Response.OutputStream.Write($bytes, 0, $bytes.Length) $ctx.Response.Close() } } finally { $listener.Stop() }