Private/02.BigBrother.Messenger.ps1
|
<#
SPDX-License-Identifier: Apache-2.0 Copyright (c) 2025 Stefan Ploch #> <# .SYNOPSIS Big Brother Messenger - security related messaging and communication for the module. .DESCRIPTION This module provides cmdlets for managing and enforcing security related messaging and communication in the Module. Notes: - Public entry point: Write-SecurityWarning - RiskLevel values currently produced: 'krbtgt','High','Medium' - Enables opt-out via: -Suppress or env var STCRYPT_SUPPRESS_SECURITY_WARNING=1 - Tries to stay pure (no Write-Host) unless -AsString:$false (default) for visibility. - Returns the composed banner text (always) so callers can log it. #> #region Security / Risk Warning Presentation # ---------------------------------------------------------------------- # # # Security / Risk Warning Presentation # # ---------------------------------------------------------------------- # function Write-EmptyBannerLine { <# .SYNOPSIS Write an empty banner line with a specific width and color. #> param( [int]$Width = 82, [ConsoleColor]$Color = 'Red' ) $inner = ' ' * ($Width) Write-Host ("|{0}|" -f $inner) -ForegroundColor $Color } function Write-BannerText { <# .SYNOPSIS Write a banner text with optional centering and color. #> param( [Parameter(Mandatory)][string]$Message, [int]$Width = 82, [switch]$Centered, [ConsoleColor]$Color = 'Red' ) # Width = inner content width (excluding the two border pipes) $lines = $Message -split "(`r`n|`n)" foreach ($l in $lines) { if (-not $l) { Write-EmptyBannerLine -Width $Width -Color $Color; continue } # Break long lines into chunks $remaining = $l while ($remaining.Length -gt 0) { $chunkSize = [Math]::Min($Width, $remaining.Length) $chunk = $remaining.Substring(0,$chunkSize) $remaining = if ($remaining.Length -gt $chunkSize) { $remaining.Substring($chunkSize) } else { '' } $padLeft = 0 $padRight = 0 if ($Centered) { $padLeft = [Math]::Floor(($Width - $chunk.Length)/2) $padRight = $Width - $chunk.Length - $padLeft } else { $padLeft = 0 $padRight = $Width - $chunk.Length } $line = '|' + (' ' * $padLeft) + $chunk + (' ' * $padRight) + '|' Write-Host $line -ForegroundColor $Color } } } function New-SecurityBannerContent { <# .SYNOPSIS Create the content for a security warning banner based on risk level. #> param( [Parameter(Mandatory)][string]$RiskLevel, [Parameter(Mandatory)][string]$SamAccountName ) switch ($RiskLevel.ToLowerInvariant()) { 'krbtgt' { @( 'SECURITY WARNING', '', 'You are exporting / handling KRBTGT key material.', 'Possession enables forging (Golden Tickets) & global Kerberos decryption across the forest.', '', 'Treat as a Tier-0 secret. Strongly restrict storage, transport and lifetime.', 'Perform ONLY in a controlled (lab / IR / recovery) scenario with explicit approval.', '', 'Existence of this file is itself a critical risk indicator.' ) } 'high' { @( 'HIGH RISK KEYTAB', '', "Account: $SamAccountName", 'Domain Controller or high-impact service account keys allow lateral movement, ticket forging for that host / services, and decryption of its Kerberos traffic.', '', 'Handle under change control. Limit distribution. Consider immediate secure deletion after use.' ) } default { @( 'SENSITIVE MATERIAL', '', "Account: $SamAccountName", 'Keytab grants impersonation for this principal and decryption of its Kerberos traffic.', 'Store minimally, transmit over secure channels, and purge after intended use.' ) } } } function Write-SecurityWarning { <# .SYNOPSIS Write a security warning banner based on risk level. #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$RiskLevel, [Parameter(Mandatory)][string]$SamAccountName, [int]$Width = 80, [switch]$Suppress, [switch]$AsString, [switch]$NoColor ) if ($Suppress -or ($env:STCRYPT_SUPPRESS_SECURITY_WARNING -eq '1')) { if ($AsString) { return "" } else { return } } # Normalize internal width (content width inside borders) if ($Width -lt 40) { $Width = 40 } $contentWidth = $Width - 2 # account for border pipes $lines = New-SecurityBannerContent -RiskLevel $RiskLevel -SamAccountName $SamAccountName $borderLine = '+' + ('\' * ($contentWidth)) + '+' $stringBuilder = [System.Text.StringBuilder]::new() [void]$stringBuilder.AppendLine($borderLine) foreach ($l in $lines) { # Manual line wrapping consistent with Write-STBannerText $remaining = $l if (-not $remaining) { [void]$stringBuilder.AppendLine('|' + (' ' * $contentWidth) + '|') continue } while ($remaining.Length -gt 0) { $chunkSize = [Math]::Min($contentWidth, $remaining.Length) $chunk = $remaining.Substring(0,$chunkSize) $remaining = if ($remaining.Length -gt $chunkSize) { $remaining.Substring($chunkSize) } else { '' } $padRight = $contentWidth - $chunk.Length [void]$stringBuilder.AppendLine('|' + $chunk + (' ' * $padRight) + '|') } } [void]$stringBuilder.AppendLine($borderLine) $bannerText = $stringBuilder.ToString().TrimEnd() if ($AsString) { return $bannerText } $color = if ($NoColor) { $null } elseif ($RiskLevel -eq 'krbtgt') { 'Red' } elseif ($RiskLevel -eq 'High') { 'Yellow' } else { 'DarkYellow' } if ($color) { foreach ($outLine in ($bannerText -split "`r?`n")) { Write-Host $outLine -ForegroundColor $color } } else { Write-Host $bannerText } } #endregion |