Powershell : modification du registre pour contourner CredSSP

Au mois de mars, des correctifs Microsoft ont corrigé certaines failles par rapport à CredSSP qui est notamment utilisé pour sécuriser des connexions RDP (voir ce lien). Un message d’erreur peut survenir si un client patché tente de se connecter à un serveur non patché ou vice-versa. Pour faire simple, il est nécessaire que client comme serveur soient patchés pour qu’ils puissent communiquer.

J’ai réalisé un script Powershell permettant de contourner la sécurité via le registre afin que le système ne tienne pas compte du delta de version entre le CredSSP client et serveur. Dans un monde parfait, cela ne devrait pas exister car postes de travail et serveurs devraient être patchés en temps et en heure, mais la réalité est bien évidemment tout autre pour tout un tas de raisons. 👍

#Requires -RunAsAdministrator
$CredSspPath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System »
$ParametersPath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP »
$AllowEncryptionOraclePath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters »
New-Item -Path $CredSspPath -Name « CredSSP » -Force
New-Item -Path $ParametersPath -Name « Parameters » -Force
New-ItemProperty -Path $AllowEncryptionOraclePath -Name « AllowEncryptionOracle » -PropertyType DWORD -Value « 2 » -Force
Write-Host « If everything went fine, please reboot the system for the changes to take effect. »
Sleep 5

Le script doit être lancé en administrateur ; une fois les clefs déployées, il est nécessaire de redémarrer la station qui devrait pouvoir se connecter sans le message d’erreur plus haut, ce qui ne dispense évidemment pas d’une campagne de patch ! 😉

Vous pouvez trouver une version commentée du script sur le serveur de fichiers en cliquant sur ce lien. 💾

Powershell : obtention de la taille d’un share

Voici un petit script Powershell permettant de récupérer la taille d’un ou plusieurs share.

Ce script peut être appelé de deux manières :

  • sans arguments : le script va chercher un fichier texte dans le répertoire d’exécution pour passer sur TOUS les répertoires indiqués dans le fichier (un partage par ligne) ; le but est donc de renseigner tous les partages pour lesquels on souhaite connaître la volumétrie dans le fichier. Le script retourne ensuite un fichier CSV :
 
 
 
 
  •  avec des arguments : en donnant en argument d’exécution du script le serveur et le partage, on récupère la taille dans la console directement :
param([string]$shareHost, [string]$shareName)

function folderSize()
{
param($path)
try {
$byteSize = (Get-ChildItem -path "$path" -Recurse -ErrorAction SilentlyContinue | Measure-Object Length -sum).Sum
if ($byteSize.ToString().Length -lt 4) { return "$byteSize B." }
if ($byteSize.ToString().Length -ge 4 -and $byteSize.ToString().Length -lt 7) { $kbSize = [math]::Round($byteSize/1024,1) ; return "$kbSize KB." }
if ($byteSize.ToString().Length -ge 7 -and $byteSize.ToString().Length -lt 10) { $mbSize = [math]::Round($byteSize/1048576,1) ; return "$mbSize MB." }
if ($byteSize.ToString().Length -ge 10 -and $byteSize.ToString().Length -lt 13) { $gbSize = [math]::Round($byteSize/1073741824,1) ; return "$gbSize GB." }
if ($byteSize.ToString().Length -ge 13) { $tbSize = [math]::Round($byteSize/1099511627776,2) ; return "$tbSize TB." }
}
catch { return "Share doesn't exist or is unreachable or you don't have the required privileges." }
}

Write-Host "Share size retriever Powershell script"
Write-Host "======================================"
Write-Host "Usage: .\win_foldersize.ps1 <SERVER> <SHARE>"
Write-Host "If any parameter is missing, the script will look for a sharelist.txt file in the execution folder and will detect the size of all the folders inside the file."

if ($shareHost -eq "" -or $shareName -eq "")
{
Write-Host "You didn't provide both parameters while running the script. I will now try to read directly the shares from sharelist.txt in the script folder."
$today = Get-Date -f "yyMMdd"
$CsvOutputFile = "share-size-$today.csv"
$InputFile = "sharelist.txt"
$CsvHeader = "Share,Size"
Add-Content $CsvOutputFile $CsvHeader
try { $ShareList = Get-Content $InputFile -ErrorAction Stop }
catch { Write-Host "Unable to read sharelist.txt. Aborting." ; break }
foreach ($Share in $ShareList)
{
$sizeDump = folderSize($Share)
$Dump = "$Share,$sizeDump"
Add-Content $CsvOutputFile $Dump
}
Write-Host "Done, output file : $CsvOutputFile"
}
else {
Write-Host "Both parameters detected."
Write-Host "Server specified: $shareHost"
Write-Host "Share: $shareName"
$shareString = "\\"+"$shareHost"+"\"+"$shareName"
$displaySize = folderSize($shareString)
Write-host $displaySize
}

Powershell : audit des serveurs SQL Server

J’ai développé en prenant comme base ce script sur le TechNet un script Powershell récupérant diverses informations propres aux serveurs SQL Server dont le nom est renseigné dans un fichier CSV.

Le script parcourt dans le fichier CSV spécifié la colonne ayant pour entête ServerName (si jamais votre extract d’origine comprend plusieurs colonnes, comme le nom de l’instance par exemple) et va tenter de récupérer plusieurs informations pour chacun des hostname lus. Une fois un serveur traité, les détails recueillis sont ajoutés à un fichier CSV exploitable par la suite dans Excel par exemple.

Un exemple de fichier d’entrée avec les hostname des serveurs SQL à auditer.

Je n’ai pas réalisé de contrôles particuliers sur l’écriture ou l’accès aux serveurs SQL dans le script, il faudra donc en cas de dysfonctionnement quelconque s’assurer que les fichiers CSV d’entrée et sortie puissent être lus et écrits sur le disque et que l’utilisateur exécutant le script ait un minimum de privilèges sur SQL Server.

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO')

function Get_ProperOS{
param($OS)
if ($OS -like '5.0*') { return "Windows Server 2000" }
elseif ($OS -like '5.2*') { return "Windows Server 2003" }
elseif ($OS -like '6.0*') { return "Windows Server 2008" }
elseif ($OS -like '6.1*') { return "Windows Server 2008 R2" }
elseif ($OS -like '6.2*') { return "Windows Server 2012" }
elseif ($OS -like '6.3*') { return "Windows Server 2012 R2" }
elseif ($OS -like '10.0*') { return "Windows Server 2016" }
else { return "Unknown NT version" }
}

function Get_ProperSQL{
param($SQL)
if ($SQL -like '8.0*') { return "SQL Server 2000" }
elseif ($SQL -like '9.0*') { return "SQL Server 2005" }
elseif ($SQL -like '10.0*') { return "SQL Server 2008" }
elseif ($SQL -like '10.5*') { return "SQL Server 2008 R2" }
elseif ($SQL -like '11.0*') { return "SQL Server 2012" }
elseif ($SQL -like '12.0*') { return "SQL Server 2014" }
elseif ($SQL -like '13.0*') { return "SQL Server 2016" }
elseif ($SQL -like '14.0*') { return "SQL Server 2017" }
else { return "Unknown SQL version" }
}

function Get_ServerName{
param($SqlHostProperties)
$SrvHostname = $SqlHostProperties | where {$_.name -eq "ComputerNamePhysicalNetBios"} | select value
return $SrvHostname.Value
}

function Get_ServerMemory{
param($SqlHostProperties)
$SrvMemory = $SqlHostProperties | where {$_.name -eq "PhysicalMemory"} | select value
return $SrvMemory.Value
}

function Get_ServerProcessors{
param($SqlHostProperties)
$SrvCPUs = $SqlHostProperties | where {$_.name -eq "Processors"} | select value
return $SrvCPUs.Value
}

Write-Host "SQL Server Inventory Powershell Script"
Write-Host "======================================"
$today = Get-Date -f "yyMMdd"
$CsvFile = "C:\temp\SqlServer_Inventory_$today.csv"
Clear-Content -path $CsvFile -Force
$CsvHeader = "ServerName,OS,SqlServerVersion,SqlServerEdition,SqlServicePack,Cores,RAM"
Add-Content $CsvFile $CsvHeader
$SqlSrvListFile = "sqlsrv_list.csv"
$SqlSrvList = [object]$SqlSrvList = Import-CSV $SqlSrvListFile
foreach ($SqlServer in $SqlSrvList.ServerName) {
Write-Host "Working on $SqlServer"
$SqlHost = New-Object -type Microsoft.SqlServer.Management.Smo.Server -ArgumentList $SqlServer
if ($SqlHost.ComputerNamePhysicalNetBIOS -ne $null) {
$SqlHostProperties = $SqlHost.Information.Properties | select Name,Value
$ServerName = Get_ServerName($SqlHostProperties)
$OSVersString = Get_ProperOS($SqlHost.OSVersion)
$SqlVersString = Get_ProperSQL($SqlHost.VersionString)
$SqlEdit = $SqlHost.Edition
$SqlSpack = $SqlHost.ProductLevel
$Cpu = Get_ServerProcessors($SqlHostProperties)
$Mem = Get_ServerMemory($SqlHostProperties)
$Dump =  "$ServerName"+","+"$OSVersString"+","+"$SqlVersString"+","+"$SqlEdit"+","+"$SqlSpack"+","+"$Cpu"+","+"$Mem"
Add-Content $CsvFile $Dump
}
else { Write-Host "Couldn't query $SqlServer." }
}
Write-Host "Done."

Voici une copie d’écran d’exemple du fichier CSV généré par le script :

Powershell : compression et archivage automatique de logs

En vue d’une éventuelle mise en place d’un event collector permettant de centrer en un seul et même endroit tous les logs générés par les machines et lisibles en local dans l’event viewer, j’ai développé un petit script Powershell permettant une compression automatique des logs présents dans un répertoire dans un but d’archive. Le script ne retourne aucune information et ne procède à aucune vérification puisqu’il est idéalement appelé depuis une tâche planifiée et les droits de lecture/écriture par rapport au compte d’exécution auront été contrôlés auparavant. La journalisation étant activée, il sera possible de tracer les actions de 7z.exe et constater le succès ou l’échec de l’archivage.

Le script exécute 7-Zip, compresse les fichiers *.evtx dans une archive puis les supprime. Il conviendra naturellement d’adapter les chemins en fonction de l’environnement.

$7zpath = "C:\Program Files\7-Zip"
$logpath = "C:\events\logs-files\*.evtx"
$archivepath = "C:\events\archives"
$date = Get-Date -Format "yyyy-MM-dd"
$archivename = "logs-archive-$date.zip"
$7zreport = "C:\events\7z-compresslog-$date.log"
cd $7zpath
.7z.exe a "$archivepath$archivename" -r $logpath -mx=9 -bb -sdel > $7zreport 2>&1