From 11f99a588f5f9293839fb52e59d9af9b3e7dc9b7 Mon Sep 17 00:00:00 2001 From: Pierre Cosson Date: Fri, 30 Oct 2026 08:48:13 +0000 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9verser=20les=20fichiers=20vers=20?= =?UTF-8?q?"/"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 +++++++++++++++++++- hardening.ps1 | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 33 ++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 hardening.ps1 create mode 100644 setup.py diff --git a/README.md b/README.md index b5646f7..ab7407f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ -# comi-hardening-windows +# Com itari Windows hardening safe v1 (WAPT source) +SOURCE ONLY: must build and sign with the organization's WAPT tooling. Not yet Windows/WAPT-tested. + +## Scope +Five HKLM registry settings: disable AutoRun and AutoPlay (including non-volume devices), disable lock-screen camera and slideshows. No account / password / LAPS change. No WAPT, firewall, network, SMB, BitLocker, Microsoft Defender, antivirus, remote desktop, printing or power settings change. + +## Safeguards +- Initial values saved once in `%ProgramData%\Comitari\HardeningSafe\original-values.json` before any modifications. +- Reinstallation idempotent; uninstall conditional: restores only values still equal to package target, leaving externally modified values unchanged. +- No reboot forced. Audit implemented. +- State persists after uninstall intentionally, so repeated uninstall does not overwrite third-party edits. +- Policy owner can override package; audit then reports WARNING. + +## Deployment +1. Inspect `setup.py` and `hardening.ps1` in an isolated preproduction repository. +2. Validate registry settings and resulting behavior on a Windows 11 25H2 representative workstation, especially AutoPlay workflow. +3. Build/sign WAPT package with organization's key. Do not distribute unsigned sources as a ready-to-deploy binary. +4. Deploy to 1 test PC, verify WAPT agent/check-in, LapsByWAPT password rotation and recovery, business apps, USB workflows. +5. Expand to 3-5 devices for 48h and later to waves. Remove machine dependency before `wapt-get remove`; see WAPT docs on remove vs uninstall. +6. Verify rollback in pilot via `wapt-get uninstall comitari-hardening-safe` (function call) and validate state. + +IMPORTANT: `wapt-get remove` is NOT assured to execute a custom uninstall() function. Follow your WAPT version's verified removal workflow and audit this before promotion. + +## Deliberately deferred +Windows LAPS controls 10701-10703 (LapsByWAPT alternative); firewall enforcement; VBS/HVCI/Credential Guard; ASR; NTLM/SMB guest/signing; SMBv1 removal; BitLocker; print drivers; user rights; audit logging and command-line collection; remote admin; LLMNR and NetBIOS. These require separate pilot checks. diff --git a/hardening.ps1 b/hardening.ps1 new file mode 100644 index 0000000..54c8cd1 --- /dev/null +++ b/hardening.ps1 @@ -0,0 +1,66 @@ +param([ValidateSet('install','audit','uninstall')][string]$Action = 'audit') +$ErrorActionPreference = 'Stop' +$stateDir = Join-Path $env:ProgramData 'Comitari\HardeningSafe' +$stateFile = Join-Path $stateDir 'original-values.json' +# Only low-impact, machine-wide controls. Deliberately excludes Windows LAPS, accounts, +# services, remote management, networking, firewall policy, BitLocker and Defender. +$settings = @( + @{ Path='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name='NoAutorun'; Value=1 }, + @{ Path='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name='NoDriveTypeAutoRun'; Value=255 }, + @{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer'; Name='NoAutoplayfornonVolume'; Value=1 }, + @{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization'; Name='NoLockScreenCamera'; Value=1 }, + @{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization'; Name='NoLockScreenSlideshow'; Value=1 } +) +function Read-Value($path, $name) { + if (-not (Test-Path -LiteralPath $path)) { return @{ Exists=$false; Value=$null } } + $key = Get-Item -LiteralPath $path + if ($key.GetValueNames() -contains $name) { + return @{ Exists=$true; Value=[int]$key.GetValue($name) } + } + return @{ Exists=$false; Value=$null } +} +function Write-Value($path, $name, [int]$value) { + if (-not (Test-Path -LiteralPath $path)) { New-Item -Path $path -Force | Out-Null } + New-ItemProperty -LiteralPath $path -Name $name -Value $value -PropertyType DWord -Force | Out-Null +} +if ($Action -eq 'install') { + if (-not (Test-Path -LiteralPath $stateDir)) { New-Item -Path $stateDir -ItemType Directory -Force | Out-Null } + if (-not (Test-Path -LiteralPath $stateFile)) { + $original = @() + foreach ($setting in $settings) { + $old = Read-Value $setting.Path $setting.Name + $original += [pscustomobject]@{ Path=$setting.Path; Name=$setting.Name; Exists=$old.Exists; Value=$old.Value; Target=$setting.Value } + } + $json = ConvertTo-Json -InputObject @($original) -Depth 5 + $temp = "$stateFile.tmp" + [System.IO.File]::WriteAllText($temp, $json, [System.Text.UTF8Encoding]::new($false)) + Move-Item -LiteralPath $temp -Destination $stateFile -Force + } + foreach ($setting in $settings) { Write-Value $setting.Path $setting.Name $setting.Value } + Write-Output 'Installed conservative hardening; LapsByWAPT untouched.' +} +elseif ($Action -eq 'audit') { + $failed = @() + foreach ($setting in $settings) { + $actual = Read-Value $setting.Path $setting.Name + if (-not $actual.Exists -or $actual.Value -ne $setting.Value) { $failed += "$($setting.Name) != $($setting.Value)" } + } + if ($failed.Count -gt 0) { $failed | ForEach-Object { Write-Output $_ }; exit 1 } + Write-Output 'All 5 settings compliant.' +} +else { + if (-not (Test-Path -LiteralPath $stateFile)) { throw 'No saved baseline; refusing blind rollback.' } + $original = @(Get-Content -LiteralPath $stateFile -Raw | ConvertFrom-Json) + foreach ($item in $original) { + $current = Read-Value $item.Path $item.Name + # Do not override later changes made by GPO or another administrator. + if (-not $current.Exists -or $current.Value -ne [int]$item.Target) { + Write-Output "Skipped externally changed setting: $($item.Name)" + continue + } + if ($item.Exists) { Write-Value $item.Path $item.Name ([int]$item.Value) } + else { Remove-ItemProperty -LiteralPath $item.Path -Name $item.Name -ErrorAction Stop } + } + # Keep baseline for forensic trace; uninstall is idempotent only for settings still matching targets. + Write-Output 'Conditional rollback completed; LapsByWAPT untouched.' +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b4d8145 --- /dev/null +++ b/setup.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from setuphelpers import * +import os +import subprocess + +uninstallkey = [] + + +def _run(action): + script = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hardening.ps1') + result = subprocess.run(['powershell.exe', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', script, '-Action', action], capture_output=True, text=True) + print(result.stdout) + if result.stderr: + print(result.stderr) + if result.returncode != 0: + raise Exception('Hardening %s failed with code %s' % (action, result.returncode)) + + +def install(): + _run('install') + + +def audit(): + try: + _run('audit') + return 'OK' + except Exception as exc: + print(exc) + return 'WARNING' + + +def uninstall(): + _run('uninstall')