PI Services

Le blog des collaborateurs de PI Services

PowerShell - Créer une fonction de génération de mot de passe

La génération de mots de passe dans un script est un besoin fréquent lors de création de compte utilisateur. Au lieu d'appeler les cmdlets pour générer un mot de passe aléatoire à chaque besoin, il est plus esthétique de créer une fonction de génération de mots de passe dédiée et de l'utiliser quand le besoin se présente.

Le code suivant propose une fonction de génération de mots de passe qui essaye d'être simple, compréhensible et adaptative.

 

La fonction de génération de mots de passe

function New-RandomPassword #Fonction qu'il faudra appeler pour générer un mot de passe aléatoire
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false)] #Indique que ce paramètre n'est pas obligatoire
        [ValidateNotNullOrEmpty()] #Indique que ce champ ne peut pas être vide ou null
        [int]$PasswordLength = 16, #Paramètre optionel qui spécifie la longueur du mot de passe, valeur par défaut 16

        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [int]$NumberOfAlphaNumericCharacters = 2, #Paramètre optionnel qui spécifie le nombre de caractères alphanumériques que le mot de passe doit contenir, valeur par défaut 2

        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [switch]$ConvertToSecureString #Paramètre qui si présent convertit le mot de passe en secure string pour pouvoir construire un PSCredential Object utile par exemple pour exécuter des cmdlets avec un compte de service 
    )

    Begin
    {
        Add-Type -AssemblyName 'System.Web' # Ajout d'une classe .NET à la session PowerShell qui permet d'accéder aux fonctions nécessaires à la génération du mot de passe
        
        $Return = @{} #Tableau de retour qui contient le mot de passe en clair et le mot de passe sous forme de secure string 
    }

    Process
    {
        $Password = [System.Web.Security.Membership]::GeneratePassword($PasswordLength,$NumberOfAlphaNumericCharacters) #Génération du mot de passe en clair avec les paramètres fournis
        $Return.passwordObject = $Password #Attribution du mot de passe généré dans la variable de sortie de la fonction

        if ($ConvertToSecureString.IsPresent) #Si le paramètre ConvertToSecureString est présent, génère une secure string du mot de passe
        {
            $Password = ConvertTo-SecureString -String $Password -AsPlainText -Force #Génération de la secure string du mot de passe
            $Return.passwordSecureObject = $Password #Attribution de la secure string du mot de passe dans la variable de sortie de la fonction
        }

        Return $Return #Retourne le mot de passe ainsi que la secure string en sortie de la fonction
    }
}

 

 

 

Exemple d'utilisation de la fonction de génération de mots de passe

. .\New-RandomPassword_article.ps1 #Permet de déclarer (dot source) la fonction de génération de mots de passe pour qu'elle puisse être utilisée

$Password = New-RandomPassword -PasswordLength 20 -ConvertToSecureString #Appelle de la fonction de génération de mots de passe
#Avec génération d'une secure string du mot de passe et une longueur de mot de passe de 20 caractères

$Password.passwordObject #Le mot de passe en clair est dans l'attribut passwordObject de la variable de sortie définit ci-avant $Password
$Password.passwordSecureObject #Le mot de passe sous forme de secure string est dans l'attribut passwordSecureObject de la variable de sortie définit ci-avant $Password

 

Le résultat


Appel du script PowerShell Call_me_maybe.ps1 ci-avant

PS D:\Hey_Im_a_folder> .\Call_me_maybe.ps1
LmcfY>5O$-GZ^z/fvfB1 #Affichage du mot de passe en clair
System.Security.SecureString #Mot de passe sous forme de secure string

 

Azure - Combiner les informations de sécurité SSPR et MFA

SSPR et MFA

Face aux nombreuses attaques informatique ces dernières années, les entreprises répondent en renforçant la sécurité de leur système d'information (SI).

SSPR (Self-Service Password Reset) et MFA (Multi-factor authentication) y contribuent.

SSPR permet à un utilisateur de réinitialiser son mot de passe dans Azure de façon autonome via des méthodes de récupération qu'il aura configuré préalablement, SMS, appel téléphonique, questions secrètes..

MFA demande à un utilisateur un deuxième moyen de prouver son identité, c'est par exemple parfois le cas suite à un achat en ligne, un code à usage unique (OTP) pour valider la transaction est envoyé par SMS à un téléphone dont le numéro a été communiqué au fournisseur de service en amont. D'autres méthodes existent tel que l'application Microsoft Authenticator ou l'appel d'un numéro de téléphone.

 

On observe donc que certaines méthodes d'authentification sont communes entre SSPR et MFA, les utilisateurs seront donc invités à renseigner plusieurs fois les mêmes informations ce qui détériore leurs expériences et par conséquent amoindris leurs adhésions à ces outils.

 

Combiner les informations de sécurité SSPR et MFA

 

Il existe dans Azure un paramètre qui permet de combiner les informations de sécurité SSPR et MFA.
Se connecter avec un compte administrateur global à portal.azure.com > User settings > Manage user feature settings

 

Choisir ensuite All dans User can use the combined security information registration experience

Selected permet d'activer ce paramètre pour des utilisateurs nominatif

 

Pour aller plus loin

Documentation officielle Microsoft sur la combinaison des informations de sécurité SSPR et MFA

Active Directory GPO - Comment modifier les paramètres Internet Explorer

Oh, la belle bleue (même si elle est verte) !

 

Modifier les paramètres Internet Explorer (IE) via GPO permet de simplifier grandement le déploiement d'un paramètre spécifique IE, si tenté que la GPO soit intuitive à configurer..

Rappelons dans un premier temps comment accèder aux settings IE, une fois la GPO ouverte pour modification : User Configuration > Preferences > Control Panel Settings > Internet Settings > New

 

La même fenêtre des options IE que sur une machine local s'affiche avec la particularité d'avoir des paramètres soulignés en vert d'un trait plein et d'autre souligné en rouge d'un trait en pointillé.

 

 

Signification et modification

 

Les codes couleurs fonctionnent comme ceci :

  • Les traits verts correspondent aux paramètres qui sont activés et s'appliqueront sur les machines
  • Les traits rouges correspondent aux paramètres non désactivés et qui ne s'appliqueront pas sur les machines

 

Comment faire pour basculer un paramètre activé à désactivé puisque les raccourcis habituels ne semblent pas fonctionner ?

C'est grâce aux touches F5, F6, F7 et F8 qu'il est possible de changer le statut d'un paramètre :

  • F5 : active tous les paramètres de l'onglet
  • F6 : active uniquement le paramètre qui a été modifié en dernier
  • F7 : désactive uniquement le paramètre qui a été modifié en dernier
  • F8 : désactive tous les paramètres de l'onglet

 

PowerShell - Créer une fonction de logging

À l'image du chien qui est le meilleur ami de l'homme, le fichier de log est le meilleur ami de l'informaticien qui débug.

Générer des logs lorsqu'un code PowerShell s'exécute permet de savoir si une fonction a été correctement exécutée et si ce n'est pas le cas de savoir précisément à quel endroit un problème est survenu, ce qui va sans dire est d'une aide considérable lors d'un débug.

 

La fonction de log

function Get-CurrentLineNumber #Fonction qui permet de récupérer la ligne actuelle dans un script, elle sera utilisée par le script qui appelle la fonction de log
{ 
    Return $MyInvocation.ScriptLineNumber
}


function Write-Log #Fonction qu'il faudra appeler lorsque l'on voudra faire du logging
{
    [CmdletBinding()] #Déclaration des paramètres qu'il faudra fournir à la fonction pour qu'elle puisse s'exécuter
    param
    (
        [Parameter(Mandatory=$true)] #Indique que ce paramètre est obligatoire
        [ValidateNotNullOrEmpty()] #Indique que ce champ ne peut pas être vide ou null
        [string]$LogFile, #Paramètre qui contient le chemin complet du script qui appelle la fonction de log

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$LogLine, #Paramètre qui contient la ligne à laquelle la fonction de log est appelée

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$LogMessage, #Paramètre qui contient le log 

        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [string]$LogPath, #Paramètre qui contient le chemin complet du fichier de log
 
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Information','Warning','Error')] #Valeurs disponibles pour qualifier le log
        [string]$LogSeverity #Paramètre qui va quantifier la nature du log parmi les valeurs disponibles
    )

    Begin
    {
        if (!$LogPath) #Code qui permet de générer un dossier de log ainsi que le fichier de log de façon dynamique si le paramètre LogPath est vide
        {
            $CurrentDateFormatForLog = Get-Date -Format "yyyy-MM-dd_HH" #Recupère la date du jour pour la mettre à la fin du nom de fichier de log
            $LogFolderName = "Logs" #Nom du fichier de log
            $LogFolderPath = $PSScriptRoot + "\" + $LogFolderName #Détermine dynamiquement la localisation du dossier de log qui doit se trouver dans le même dossier que le script PowerShell qui appelle la fonction de log
            
            if (!(Test-Path -Path $LogFolderPath))#Vérifie l'existence d'un dossier de log dans le même dossier que le script qui appelle la fonction de log
            {
                New-Item -ItemType Directory -Path $LogFolderPath | Out-Null #Si le dossier de log n'existe pas, le créé
            }
            
            $LogPath = $LogFolderPath + "\" + "Log_" + $CurrentDateFormatForLog + ".csv" #Détermine le nom du chemin complet du fichier de log
        }
    }
    
    Process
    {
        [pscustomobject]@{ #Génére un objet PowerShell dont chaque ligne représente une colonne du fichier de log
            Date = Get-Date -Format "yyyy-MM-dd HH:mm:ss" #Première colonne qui contient la date à laquelle la fonction de log s'est exécuté
            Severity = $LogSeverity #Deuxième colonne qui contient la nature du log
            File = $LogFile #Troisième colonne qui contient le nom du script PowerShell qui appelle la fonction de log
            Line = $LogLine #Quatrième colonne qui contient la ligne à laquelle la fonction de log a été appelée
            Message = $LogMessage #Cinquième colonne qui contient le log
            
        } | Export-Csv -Path $LogPath -Append -NoTypeInformation -Delimiter ";" -Encoding UTF8 #Code qui permet de transformer l'objet PowerShell en fichier de log (csv)
    }
}

 

Exemple d'utilisation de la fonction de log

. .\Write-Log_article.ps1 #Permet de déclarer (dot source) la fonction de log pour qu'elle puisse être utilisée

try #Appelle de la fonction de log pour indique l'utilisateur a bien été trouvé 
{
    $User = Get-ADUser -Identity "maybe" #Cherche un utilisateur dont le samAccountName est maybe
    Write-Log -LogSeverity "Information" -LogMessage "L'utilisateur a bien été récupéré" -LogFile $PSCommandPath -LogLine $(Get-CurrentLineNumber)
    #$PSCommandPath permet de trouver le chemin complet du script qui est en train de s'exécuter
    #Get-CurrentLineNumber fonction déclarée dans le script de log qui permet de récupérer la ligne courante du script qui appelle la fonction de log
}

catch #Appelle la fonction de log si l'utilisateur n'a pas été trouvé et envoie en tant que message de log l'erreur généré par la cmdlet Get-ADUser
{
    Write-Log -LogSeverity "Error" -LogMessage $_.Exception.Message -LogFile $PSCommandPath -LogLine $(Get-CurrentLineNumber) 
}

 

 

Le résultat

Une fois le script PowerShell Call_me_maybe.ps1 appelé, le dossier de log est généré

 

Le fichier de log est généré

 

Le log est généré

Office : Erreur de mise à niveau du client Office 2016 vers la version M365 2008 en utilisant le centre logiciel MECM

Scenario:

L'utilisateur avait Office 2016 32 bits installé et souhaite faire une mise à niveau vers le client M365 v2008 (semi-annual channel).

Problème:

Lors de la mise à niveau, l'erreur 0x80077562 (-2146994846) s'affiche dans le centre logiciel MECM --> Tous les produits office ont cessé de fonctionner et Office 2016 n'a pas été supprimé des programmes.

Lorsque vous essayez de désinstaller manuellement l'ancienne version d'Office 2016, cette erreur s'affiche :

"La langue de ce package d'installation n'est pas prise en charge par le système"

Explication:

Cette erreur est parfois causée par l'échec des précédentes tentatives d'installation/désinstallation du produit Office.

Solution:

1. Réinstallez Office 2016 32 bits à l'aide des sources d'installation d'Office

2. Réessayez l'installation d'Office 365 à partir du centre logiciel

NPS 2019 - Tous les paquets Radius sont ignorés

Le rôle NPS (Network Policy Server) reste le seul moyen natif d’utiliser un serveur Windows pour réaliser des authentifications RADIUS, ce qui est très utile notamment pour gérer l’authentification à des appliances réseau, des bornes Wifi ou même des utilisations plus avancées comme de l’authentification par certificat avec AlwaysOn VPN.

Bien qu’il n’ait pas évolué depuis plusieurs versions de Windows et que sa partie NAP (Network Access Protection) soit dépréciée depuis Windows 2012 R2, il reste parfaitement fonctionnel pour son rôle de serveur RADIUS.

J’ai donc récemment déployé un serveur NPS sous Windows 2019 sur un serveur flambant neuf, configuré mon client Radius (une appliance NTP) ainsi qu’une stratégie d’authentification basique pour pouvoir utiliser des identifiants de l’AD pour me connecter à l’appliance.

Malheureusement, le premier essai de connexion fut un échec. Pas d’affolement, les stratégies NPS sont souvent assez obscures et il est vite arrivé de manquer un paramètre, me dis-je… mais pas cette fois.

J’active donc les logs d’audit de connexion, ils permettent souvent d’en apprendre plus sur les raisons de l’échec. Sauf que cette fois, ils sont intégralement vides : non seulement il n’y a pas d’erreur, mais il n’y a en réalité pas le moindre évènement, comme si la requête RADIUS n’arrivait jamais au serveur.

Les règles de firewall Windows créée automatiquement lors de l’installation du rôle NPS (groupe Network Policy Server) sont pourtant bien présentes et actives, et l’administrateur réseau me confirme que les paquets arrivent bien jusqu’au serveur.

Il est donc temps de sortir Wireshark : effectivement, les paquets RADIUS arrivent bien au serveur mais ensuite rien, aucune réaction; encore une fois comme si la requête n’arrivait jamais au rôle NPS…

Après bien des tentatives infructueuses, j’essaye en désespoir de cause de désactiver le firewall Windows; et miracle, tout tombe en marche.

Le problème se situe donc au niveau du firewall. Après quelques recherches, il apparaît que les règles natives sont configurées pour ne fonctionner que pour le service IAS, ce qui est normal. Par contre, dans Windows 2019, ce service utilise un identifiant de sécurité (service SID) qui l’empêche d’être la cible d’une règle de firewall… et donc la règle ne fonctionne pas et les flux sont bloqués.

Deux solutions s’offrent donc à nous :

  • Reconfigurer le service pour retirer cette restriction à l’aide de la commande sc.exe sidtype IAS unrestricted
  • Reconfigurer les règles de firewall pour qu’elles fonctionnent avec n’importe quel service :  Get-NetFirewallRule -DisplayGroup "Network Policy Server" | where DisplayName -like "*RADIUS*" | Set-NetFirewallRule -Service Any

Après avoir exécuté une de ces deux solutions, tout devrait rentrer dans l’ordre !

Active Directory - Accéder aux principaux outils d'administration AD en ligne de commande

Marre de devoir attendre que le bureau Windows Server 2016/2019 se charge pour chercher votre console d'administration ou peur que le simple fait d'ouvrir la recherche Windows de Windows Server 2012 freeze votre session ?

Voici la liste des commandes les plus utiles pour accéder aux outils d'administration Windows :

 

Active Directory Domains & Trust DOMAIN.MSC
Active Directory Sites & Services DSSITE.MSC
Active Directory Users & Computers DSA.MSC
Certificates snap-in ERTMGR.MSC
Certification Services CERTSRV.MSC
Command Prompt CMD.EXE
Computer Management COMPMGMT.MSC
Device Manager DEVMGMT.MSC
DHCP Manager DHCPMGMT.MSC
Disk Defragmenter DFRG.MSC
Disk Management DISKMGMT.MSC
Distributed File System DFSGUI.MSC
DNS Manager DNSMGMT.MSC
Domain Controller Security Policy DCPOL.MSC
Domain Security Policy DOMPOL.MSC
Event Viewer EVENTVWR.MSC
Hardware and software configuration information MSINFO32.EXE
Internet Authentication Service IAS.MSC
Internet Information Service (\Windows\system32\inetsrv) INETMGR
local Group Policy Editor GPEDIT.MSC
Local Security Policy SECPOL.MSC
Local Users and Groups LUSRMGR.MSC
Microsoft Management Console MMC.EXE
Performance Monitor PERFMON.MSC
Remote Desktop MSTSC
Resultant Set of Policy RSOP.MSC
Routing and Remote Access RRASMGMT.MSC
Run Registry Editor REGEDIT.EXE
Service Configuration SERVICES.MSC
Shared Folders FSMGMT.MSC
Terminal Services TSCC.MSC

Réseau - Trouver le mot de passe d'un réseau wifi précédement utilisé

À quoi cela sert-il ?

En réalité, il y a peu de cas pratiques ou trouver le mot de passe d'un réseau wifi précédemment utilisé est utile, car bien souvent, celui-ci étant sauvegardé, il n'y a pas besoin de l'entrer à nouveau.

Alors à quoi cela sert-il ? C'est avant tout pour mettre en relief que certaines cmdlets Windows sont peu sécurisées et révèlent beaucoup d'informations avec peu de privilèges, ce qui est bien évidemment le combo préféré des hackers.

 

Les commandes

 

Les commandes ci-après sont exécutés avec un utilisateur qui n'est ni administrateur de la machine ni en mode administrateur

La première commande permet d'afficher les profiles auquel la machine s'est déjà connecté

C:\WINDOWS\System32>netsh wlan show profiles
 
Profiles on interface Wi-Fi:
 
Group policy profiles (read only) #Dans le cas d'un PC dans un domaine Active Directory les profiles déployés par GPO sont affichés ci-après
---------------------------------
    Profile_entreprise
    Profile_entreprise2
 
User profiles #Les profiles sont affichés ci-après
-------------
    All User Profile     : MonWifi
    All User Profile     : Faux_reseau
    All User Profile     : Toto

 

 

Cette commande va afficher le mot de passe du profile appelé en clair

C:\WINDOWS\System32>netsh wlan show profiles MonWifi key=clear

Profile MonWifi on interface Wi-Fi:
=======================================================================

Applied: All User Profile

Profile information
-------------------
    Version                : 1
    Type                   : Wireless LAN
    Name                   : MonWifi
    Control options        :
        Connection mode    : Connect automatically
        Network broadcast  : Connect only if this network is broadcasting
        AutoSwitch         : Do not switch to other networks
        MAC Randomization  : Disabled

Connectivity settings
---------------------
    Number of SSIDs        : 1
    SSID name              : "MonWifi"
    Network type           : Infrastructure
    Radio type             : [ Any Radio Type ]
    Vendor extension          : Not present

Security settings
-----------------
    Authentication         : WPA2-Personal
    Cipher                 : CCMP
    Authentication         : WPA2-Personal
    Cipher                 : GCMP
    Security key           : Present
    Key Content            : MonMotDePasseEnClair #Le mot de passe est affiché en clair

Cost settings
-------------
    Cost                   : Unrestricted
    Congested              : No
    Approaching Data Limit : No
    Over Data Limit        : No
    Roaming                : No
    Cost Source            : Default

 

Quels sont alors les moyens de se prémunir de cette vulnérabilité ? Les méthodes sont diverses, telles que l'utilisation du 802.1X ou un changement régulier du mot de passe.


Pour aller plus loin

 

Vidéo youtube qui propose un script python pour automatiser la récupération des mots de passe de tous les profils d'un ordinateur

 

PowerShell - Les bienfaits du splatting

Ça pique les yeux

 

Vous avez peut être déjà eu recours à la commande (cmdlet) suivante pour créér un compte dans l'Active Directory (AD) :

New-ADUser -Name $Name -samAccountName $samAccountName -userPrincipalName $UserPrincipalName -Path $Path -AccountPassword $AccountPassword -DisplayName $DisplayName -GivenName $GivenName -sn $sn -Description $Description -Server $Server -ChangePasswordAtLogon -Enabled

 

Oui ca fait mal aux yeux, mais pourquoi ne pas utiliser le caractère backtick ` qui permet de scinder une cmdlet sur plusieurs lignes ?

New-ADUser -Name $Name -samAccountName $samAccountName 
` -userPrincipalName $UserPrincipalName
` -Path $Path -AccountPassword $AccountPassword 
` -DisplayName $DisplayName -GivenName $GivenName -sn $sn
` -Description $Description -Server $Server
` -ChangePasswordAtLogon -Enabled

 

Effectivement, c'est mieux, mais cela n'est toujours pas très esthétique, peut-on faire encore mieux ?

 

Le splatting

 

Le splatting permet de passer à une cmdlet un hash table (tableau associatif), array (tableau) ou une liste d'arguments qui va contenir la liste des paramètres voulus

$parameters = @{ #Ceci est un hash table qui va associer à gauche du signe égal le nom du paramètre avec à droite la valeur du dit paramètre
    Name = $Name
    samAccountName = $samAccountName
    userPrincipalName = $userPrincipalName
    Path = $Path 
    AccountPassword = $AccountPassword 
    DisplayName = $DisplayName
    GivenName = $GivenName
    sn = $sn
    Description = $Description
    Server = $Server
    ChangePasswordAtLogon = $true
    Enabled = $true
}

New-ADUser @parameters #Pour utiliser le splatting il faut passer en paramètre le hash table à l'aide du caractère @ et non $

 

En plus de rendre le code plus lisible, les méthodes associées aux hash table sont désormais accessibles, si par exemple on veut ajouter ou retirer des éléments plus loin dans le code :

 

$parameters.add('employeeID', $EmployeeID) #Ajout du paramètre à gauche du signe , employeeID et à droite la valeur du dit paramètre dans le hashtable $parameters 
$parameters.remove('ChangePasswordAtLogon') #Suppression du paramètre ChangePasswordAtLogon du hash table $parameters

 

Pour aller plus loin

 

Documentation officielle de Microsoft sur le splatting

Azure - S'octroyer des permissions sur des souscriptions en tant qu'administrateur global

Les souscriptions, des ressources Azure aux permissions isolées

 

Le rôle Azure administrateur global (global administrateur) est le rôle absolu, il octroie le droit de vie ou de mort sur un tenant Azure, cependant, les irrésistibles souscriptions (subscriptions) n'en ont que faire et un global administrator se fera jeter comme un simple utilisateur s'il tente d'accéder à une ressource Azure auquel il n'a pas accès si tenté qu'il puisse déjà la voir, à moins que..

 

S'octroyer les permissions sur toutes les souscriptions

 

Il existe une fonctionnalité Azure qui permet à un Global admin de se conférer des droits sur toutes les subscriptions existantes et ceci en un clic, pour ce faire :

Connectez-vous à portal.azure.com avec votre compte Global admin et dans la liste déroulante de gauche, ouvrez Properties

En bas de la fenêtre dans la section Access management for Azure ressources basculez le bouton en Yes

 

Attention, ce rôle ne fournit pas le rôle Propriétaire (Owner) sur les subscriptons, il fournit le rôle User Access Administrator à la racine ce qui permet d'accéder au RBAC Azure de la subscription et de s'octroyer les droits voulus.


Pensez à retirer vos droits de la subscription avant de basculer à nouveau le bouton évoqué précédemment en No

 

Pour aller plus loin

Article officiel de Microsoft sur la délégation de permissions sur les souscriptions Azure en tant qu'administrateur global

Article officiel de Microsoft sur le RBAC Azure