PoshC2_Python/Modules/Get-LAPSPasswords.ps1

147 lines
7.0 KiB
PowerShell

function Get-LAPSPasswords
{
<#
.Synopsis
This module will query Active Directory for the hostname, LAPS (local administrator) stored password,
and password expiration for each computer account.
.DESCRIPTION
This module will query Active Directory for the hostname, LAPS (local administrator) stored password,
and password expiration for each computer account. The script filters out disabled domain computers.
LAPS password storage can be identified by querying the (domain user available) ms-MCS-AdmPwdExpirationTime
attribute. If the attribute (timestamp) exists, LAPS is in use for local administrator passwords. Access to
ms-MCS-AdmPwd attribute should be restricted to privileged accounts. Also, since the script uses data tables
to output affected systems the results can be easily piped to other commands such as test-connection or a Export-Csv.
.EXAMPLE
The example below shows the standard command usage. Disabled system are excluded by default. If your user doesn't
have the rights to read the password, then it will show 0 for Readable.
PS C:\> Get-LAPSPasswords -DomainController 192.168.1.1 -Credential demo.com\administrator | Format-Table -AutoSize
Hostname Stored Readable Password Expiration
-------- ------ -------- -------- ----------
WIN-M8V16OTGIIN.test.domain 0 0 NA
WIN-M8V16OTGIIN.test.domain 0 0 NA
ASSESS-WIN7-TES.test.domain 1 1 $sl+xbZz2&qtDr 6/3/2015 7:09:28 PM
.EXAMPLE
The example below shows how to write the output to a csv file.
PS C:\> Get-LAPSPasswords -DomainController 192.168.1.1 -Credential demo.com\administrator | Export-Csv c:\temp\output.csv -NoTypeInformation
.LINK
https://blog.netspi.com/running-laps-around-cleartext-passwords/
https://github.com/kfosaaen/Get-LAPSPasswords
https://technet.microsoft.com/en-us/library/security/3062591
.NOTES
Author: Karl Fosaaen - 2015, NetSPI
Version: Get-LAPSPasswords.psm1 v1.0
Comments: The technique used to query LDAP was based on the "Get-AuditDSComputerAccount"
function found in Carlos Perez's PoshSec-Mod project. The general idea is based off of
a Twitter conversation with @_wald0. The bones of this were borrowed (with permission) from
Scott Sutherland's Get-ExploitableSystems function.
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
HelpMessage="Credentials to use when connecting to a Domain Controller.")]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory=$false,
HelpMessage="Domain controller for Domain and Site that you want to query against.")]
[string]$DomainController,
[Parameter(Mandatory=$false,
HelpMessage="Maximum number of Objects to pull from AD, limit is 1,000.")]
[int]$Limit = 1000,
[Parameter(Mandatory=$false,
HelpMessage="scope of a search as either a base, one-level, or subtree search, default is subtree.")]
[ValidateSet("Subtree","OneLevel","Base")]
[string]$SearchScope = "Subtree",
[Parameter(Mandatory=$false,
HelpMessage="Distinguished Name Path to limit search to.")]
[string]$SearchDN
)
Begin
{
if ($DomainController -and $Credential.GetNetworkCredential().Password)
{
$objDomain = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
}
else
{
$objDomain = [ADSI]""
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
}
}
Process
{
# Status user
Write-Verbose "[*] Grabbing computer accounts from Active Directory..."
# Create data table for hostnames, and passwords from LDAP
$TableAdsComputers = New-Object System.Data.DataTable
$TableAdsComputers.Columns.Add('Hostname') | Out-Null
$TableAdsComputers.Columns.Add('Stored') | Out-Null
$TableAdsComputers.Columns.Add('Readable') | Out-Null
$TableAdsComputers.Columns.Add('Password') | Out-Null
$TableAdsComputers.Columns.Add('Expiration') | Out-Null
# ----------------------------------------------------------------
# Grab computer account information from Active Directory via LDAP
# ----------------------------------------------------------------
$CompFilter = "(&(objectCategory=Computer))"
$ObjSearcher.PageSize = $Limit
$ObjSearcher.Filter = $CompFilter
$ObjSearcher.SearchScope = "Subtree"
if ($SearchDN)
{
$objSearcher.SearchDN = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($SearchDN)")
}
$ObjSearcher.FindAll() | ForEach-Object {
# Setup fields
$CurrentHost = $($_.properties['dnshostname'])
$CurrentUac = $($_.properties['useraccountcontrol'])
$CurrentPassword = $($_.properties['ms-MCS-AdmPwd'])
if ($_.properties['ms-MCS-AdmPwdExpirationTime'] -ge 0){$CurrentExpiration = $([datetime]::FromFileTime([convert]::ToInt64($_.properties['ms-MCS-AdmPwdExpirationTime'],10)))}
else{$CurrentExpiration = "NA"}
$PasswordAvailable = 0
$PasswordStored = 1
# Convert useraccountcontrol to binary so flags can be checked
# http://support.microsoft.com/en-us/kb/305144
# http://blogs.technet.com/b/askpfeplat/archive/2014/01/15/understanding-the-useraccountcontrol-attribute-in-active-directory.aspx
$CurrentUacBin = [convert]::ToString($CurrentUac,2)
# Check the 2nd to last value to determine if its disabled
$DisableOffset = $CurrentUacBin.Length - 2
$CurrentDisabled = $CurrentUacBin.Substring($DisableOffset,1)
# Set flag if stored password is not available
if ($CurrentExpiration -eq "NA"){$PasswordStored = 0}
if ($CurrentPassword.length -ge 1){$PasswordAvailable = 1}
# Add computer to list if it's enabled
if ($CurrentDisabled -eq 0){
# Add domain computer to data table
$TableAdsComputers.Rows.Add($CurrentHost,$PasswordStored,$PasswordAvailable,$CurrentPassword, $CurrentExpiration) | Out-Null
}
# Display results
$TableAdsComputers | Sort-Object {$_.Hostname} -Descending
}
}
End
{
}
}