· 6 years ago · May 20, 2019, 02:14 PM
1
2
3###########################################################
4# CypherDog2.1 - BloodHound Dog Whisperer - @SadProcessor #
5###########################################################
6
7###################################################### ToDo
8# - Code > Check all Aliases [X]
9# - Code > CleanUp Stuff [~]
10# - BloodHound > Update Table [X] <- Alphabetical order??
11# - EdgeInfo > CopyPaste info stuff [ ]
12# - HelpPages > Description/Examples++ [ ]
13# - Doc > Generate Wiki [+ HelpURI Binding] [ ]
14# - DeBug > Beta via Slack [~]
15# - DogPost > Update to better error message [X]
16# - 2.1 > Test new Edges [X] <- Re-collect
17
18###########################################################
19
20
21###########################################################
22#region ############################################## VARS
23
24##################################################### ASCII
25$ASCII= @("
26 _____________________________________________
27 _______|_____________________________________
28 ______||_______________________CYPHERDOG2.1__
29 ______||-________...__________________ Beta__
30 _______||-__--||||||||-._____________________
31 ________!||||||||||||||||||--________________
32 _________|||||||||||||||||||||-______________
33 _________!||||||||||||||||||||||.____________
34 ________.||||||!!||||||||||||||||-___________
35 _______|||!||||___||||||||||||||||.__________
36 ______|||_.||!___.|||'_!||_'||||||!__________
37 _____||___!||____|||____||___|||||.__________
38 ______||___||_____||_____||!__!|||'__________
39 ___________ ||!____||!_______________________
40 _____________________________________________
41
42 BloodHound Dog Whisperer - @SadProcessor 2019
43")
44
45##################################################### Enums
46
47## NodeType
48enum NodeType{
49 Computer
50 Domain
51 Group
52 User
53 GPO
54 OU
55 }
56
57
58## EdgeType
59enum EdgeType{
60 #Default
61 MemberOf
62 HasSession
63 AdminTo
64 TrustedBy
65 #ACL
66 AllExtendedRights
67 AddMember
68 ForceChangePassword
69 GenericAll
70 GenericWrite
71 Owns
72 WriteDacl
73 WriteOwner
74 ReadLAPSPassword
75 #GPO
76 Contains
77 GpLink
78 #Special
79 CanRDP
80 ExecuteDCOM
81 AllowedToDelegate
82 AddAllowedToAct
83 AllowedToAct
84 }
85
86# Default
87enum EdgeDef{
88 MemberOf
89 HasSession
90 AdminTo
91 TrustedBy
92 }
93
94# ACL
95enum EdgeACL{
96 AllExtendedRights
97 AddMember
98 ForceChangePassword
99 GenericAll
100 GenericWrite
101 Owns
102 WriteDacl
103 WriteOwner
104 ReadLAPSPassword
105 }
106
107# GPO/OU
108enum EdgeGPO{
109 Contains
110 GpLink
111 }
112
113# Special
114enum EdgeSpc{
115 CanRDP
116 ExecuteDCOM
117 AllowedToDelegate
118 AddAllowedToAct
119 AllowedToAct
120 }
121
122
123################################################# PathClass
124Class BHEdge{
125 [int]$ID
126 [int]$Step
127 [string]$startNode
128 [string]$Edge
129 [String]$Direction
130 [string]$EndNode
131 }
132
133################################################# CypherDog
134$CypherDog = [PSCustomObject]@{
135 Host = 'localhost'
136 Port = 7474
137 UserList = $Null
138 GroupList = $Null
139 ComputerList = $Null
140 DomainList = $Null
141 GPOList = $Null
142 OUList = $Null
143 }
144
145#endregion ################################################
146
147
148###########################################################
149#region ############################################## UTIL
150
151# CacheNode
152# DynP
153# GenEdgeStr
154# ToPathObj
155# ClipThis
156# JoinCypher
157# FixPathID
158
159################################################# CacheNode
160function CacheNode{
161<#
162.Synopsis
163 Cache Bloodhound Node Lists [Internal]
164.DESCRIPTION
165 Cache Name Lists per Node type
166 All types if none specified
167 Use at startup and on Node Create/Delete
168.EXAMPLE
169 CacheNode
170 Caches Name lists for All Node Types
171.EXAMPLE
172 CacheNode Computer,User
173 Caches Name Lists of specified node types
174#>
175 [CmdletBinding()]
176 Param(
177 # Specify Type(s)
178 [parameter(Mandatory=0)][NodeType[]]$Type
179 )
180 # No Type == All
181 If($Type -eq $Null){$Type=[Enum]::GetNames([NodeType])}
182 # For each type
183 foreach($T in $Type){
184 Write-Verbose "Caching Node List: $T"
185 # Prep Query
186 $Query = "MATCH (n:$T) RETURN n"
187 # Cache matching name list
188 $Script:CypherDog."${T}List"=(DogPost $Query).name
189 }}
190#####End
191
192################################################# SetCred
193function SetCred{
194 <#
195 .Synopsis
196 Sets the credential for your Neo4j database to be used
197 .DESCRIPTION
198 Set a cred for when you need one and stuff
199 .EXAMPLE
200 SetCred
201 #>
202 [CmdletBinding()]
203 Param(
204 # Specify Type(s)
205 [parameter(Mandatory=1)][pscredential]$Credential = (Get-Credential)
206 )
207 [pscredential]$script:Neo4jCred = $Credential
208 }
209 #####End
210
211###################################################### DynP
212function DynP{
213<#
214.Synopsis
215 Get Dynamic Param [Internal]
216.DESCRIPTION
217 Return Single DynParam to be added to dictionnary
218.EXAMPLE
219 DynP TestParam String -mandatory 1
220#>
221 [CmdletBinding()]
222 Param(
223 [Parameter(Mandatory=1)][String]$Name,
224 [Parameter(Mandatory=1)][string]$Type,
225 [Parameter(Mandatory=0)][bool]$Mandat=0,
226 [Parameter(Mandatory=0)][int]$Pos=$Null,
227 [Parameter(Mandatory=0)][bool]$Pipe=0,
228 [Parameter(Mandatory=0)][bool]$PipeProp=0,
229 [Parameter(Mandatory=0)]$VSet=$Null
230 )
231 # Create Attribute Obj
232 $Attrb = New-Object Management.Automation.ParameterAttribute
233 $Attrb.Mandatory=$Mandat
234 $Attrb.ValueFromPipeline=$Pipe
235 $Attrb.ValueFromPipelineByPropertyName=$PipeProp
236 if($Pos -ne $null){$Attrb.Position=$Pos}
237 # Create AttributeCollection
238 $Cllct = New-Object Collections.ObjectModel.Collection[System.Attribute]
239 # Add Attribute Obj to Collection
240 $Cllct.Add($Attrb)
241 if($VSet -ne $Null){
242 # Create ValidateSet & add to collection
243 $VldSt=New-Object Management.Automation.ValidateSetAttribute($VSet)
244 $Cllct.Add($VldSt)
245 }
246 # Create Runtine DynParam
247 $DynP = New-Object Management.Automation.RuntimeDefinedParameter("$Name",$($Type-as[type]),$Cllct)
248 # Return DynParam
249 Return $DynP
250 }
251#End
252
253################################################ GenEdgeStr
254function GenEdgeStr{
255<#
256.Synopsis
257 Generate Edge String [Internal]
258.DESCRIPTION
259 Generate Edge String for Cypher Queries
260.EXAMPLE
261 GenEdgeStr NoACL,NoSpc -Include ForceChangePassword
262#>
263 Param(
264 [ValidateSet('NoDef','NoACL','NoGPO','NoSpc')]
265 [Parameter(Mandatory=0)][String[]]$Type,
266 [Parameter(Mandatory=0)][Edgetype[]]$Exclude,
267 [Parameter(Mandatory=0)][Edgetype[]]$Include
268 )
269 # Start with all
270 $R = [Enum]::GetNames([EdgeType])
271 # Exclude Category
272 Switch -regex ($Type) {
273 NoDef {$R = (Compare $R ([Enum]::GetNames([EdgeDef]))).InputObject}
274 NoACL {$R = (Compare $R ([Enum]::GetNames([EdgeACL]))).InputObject}
275 NoGPO {$R = (Compare $R ([Enum]::GetNames([EdgeGPO]))).InputObject}
276 NoSpc {$R = (Compare $R ([Enum]::GetNames([EdgeSpc]))).InputObject}
277 }
278 # Exclude stuff
279 foreach($x in $Exclude){$R = $R -ne $x}
280 # Include stuff
281 Foreach($y in $Include){$R += $y}
282 # Return String
283 Return $R -join '|:'
284 }
285#end
286
287################################################# ToPathObj
288function ToPathObj{
289<#
290.Synopsis
291 Parse to Path Object [Internal]
292.DESCRIPTION
293 Format query result as Path Object
294.EXAMPLE
295 Example
296#>
297 [CmdletBinding()]
298 [OutputType([BHEdge])]
299 [Alias()]
300 Param(
301 [Parameter(ValueFromPipeline=1)][Object[]]$Data
302 )
303 Begin{$ID=0;$Result=@()}
304 Process{
305 foreach($D in $Data){
306 $StepCount = $D.relationships.count
307 # if Steps
308 if($StepCount -gt 0){
309 $PathObj = @()
310 0..($StepCount -1)|%{
311 [BHEdge]@{
312 'ID' = $ID
313 'Step' = $_
314 'StartNode' = (irm -Method Get -Headers $header -uri @($D.nodes)[$_]).data.name
315 'Edge' = (irm -Method Get -Headers $header -uri @($D.relationships)[$_]).type
316 'EndNode' = (irm -Method Get -Headers $header -uri @($D.nodes)[$_+1]).data.name
317 'Direction' = @($D.directions)[$_]
318 } | select 'ID','Step','StartNode','Edge','Direction','EndNode'}
319 $ID+=1
320 }}}
321 End{<#NoOp#>}
322 }
323#End
324
325################################################## ClipThis
326Function ClipThis{
327<#
328.Synopsis
329 Query to Clipboard [Internal]
330.DESCRIPTION
331 Displays resulting query and sets clipboard
332.EXAMPLE
333 ClipThis $Query [-with $Params]
334#>
335 [CmdletBinding()]
336 Param(
337 # Query
338 [Parameter(Mandatory=1)][String]$Query,
339 # Params
340 [Parameter(Mandatory=0)][Alias('With')][HashTable]$Params
341 )
342 # If Params
343 if($Params.count){$Params.keys|%{$Query=$Query.replace("{$_}","'$($Params.$_)'")}}
344 # Verbose
345 Write-Verbose "$Query"
346 # Clipboard
347 $Query | Set-ClipBoard
348 # Return Query
349 Return $Query
350 }
351#End
352
353################################################ JoinCypher
354function JoinCypher{
355<#
356.Synopsis
357 Cypher Query Union
358.DESCRIPTION
359 Join Cypher Querie with UNION
360.EXAMPLE
361 Example
362#>
363 [Alias('Union')]
364 Param(
365 [Parameter(ValueFromPipeline=1)][string[]]$Queries
366 )
367 Begin{$QCollection = @()}
368 Process{foreach($Q in $Queries){$QCollection+=$Q}}
369 End{$Out=$QCollection-join"`r`nUNION ALL`r`n";$Out|Set-clipboard;Return $Out}
370 }
371#End
372
373################################################# FixPathID
374function FixPathID{
375<#
376.Synopsis
377 Fix Path ID
378.DESCRIPTION
379 Fix Path ID
380.EXAMPLE
381 Example
382#>
383 [Alias('FixID')]
384 Param(
385 [Parameter(mandatory=1,ValueFromPipeline=1)][BHEdge]$Path
386 )
387 Begin{$ID=-1}
388 Process{foreach($P in $Path){
389 if($P.Step -eq 0){$ID+=1}
390 $P.ID=$ID
391 Return $P
392 }}
393 End{}
394 }
395#end
396
397#endregion ################################################
398
399
400###########################################################
401#region ############################################## MISC
402
403# Get-BloodHoundCmdlet
404# Send-BloodHoundPost
405
406################################################ BloodHound
407function Get-BloodHoundCmdlet{
408<#
409.Synopsis
410 BloodHound RTFM - Get Cmdlet
411.DESCRIPTION
412 Get Bloodhound [CypherDog] Cmdlets
413.EXAMPLE
414 BloodHound
415.EXAMPLE
416 BloodHound -Online
417#>
418 [CmdletBinding(HelpURI='https://Github.com/SadProcessor')]
419 [Alias('BloodHound','CypherDog')]
420 Param([Parameter()][Switch]$Online)
421 if($Online){Get-Help Get-BloodHoundCmdlet -Online; Return}
422 $CmdList = @(
423 ######################################################################################################################
424 # CMDLET | SYNOPSIS | Alias |
425 ######################################################################################################################
426 @{Cmdlet='Get-BloodHoundCmdlet' ; Synopsis='BloodHound RTFM - Get Cmdlet' ; Alias='BloodHound' }
427 @{Cmdlet='Send-BloodHoundPost' ; Synopsis='BloodHound POST - Cypher to REST API' ; Alias='DogPost' }
428 ######################################################################################################################
429 @{Cmdlet='Get-BloodHoundNode' ; Synopsis='BloodHound Node - Get Node' ; Alias='Node' }
430 @{Cmdlet='Search-BloodHoundNode' ; Synopsis='BloodHound Node - Search Node' ; Alias='NodeSearch' }
431 @{Cmdlet='New-BloodHoundNode' ; Synopsis='BloodHound Node - Create Node' ; Alias='NodeCreate' }
432 @{Cmdlet='Set-BloodHoundNode' ; Synopsis='BloodHound Node - Update Node' ; Alias='NodeUpdate' }
433 @{Cmdlet='Remove-BloodHoundNode' ; Synopsis='BloodHound Node - Delete Node' ; Alias='NodeDelete' }
434 @{Cmdlet='Get-BloodHoundNodeList' ; Synopsis='BloodHound Node - Get List' ; Alias='List' }
435 @{Cmdlet='Get-BloodHoundNodeHighValue' ; Synopsis='BloodHound Node - Get HighValue' ; Alias='HighValue' }
436 @{Cmdlet='Get-BloodHoundNodeOwned' ; Synopsis='BloodHound Node - Get Owned' ; Alias='Owned' }
437 @{Cmdlet='Get-BloodHoundNodeNote' ; Synopsis='BloodHound Node - Get Notes' ; Alias='Note' }
438 @{Cmdlet='Set-BloodHoundNodeNote' ; Synopsis='BloodHound Node - Set Notes' ; Alias='NoteUpdate' }
439 @{Cmdlet='Get-BloodHoundBlacklist' ; Synopsis='BloodHound Node - Get Blacklist' ; Alias='Blacklist' }
440 @{Cmdlet='Set-BloodHoundBlacklist' ; Synopsis='BloodHound Node - Set Blacklist' ; Alias='BlacklistAdd' }
441 @{Cmdlet='Remove-BloodHoundBlacklist' ; Synopsis='BloodHound Node - Remove Blacklist' ; Alias='BlacklistDelete' }
442 ######################################################################################################################
443 @{Cmdlet='Get-BloodHoundEdge' ; Synopsis='BloodHound Edge - Get Target' ; Alias='Edge' }
444 @{Cmdlet='Get-BloodHoundEdgeReverse' ; Synopsis='BloodHound Edge - Get Source' ; Alias='EdgeR' }
445 @{Cmdlet='Get-BloodHoundEdgeCrossDomain' ; Synopsis='BloodHound Edge - Get CrossDomain' ; Alias='CrossDomain' }
446 @{Cmdlet='Get-BloodHoundEdgeCount' ; Synopsis='BloodHound Edge - Get Count' ; Alias='EdgeCount' }
447 @{Cmdlet='Get-BloodHoundEdgeInfo' ; Synopsis='BloodHound Edge - Get Info' ; Alias='EdgeInfo' }
448 @{Cmdlet='New-BloodHoundEdge' ; Synopsis='BloodHound Edge - Create Edge' ; Alias='EdgeCreate' }
449 @{Cmdlet='Remove-BloodHoundEdge' ; Synopsis='BloodHound Edge - Delete Edge' ; Alias='EdgeDelete' }
450 ######################################################################################################################
451 @{Cmdlet='Get-BloodHoundPathShort' ; Synopsis='BloodHound Path - Get Shortest' ; Alias='Path' }
452 @{Cmdlet='Get-BloodHoundPathAny' ; Synopsis='BloodHound Path - Get Any' ; Alias='PathAny' }
453 @{Cmdlet='Get-BloodHoundPathCost' ; Synopsis='BloodHound Path - Get Cost' ; Alias='PathCost' }
454 @{Cmdlet='Get-BloodHoundPathCheap' ; Synopsis='BloodHound Path - Get Cheapest' ; Alias='PathCheap' }
455 @{Cmdlet='Get-BloodHoundWald0IO' ; Synopsis='BloodHound Path - Wald0 Index' ; Alias='Wald0IO' }
456 @{Cmdlet='Get-BloodHoundWald0IOAVG' ; Synopsis='BloodHound Path - Wald0 Index Average'; Alias='Wald0IOAVG' }
457 ######################################################################################################################
458 )
459 # Return Help Obj
460 Return $CmdList | %{New-Object PSCustomObject -Property $_} | Select Cmdlet,Synopsis,Alias,@{n='RTFM';e={"Help $($_.Alias)"}}
461 }
462#End
463
464################################################### DogPost
465function Send-BloodHoundPost{
466<#
467.Synopsis
468 BloodHound POST - Cypher to REST API
469.DESCRIPTION
470 DogPost $Query [$Params] [-expand <prop,prop>]
471 Post Cypher Query to DB REST API
472.EXAMPLE
473 $query="MATCH (n:User) RETURN n"
474 DogPost $Query
475.EXAMPLE
476 $query = "MATCH (A:Computer {name: {ParamA}}) RETURN A"
477 $Params = @{ParamA="APOLLO.EXTERNAL.LOCAL"}
478 DogPost $Query $Params
479.EXAMPLE
480 $Query = "MATCH (A:User {name: {ParamA}}), (B:Group {name: {ParamB}}), x=shortestPath((A)-[*1..]->(B)) RETURN x"
481 $Params= @{ParamA="ACHAVARIN@EXTERNAL.LOCAL";ParamB="DOMAIN ADMINS@EXTERNAL.LOCAL"}
482 DogPost $Query $Params -Expand Data | ToPathObj
483.EXAMPLE
484 $Query="MATCH
485 (U:User)-[r:MemberOf|:AdminTo*1..]->(C:Computer)
486 WITH
487 U.name as n,
488 COUNT(DISTINCT(C)) as c
489 RETURN
490 {Name: n, Count: c} as SingleColumn
491 ORDER BY c DESC
492 LIMIT 10"
493 DogPost $Query -x $Null
494#>
495 [CmdletBinding()]
496 [Alias('DogPost')]
497 Param(
498 [Parameter(Mandatory=1)][string]$Query,
499 [Parameter(Mandatory=0)][Hashtable]$Params,
500 [Parameter(Mandatory=0)][Alias('x')][String[]]$Expand=@('data','data'),
501 [Parameter(Mandatory=0)][Switch]$Profile,
502 [Parameter(Mandatory=0)][pscredential]$Credential = $script:Neo4jCred
503 )
504 # Uri
505 $Uri = "http://$($CypherDog.Host):$($CypherDog.Port)/db/data/cypher"
506 # Header
507 $Header=@{'Accept'='application/json; charset=UTF-8';'Content-Type'='application/json'}
508 # Query
509 if($Profile){$QUery="PROFILE "+$Query;$Expand='plan'}
510 # Body
511 if($Params){$Body = @{params=$Params; query=$Query}|Convertto-Json}
512 else{$Body = @{query=$Query}|Convertto-Json}
513 # Call
514 write-verbose $Body.replace(')\u003c-',')<-').replace('-\u003e(','->(').replace('\r','').replace('\n',' ').replace('\u0027',"'")
515
516 if($null -ne $credential){
517 $Reply = Try{Invoke-RestMethod -Uri $Uri -Method Post -Headers $Header -Body $Body -Credential $Credential}Catch{$Oops = $Error[0].ErrorDetails.Message}
518 }else{
519 $Reply = Try{Invoke-RestMethod -Uri $Uri -Method Post -Headers $Header -Body $Body }Catch{$Oops = $Error[0].ErrorDetails.Message}
520 }
521
522 # Format obj
523 if($Oops){Write-Warning "$((ConvertFrom-Json $Oops).errors.message)";Return}
524 if($Expand){$Expand | %{$Reply = $Reply.$_}}
525 if($Profile){
526 $Output = @(); $Step = 0; $Obj = $Reply
527 while($Step -eq 0 -OR $Obj.children){
528 if($Obj){
529 [HashTable]$Props = @{}
530 $Props.add('Step',"$Step")
531 $Props.add('Name',"$($Obj.name)")
532 $Argum = $Obj.args
533 $Argum | GM | ? MemberType -eq NoteProperty | %{
534 $Key = $_.name; $Value = $Argum.$Key
535 $Props.add("$Key","$Value")
536 }
537 $Output += New-Object PSCustomObject -Property $Props
538 }
539 $Obj = $Obj.children; $Step += 1; $Reply = $Output
540 }}
541 # Output Reply
542 if($Reply){Return $Reply}
543 }
544#End
545
546#endregion ################################################
547
548
549###########################################################
550#region ############################################## NODE
551
552# Get-BloodHoundNode
553# Search-BloodHoundNode
554# New-BloodHoundNode
555# Set-BloodHoundNode
556# Remove-BloodHoundNode
557# Get-BloodHoundNodeList
558# Get-BloodHoundNodeHighValue
559# Get-BloodHoundNodeOwned
560# Get-BloodHoundNodeNote
561# Set-BloodHoundNodeNote
562# Get-BloodHoundBlacklist
563# Set-BloodHoundBlacklist
564# Remove-BloodHoundBlacklist
565
566###################################################### Node
567function Get-BloodHoundNode{
568<#
569.Synopsis
570 BloodHound Node - Get Node
571.DESCRIPTION
572 Get BloodHound Node by Type and Name(s)
573.EXAMPLE
574 Get-BloodhoundNode User
575.EXAMPLE
576 Node User BRITNI_GIRARDIN@DOMAIN.LOCAL
577#>
578 [CmdletBinding()]
579 [Alias('Get-Node','Node')]
580 Param(
581 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$Type,
582 [Parameter(Mandatory=0)][Switch]$Label,
583 [Parameter(Mandatory=0)][Switch]$Notes,
584 [Parameter(Mandatory=0)][Switch]$Cypher
585 )
586 DynamicParam{
587 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
588 # Prep DynNamelist
589 $DynNameList = @($Script:CypherDog."${Type}List")
590 # Prep DynP
591 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 0 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $DynNameList
592 # DynP to Dico
593 $Dico.Add("Name",$DynName)
594 # Return Dico
595 Return $Dico
596 }
597 Begin{<#NoOp#>}
598 Process{
599 ## If No Name
600 If(-Not$DynName.IsSet){
601 # Query
602 if($Label){Write-Warning "Must specify Name(s) when requesting Labels...";Return}
603 else{$Query = "MATCH (n:$Type) RETURN n ORDER BY n.name"}
604 if(-Not$Cypher){DogPost $Query}
605 }
606 ## Else, for each name
607 Else{Foreach($Name in $DynName.Value){
608 # If Label
609 if($Label){
610 $Query = "MATCH (n:$Type {name: '$Name'}) RETURN LABELS(n)"
611 if(-Not$Cypher){
612 $L= DogPost $Query -expand data | Select -ExpandProperty SyncRoot
613 New-Object PSCustomObject -Property @{Name="$Name";Label=@($L)}
614 }}
615 else{$Query = "MATCH (n:$Type {name: '$Name'}) RETURN n"
616 if(-Not$Cypher){
617 $Res = DogPost $Query
618 If($Notes){$Res | Select -Expand notes -ea SilentlyContinue}
619 Else{$Res}
620 }}}}}
621 End{if($Cypher){ClipThis $Query}}
622 }
623#End
624
625################################################ NodeSearch
626function Search-BloodHoundNode{
627<#
628.Synopsis
629 BloodHound Node - Search Node
630.DESCRIPTION
631 Search Nodes by partial Name or Properties
632.EXAMPLE
633 NodeSearch Group admin
634.EXAMPLE
635 Nodesearch User -Property sensitive -Value $true
636#>
637 [CmdletBinding(DefaultParameterSetName='Key')]
638 [Alias('Search-Node','NodeSearch')]
639 Param(
640 # Node Type
641 [Parameter(Mandatory=0,Position=0,ParameterSetName='Key')]
642 [Parameter(Mandatory=0,Position=0,ParameterSetName='PropNot')]
643 [Parameter(Mandatory=0,Position=0,ParameterSetName='PropVal')]
644 [Parameter(Mandatory=0,Position=0,ParameterSetName='Prop')]
645 [Parameter(Mandatory=0,Position=0,ParameterSetName='LabelNot')]
646 [Parameter(Mandatory=0,Position=0,ParameterSetName='Label')][NodeType]$Type,
647 # Property Name
648 [Parameter(Mandatory=1,ParameterSetName='PropNot')]
649 [Parameter(Mandatory=1,ParameterSetName='PropVal')]
650 [Parameter(Mandatory=1,ParameterSetName='Prop')][String]$Property,
651 # Label
652 [Parameter(Mandatory=1,ParameterSetName='LabelNot')]
653 [Parameter(Mandatory=1,ParameterSetName='Label')][String]$Label,
654 # Property Name & Value
655 [Parameter(Mandatory=1,ParameterSetName='PropVal')][String]$Value,
656 # Property/Label doesn't exists
657 [Parameter(Mandatory=1,ParameterSetName='LabelNot')]
658 [Parameter(Mandatory=1,ParameterSetName='PropNot')][Switch]$NotExist,
659 # KeyWord
660 [Parameter(Mandatory=1,Position=1,ParameterSetName='Key')][Regex]$Key,
661 # Case Sensitive
662 [Parameter(Mandatory=0,ParameterSetName='Key')][Switch]$Sensitive,
663 # Show Cypher
664 [Parameter(Mandatory=0)][Switch]$Cypher
665 )
666 if($Type -ne $null){$T=":$type"}
667 if(-Not$Sensitive){$CS='(?i)'}
668 # Prep Query
669 Switch ($PSCmdlet.ParameterSetName){
670 "Key" {$Query= "MATCH (X$T) WHERE X.name =~ {KEY} RETURN X ORDER BY X.name" ; $Param= @{KEY="$CS.*$Key.*"}}
671 "Label" {$Query= "MATCH (X$T) WHERE X:$Label RETURN X ORDER BY X.name" ; $Param= $Null}
672 "LabelNot"{$Query= "MATCH (X$T) WHERE NOT X:$Label RETURN X ORDER BY X.name" ; $Param= $Null}
673 "Prop" {$Query= "MATCH (X$T) WHERE exists(X.$Property) RETURN X ORDER BY X.name" ; $Param= $Null}
674 "PropNot" {$Query= "MATCH (X$T) WHERE NOT exists(X.$Property) RETURN X ORDER BY X.name"; $Param= $Null}
675 "PropVal" {
676 if(-not($Value -match "true|false" -OR $value -as [int])){$Value = "'$Value'"}
677 $Query= "MATCH (X$T) WHERE X.$Property = $Value RETURN X ORDER BY X.name"
678 $Param= $Null
679 }}
680 # Call Dog
681 if($Cypher){ClipThis $Query $Param}
682 Else{DogPost $Query $Param}
683 }
684#End
685
686################################################ NodeCreate
687function New-BloodHoundNode{
688<#
689.Synopsis
690 BloodHound Node - Create Node
691.DESCRIPTION
692 Create New Node by type
693.EXAMPLE
694 New-BloodHoundNode -Type User -name Bob
695.EXAMPLE
696 NodeCreate User Bob
697#>
698 [CmdletBinding(DefaultParameterSetName="Other")]
699 [Alias('New-Node','NodeCreate')]
700 Param(
701 # Node Type [Mandatory]
702 [Parameter(Mandatory=1,Position=0)][NodeType]$Type,
703 # Node Name [Mandatory]
704 [Parameter(Mandatory=1,Position=1,ValueFromPipeline=1)][String[]]$Name,
705 # Specify Node Properties [Option]
706 [Parameter(Mandatory=0,Position=2,ParameterSetName='Props')][Hashtable]$Property,
707 # Clone similar Node Properties [Option]
708 [Parameter(Mandatory=1,ParameterSetName='Clone')][Switch]$Clone,
709 # Cypher [Option]
710 [Parameter(Mandatory=0)][Switch]$Cypher
711 )
712 Begin{$Query = "MERGE (X:$Type {name: {NAME}})"}
713 Process{
714 Foreach($N in $Name){
715 $Param = @{NAME="$N"}
716 if(-Not$Cypher){DogPost $Query $Param}
717 }
718 # Cache Updated Type
719 if(-Not$Cypher){
720 # Refresh cache
721 CacheNode $Type
722 # If Props
723 if($Property.Count){$P=$Property}
724 # If Clone
725 if($Clone){
726 [HashTable]$P=@{}
727 (Node $Type | Get-Member | Where MemberType -eq Noteproperty).name -ne 'name' | %{$P.add($_,'tbd')}
728 }
729 foreach($N in $Name){
730 $Splat = @{
731 Type=$type
732 Name=$Name
733 }
734 if($P.count){
735 $Splat.add('Property',$P)
736 NodeUpdate @Splat
737 }}}}
738 # If Cypher ####
739 End{if($Cypher){
740 $FullQ="$Query`r`n$(NodeUpdate @Splat -Cypher)"
741 ClipThis $FullQ $Param
742 }}}
743#########End
744
745################################################ NodeUpdate
746function Set-BloodHoundNode{
747<#
748.Synopsis
749 BloodHound Node - Update Node
750.DESCRIPTION
751 Update BloodHound Node Properties
752.EXAMPLE
753 Set-BloodHoundNode User Bob @{MyProp='This'}
754#>
755 [CmdletBinding(DefaultParameterSetName='UpdateProp')]
756 [Alias('Set-Node','NodeUpdate')]
757 Param(
758 [Parameter(Mandatory=1,Position=0,ParameterSetName='DeleteLabel')]
759 [Parameter(Mandatory=1,Position=0,ParameterSetName='UpdateLabel')]
760 [Parameter(Mandatory=1,Position=0,ParameterSetName='UpdateProp')]
761 [Parameter(Mandatory=1,Position=0,ParameterSetName='DeleteProp')][NodeType]$Type,
762 [Parameter(Mandatory=1,ParameterSetName='DeleteLabel')]
763 [Parameter(Mandatory=1,ParameterSetName='DeleteProp')][Switch]$Delete,
764 [Parameter(Mandatory=0,ParameterSetName='DeleteLabel')]
765 [Parameter(Mandatory=0,ParameterSetName='UpdateLabel')]
766 [Parameter(Mandatory=0,ParameterSetName='DeleteProp')]
767 [Parameter(Mandatory=0,ParameterSetName='UpdateProp')][Switch]$Cypher,
768 [Parameter(Mandatory=1,ParameterSetName='DeleteLabel')]
769 [Parameter(Mandatory=1,ParameterSetName='UpdateLabel')][Switch]$Label
770 )
771 DynamicParam{
772 # Prep Dico
773 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
774 # Prep DynNamelist
775 $DynNameList = $Script:CypherDog."${Type}List"
776 # Prep DynP
777 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $DynNameList
778 $Dico.Add('Name',$DynName)
779 # If Delete Prop
780 if($PSCmdlet.ParameterSetName -eq 'DeleteProp'){
781 $DynProp = DynP -Name 'Property' -Type 'String[]'-Mandat 1 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
782 $Dico.Add('Property',$DynProp)
783 }
784 # If Update Prop
785 if($PSCmdlet.ParameterSetName -eq 'UpdateProp'){
786 $DynProp = DynP -Name 'Property' -Type 'HashTable'-Mandat 1 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
787 $Dico.Add('Property',$DynProp)
788 }
789 # If Label Update/delete
790 if($PSCmdlet.ParameterSetName -in 'UpdateLabel','DeleteLabel'){
791 $DynLabel = DynP -Name 'LabelName' -Type 'String[]' -Mandat 1 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
792 $Dico.Add('LabelName',$DynLabel)
793 }
794 # Return Dico
795 Return $Dico
796 }
797 Begin{<#NoOp#>}
798 Process{foreach($Name in @($DynName.Value)){
799 # Set Name Param
800 $Param = @{NAME="$Name"}
801 # If Delete props
802 if($PSCmdlet.ParameterSetName -eq 'DeleteProp'){
803 # Query
804 $Query="MATCH (X:$Type) WHERE X.name = {NAME} REMOVE"
805 # Append each Prop Names
806 $DynProp.Value|%{$Query += " X.$_,"}
807 }
808 # If Update Props
809 if($PSCmdlet.ParameterSetName -eq 'UpdateProp'){
810 # Query
811 $Query = "MATCH (X:$Type) WHERE X.name = {NAME} SET"
812 # For each Prop
813 $DynProp.Value.Keys|%{
814 # Append Prop to Query
815 $Query+=" X.$_={$_},"
816 # Add to Param
817 $Param += @{$_=$($DynProp.Value.$_)}
818 }}
819 # If Update Label
820 if($PSCmdlet.ParameterSetName -eq 'UpdateLabel'){
821 # Query
822 $Query = "MATCH (X:$Type) WHERE X.name = {NAME} SET"
823 # For each Prop
824 $DynLabel.Value|%{
825 # Append Prop to Query
826 $Query+=" X:$_,"
827 }}
828 # If Delete Label
829 if($PSCmdlet.ParameterSetName -eq 'DeleteLabel'){
830 # Query
831 $Query = "MATCH (X:$Type) WHERE X.name = {NAME} REMOVE"
832 # For each Prop
833 $DynLabel.Value|%{
834 # Append Prop to Query
835 $Query+=" X:$_,"
836 }}
837 # Query
838 $Query=$Query.trimEnd(',')
839 # If Not Cypher
840 if(-Not$Cypher){DogPost $Query $Param}
841 }}
842 End{if($Cypher){ClipThis $Query $Param}}
843 }
844#End
845
846################################################ NodeDelete
847function Remove-BloodHoundNode{
848<#
849.Synopsis
850 BloodHound Node - Delete Node
851.DESCRIPTION
852 Delete Bloodhound Node from Database
853.EXAMPLE
854 Remove-BloodhoundNode Remove-BloodHoundNode -Type User -Name Bob
855.EXAMPLE
856 NodeDelete User Bob -Force
857#>
858 [CmdletBinding(SupportsShouldProcess=1,ConfirmImpact='High')]
859 [Alias('Remove-Node','NodeDelete')]
860 Param(
861 # Node Type [Mandatory]
862 [Parameter(Mandatory=1,Position=0)][NodeType]$Type,
863 # Force (Skip Confirm)
864 [Parameter(Mandatory=0)][Alias('x')][Switch]$Force,
865 # Force (Skip Confirm)
866 [Parameter(Mandatory=0)][Switch]$Cypher
867 )
868 DynamicParam{
869 # Prep Dico
870 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
871 # Prep DynNamelist
872 $DynNameList = $Script:CypherDog."${Type}List"
873 # Prep DynP
874 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $DynNameList
875 $Dico.Add('Name',$DynName)
876 # Return Dico
877 Return $Dico
878 }
879 Begin{$Query = "MATCH (X:$Type {name: {NAME}}) DETACH DELETE X"}
880 Process{
881 Foreach($N in $DynName.Value){
882 $Param = @{NAME="$N"}
883 if($Cypher){ClipThis $Query $Param}
884 # Else
885 Else{
886 # If Force
887 if($Force){DogPost $Query $Param}
888 # Else Confirm
889 else{if($PSCmdlet.ShouldProcess($N,'DELETE NODE')){
890 # Call Dog
891 DogPost $Query $Param
892 }}}}}
893 # Cache Node Type ##
894 End{if(-Not$Cypher){CacheNode $Type}}
895 }
896#End
897
898###################################################### List
899Function Get-BloodHoundNodeList{
900<#
901.Synopsis
902 BloodHound Node - Get List
903.DESCRIPTION
904 List BloodHound nodes per Edge
905.EXAMPLE
906 List Membership ALBINA_BRASHEAR@DOMAIN.LOCAL
907#>
908 [Cmdletbinding()]
909 [Alias('NodeList','List')]
910 Param(
911 [ValidateSet('logon','Session','AdminTo','AdminBy','Member','Membership')]
912 [Parameter(Mandatory=1,Position=0)][String]$Type
913 )
914 DynamicParam{
915 # Prep Name List
916 Switch($Type){
917 Logon {$NameList = $CypherDog.UserList }
918 Session {$NameList = $CypherDog.ComputerList}
919 AdminTo {$NameList = $CypherDog.ComputerList}
920 AdminBy {$NameList = $CypherDog.UserList }
921 Member {$NameList = $CypherDog.GroupList }
922 Membership {$NameList = $CypherDog.UserList }
923 }
924 # DynDico
925 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
926 # DynName
927 $DynName = DynP -Name 'Name' -Type 'String' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $NameList
928 $Dico.Add('Name',$DynName)
929 # DynSub
930 $Pos=2
931 if($PSBoundParameters.Type -match "AdminTo|AdminBy"){
932 $DynSub=DynP -Name 'SubType' -Type 'String' -Mandat 0 -Pos $Pos -Pipe 0 -PipeProp 0 -VSet @('Direct','Delegated','Derivative')
933 $Dico.Add('SubType',$DynSub)
934 $Pos+=1
935 }
936 # DynDom
937 $DynDom = DynP -Name 'Domain' -Type 'String' -Pos $Pos -Mandat 0 -VSet @($Script:CypherDog.DomainList)
938 $Dico.Add('Domain',$DynDom)
939 $Pos+=1
940 # DynCypher
941 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos $Pos -Pipe 0 -PipeProp 0 -VSet $Null
942 $Dico.Add('Cypher',$DynCypher)
943 # Return Dico
944 Return $Dico
945 }
946 Begin{
947 # Edge string
948 Switch ($DynSub.Value){
949 Direct {$E=':AdminTo*1'}
950 Delegated {$E=':MemberOf*1..]->(g:Group)-[r2:AdminTo'}
951 Derivative{$E=':MemberOf|:AdminTo|:HasSession*1..'}
952 Default {$E=':MemberOf|:AdminTo|:HasSession*1..'}
953 }
954 # Domain
955 if($DynDom.Value){$D=" {domain: '$($DynDom.Value)'}"}
956 }
957 Process{
958 $N=$DynName.Value
959 Switch($Type){
960 Logon {$M="p=shortestPath((C:Computer$D)-[r:HasSession*1]->(U:User {name: '$N'}))" ;$R="DISTINCT(C) ORDER BY C.name"}
961 Session {$M="p=shortestPath((C:Computer {name: '$N'})-[r:HasSession*1]->(U:User$D))" ;$R="DISTINCT(U) ORDER BY U.name"}
962 AdminTo {$M="p=((U:User$D)-[r$E]->(C:Computer {name: '$N'}))" ;$R="DISTINCT(U) ORDER BY U.name"}
963 AdminBy {$M="p=((U:User {name: '$N'})-[r$E]->(C:Computer$D))" ;$R="DISTINCT(C) ORDER BY C.name"}
964 Member {$M="p=shortestPath(((U:User$D)-[r:MemberOf*1..]->(G:Group {name: '$N'})))" ;$R="DISTINCT(U) ORDER BY U.name"}
965 Membership {$M="p=shortestPath(((U:User {name: '$N'})-[r:MemberOf*1..]->(G:Group$D)))" ;$R="DISTINCT(G) ORDER BY G.name"}
966 }
967 if($DynCypher.IsSet){clipThis "MATCH $M RETURN p"}
968 else{DogPost "MATCH $M RETURN $R"}
969 }
970 End{}
971 }
972#End
973
974################################################# HighValue
975Function Get-BloodHoundNodeHighValue{
976<#
977.Synopsis
978 BloodHound Node - Get HighValue
979.DESCRIPTION
980 Get Bloodhound HighValueNode
981.EXAMPLE
982 HighValue User
983#>
984 [Alias('Get-NodeHighValue','HighValue')]
985 Param(
986 [ValidateSet('User','Computer','Group')]
987 [Parameter(Mandatory=0,Position=0)][String]$Type="User"
988 )
989 DynamicParam{
990 # DynDico
991 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
992 # Prep DynParam
993 $DynDom = DynP -Name 'Domain' -Type 'String' -Mandat 0 -Pos 1 -VSet @($Script:CypherDog.DomainList)
994 $Dico.Add('Domain',$DynDom)
995 # Return Dico
996 Return $Dico
997 }
998 Begin{
999 $Type = $type.ToString().Replace($Type[0],$Type[0].ToString().toUpper())
1000 If($Domain){$Dom=" {domain: '$Domain'}"}
1001 }
1002 Process{
1003 $Query = "MATCH (X:$type$Dom) WHERE X.highvalue=True RETURN X"
1004 DogPost $Query
1005 }
1006 End{}
1007 }
1008#End
1009
1010##################################################### Owned
1011Function Get-BloodHoundNodeOwned{
1012<#
1013.Synopsis
1014 BloodHound Node - Get Owned
1015.DESCRIPTION
1016 Get BloodHound Owned Nodes per type
1017.EXAMPLE
1018 Owned Computer
1019#>
1020 [Alias('Get-NodeOwned','Owned')]
1021 Param(
1022 [ValidateSet('User','Computer','Group')]
1023 [Parameter(Mandatory=0,Position=0)][String]$Type='Computer'
1024 )
1025 DynamicParam{
1026 # DynDico
1027 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1028 # Prep DynParam
1029 $DynDom = DynP -Name 'Domain' -Type 'String' -Mandat 0 -Pos 1 -VSet @($Script:CypherDog.DomainList)
1030 $Dico.Add('Domain',$DynDom)
1031 # Return Dico
1032 Return $Dico
1033 }
1034 Begin{
1035 $Type = $type.ToString().Replace($Type[0],$Type[0].ToString().toUpper())
1036 If($Domain.IsSet){$Dom=" {domain: '$($Domain.Value)'}"}
1037 }
1038 Process{
1039 $Query = "MATCH (X:$type$Dom) WHERE X.owned=True RETURN X"
1040 DogPost $Query
1041 }
1042 End{}
1043 }
1044#End
1045
1046###################################################### Note
1047function Get-BloodHoundNodeNote{
1048<#
1049.Synopsis
1050 BloodHound Node - Get Note
1051.DESCRIPTION
1052 Get BloodHound Node Notes
1053.EXAMPLE
1054 note user ALBINA_BRASHEAR@DOMAIN.LOCAL
1055#>
1056 [CmdletBinding()]
1057 [Alias('NodeNote','Note')]
1058 Param(
1059 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$Type,
1060 [Parameter(Mandatory=0)][Switch]$Cypher
1061 )
1062 DynamicParam{
1063 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1064 # Prep DynNamelist
1065 $DynNameList = @($Script:CypherDog."${Type}List")
1066 # Prep DynP
1067 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $DynNameList
1068 # DynP to Dico
1069 $Dico.Add("Name",$DynName)
1070 # Return Dico
1071 Return $Dico
1072 }
1073 Begin{<#NoOp#>}
1074 Process{
1075 Foreach($N in $DynName.Value){
1076 $Query = "MATCH (n:$Type {name: '$N'}) RETURN n.notes"
1077 if(-Not$Cypher){DogPost $Query -Expand Data}
1078 }}
1079 End{if($Cypher){ClipThis $Query}}
1080 }
1081#End
1082
1083################################################ NoteUpdate
1084function Set-BloodHoundNodeNote{
1085<#
1086.Synopsis
1087 BloodHound Node - Set Notes
1088.DESCRIPTION
1089 Set BloodHound Node Notes
1090.EXAMPLE
1091 NoteUpdate user ALBINA_BRASHEAR@DOMAIN.LOCAL 'HelloWorld'
1092#>
1093 [CmdletBinding(DefaultParameterSetname='Set')]
1094 [Alias('Set-NodeNote','NoteUpdate')]
1095 Param(
1096 # Node Type [Mandatory]
1097 [Parameter(Mandatory=1,Position=0)][NodeType]$Type,
1098 # Overwrite
1099 [Parameter(ParameterSetname='Set',Mandatory=0)][Switch]$Overwrite,
1100 # Stamp
1101 [Parameter(ParameterSetname='Set',Mandatory=0)][Switch]$Stamp,
1102 # Cypher
1103 [Parameter(ParameterSetname='Clear',Mandatory=1)][Switch]$Clear,
1104 # Cypher
1105 [Parameter(Mandatory=0)][Switch]$Cypher
1106 )
1107 DynamicParam{
1108 # Prep Dico
1109 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1110 # Prep DynP
1111 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $Script:CypherDog."${Type}List"
1112 $Dico.Add('Name',$DynName)
1113 # If Set Text
1114 if($PSCmdlet.ParameterSetName -eq 'Set'){
1115 $DynText = DynP -Name 'Text' -Type 'String' -Mandat 1 -Pos 2 -Pipe 0 -PipeProp 0
1116 $Dico.Add('Text',$DynText)
1117 }
1118 # Return Dico
1119 Return $Dico
1120 }
1121 Begin{
1122 # Query0
1123 $Query0 = "MATCH (X:$Type) WHERE X.name = {NAME} Return X.notes"
1124 }
1125 Process{
1126 Foreach($N in $DynName.Value){
1127 # If Clear
1128 if($PSCmdlet.ParameterSetName -eq 'Clear'){
1129 $Query = "MATCH (X:$Type) WHERE X.name = '$N' SET X.notes=''"
1130 }
1131 # If Set
1132 else{
1133 $Param = @{NAME="$N"}
1134 # If Stamp
1135 if($Stamp){$New = "=== $(Get-date) - $enV:USERNAME ===`r`n$($DynText.Value)"}
1136 else{$New=$DynText.Value}
1137 # Get Old Text
1138 if(-Not$Overwrite){
1139 $Old = DogPost $Query0 $Param -Expand data
1140 if($Old){$New = ("$Old",$New)-join"`r`n"}
1141 }
1142 # Prep Query1
1143 $Query = "MATCH (X:$Type) WHERE X.name = {NAME} SET X.notes='$New'"
1144 }
1145 if($Cypher){ClipThis $Query $Param}
1146 # Else
1147 Else{DogPost $Query $Param}
1148 }}
1149 End{<#NoOp#>}
1150 }
1151#End
1152
1153################################################# Blacklist
1154function Get-BloodHoundBlacklist{
1155<#
1156.Synopsis
1157 BloodHound Node - Get Blacklist
1158.DESCRIPTION
1159 Get BloodHound Node Blacklist
1160.EXAMPLE
1161 Blacklist User
1162#>
1163 [Alias('Get-Blacklist','Blacklist')]
1164 Param(
1165 [Parameter(Mandatory=1,Position=0)][Nodetype]$Type
1166 )
1167 DogPost "MATCH (x:$type) WHERE x:Blacklist RETURN x ORDER BY x.name"
1168 }
1169#End
1170
1171########################################### BlacklistUpdate
1172function Set-BloodHoundBlacklist{
1173<#
1174.Synopsis
1175 BloodHound Node - Set Blacklist
1176.DESCRIPTION
1177 Set BloodHound Blacklist Node
1178.EXAMPLE
1179 BlacklistUpdate User Bob
1180#>
1181 [Alias('Set-Blacklist','BlacklistAdd')]
1182 Param(
1183 [Parameter(Mandatory=1,Position=0)][Nodetype]$Type
1184 )
1185 DynamicParam{
1186 # DynDico
1187 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1188 # Prep DynParam
1189 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 0 -Pos 1 -Pipe 1 -PipeProp 1 -VSet @($Script:CypherDog."${type}List")
1190 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
1191 $Dico.Add('Name',$DynName)
1192 $Dico.Add('Cypher',$DynCypher)
1193 # Return Dico
1194 Return $Dico
1195 }
1196 Begin{}
1197 Process{foreach($N in $DynName.Value){
1198 $Q="MATCH (x:$Type) WHERE x.name='$N' SET x:Blacklist"
1199 if(-Not$DynCypher.IsSet){DogPost $Q}
1200 }}
1201 End{if($DynCypher.IsSet){ClipThis $Q}}
1202 }
1203#End
1204
1205########################################### BlacklistDelete
1206function Remove-BloodHoundBlacklist{
1207<#
1208.Synopsis
1209 BloodHound Node - Remove Blacklist
1210.DESCRIPTION
1211 Remove Node from blacklist
1212.EXAMPLE
1213 BlacklistDelete User Bob
1214#>
1215 [Alias('Remove-Blacklist','BlacklistDelete')]
1216 Param(
1217 [Parameter(Mandatory=1,Position=0)][Nodetype]$Type
1218 )
1219 DynamicParam{
1220 # DynDico
1221 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1222 # Prep DynParam
1223 [Array]$VSet=(Get-BloodHoundBlacklist $type).name
1224 $VSet += "*"
1225 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 0 -Pos 1 -Pipe 1 -PipeProp 1 -VSet @($Vset)
1226 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
1227 $Dico.Add('Name',$DynName)
1228 $Dico.Add('Cypher',$DynCypher)
1229 # Return Dico
1230 Return $Dico
1231 }
1232 Begin{}
1233 Process{
1234 foreach($N in ($DynName.Value)){
1235 if($DynName.Value -eq "*"){$Q="MATCH (x:$Type) WHERE x:Blacklist REMOVE x:Blacklist"}
1236 else{$Q="MATCH (x:$Type) WHERE x.name='$N' REMOVE x:Blacklist"}
1237 if(-Not$DynCypher.IsSet){DogPost $Q}
1238 }}
1239 End{if($DynCypher.IsSet){ClipThis $Q}}
1240 }
1241#End
1242
1243#endregion ################################################
1244
1245
1246###########################################################
1247#region ############################################## EDGE
1248
1249# Get-BloodHoundEdge
1250# Get-BloodHoundEdgeReverse
1251# Get-BloodHoundEdgecrossDomain
1252# Get-BloodHoundEdgeCount
1253# Get-BloodHoundEdgeInfo
1254# New-BloodHoundEdge
1255# Remove-BloodHoundEdge
1256
1257
1258###################################################### Edge
1259function Get-BloodHoundEdge{
1260<#
1261.Synopsis
1262 BloodHound Edge - Get Target
1263.DESCRIPTION
1264 Specify Source Name / Return Target
1265.EXAMPLE
1266 Edge user ALBINA_BRASHEAR@DOMAIN.LOCAL MemberOf Group
1267#>
1268 [CmdletBinding()]
1269 [Alias('Get-Edge','Edge','WhereTo')]
1270 Param(
1271 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$SourceType
1272 )
1273 DynamicParam{
1274 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1275 # Prep DynNamelist
1276 $DynNameList = @($Script:CypherDog."${SourceType}List")
1277 # Prep DynP
1278 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 1 -Pipe 1 -PipeProp 1 -VSet $DynNameList
1279 $DynEdge = DynP -Name 'EdgeType' -Type 'EdgeType' -Mandat 1 -Pos 2 -Pipe 0 -PipeProp 0 -VSet $Null
1280 $DynTarget = DynP -Name 'TargetType' -Type 'NodeType' -Mandat 1 -Pos 3 -Pipe 0 -PipeProp 0 -VSet $Null
1281 $DynMax = DynP -Name 'Degree' -Type 'String' -Mandat 0 -Pos 4 -Pipe 0 -PipeProp 0 -VSet @('1','2','3','4','5','6','7','8','9','*')
1282 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
1283 # DynP to Dico
1284 $Dico.Add("Name" ,$DynName)
1285 $Dico.Add("EdgeType" ,$DynEdge)
1286 $Dico.Add("TargetType",$DynTarget)
1287 $Dico.Add("Degree" ,$DynMax)
1288 $Dico.Add("Cypher" ,$DynCypher)
1289 # Return Dico
1290 Return $Dico
1291 }
1292 Begin{
1293 $TargetType = $DynTarget.Value
1294 $EdgeType = $DynEdge.Value
1295 # Max Max
1296 If($DynMax.Value){
1297 If($DynMax.Value -eq '*'){$Max='..'}
1298 else{$Max=".."+(([int]$DynMax.Value))}
1299 }
1300 Else{$Max=$Null}
1301 $max
1302 # If Max and not MemberOf
1303 If($DynMax.Value -AND $EdgeType -ne 'MemberOf'){
1304 # Query
1305 if($Max -ne $null){
1306 #$Query = "MATCH (A:$TargetType), (B:$SourceType {name: {NAME}}), p=shortestPath((B)-[r:MemberOf*1$max]->(X:Group)-[r2:$EdgeType*1]->(A)) RETURN DISTINCT(A) ORDER BY A.name"
1307 $Query = "MATCH (A:$TargetType), (B:$SourceType {name: {NAME}}), p=shortestPath((B)-[r:MemberOf|:$EdgeType*1$Max]->(A)) RETURN DISTINCT(A) ORDER BY A.name"
1308 }
1309 else{$Query = "MATCH (A:$TargetType), (B:$SourceType {name: {NAME}}), p=shortestPath((B)-[r:$EdgeType*1$Max]->(A)) RETURN DISTINCT(A) ORDER BY A.name"}
1310 }
1311 Else{# Query
1312 $Query = "MATCH (A:$TargetType), (B:$SourceType {name: {NAME}}), p=(B)-[r:$EdgeType*1$Max]->(A) RETURN DISTINCT(A) ORDER BY A.name"
1313 }}
1314 Process{
1315 Foreach($SourceName in $DynName.Value){
1316 $Param = @{NAME="$SourceName"}
1317 if(-Not$DynCypher.IsSet){DogPost $Query $Param}
1318 }}
1319 End{if($DynCypher.IsSet){ClipThis ($Query-replace"RETURN.+$",'RETURN p') $Param}}
1320 }
1321#End
1322
1323##################################################### EdgeR
1324function Get-BloodHoundEdgeReverse{
1325<#
1326.Synopsis
1327 BloodHound Edge - Get Source
1328.DESCRIPTION
1329 Specify Target Name / Return Source
1330.EXAMPLE
1331 EdgeR User MemberOf Group ADMINISTRATORS@SUB.DOMAIN.LOCAL
1332#>
1333 [CmdletBinding()]
1334 [Alias('Get-EdgeR','EdgeR','What')]
1335 Param(
1336 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$SourceType,
1337 [Parameter(Mandatory=1,Position=1,ValuefromPipeline=0)][EdgeType]$EdgeType,
1338 [Parameter(Mandatory=1,Position=2,ValuefromPipeline=0)][NodeType]$TargetType
1339 )
1340 DynamicParam{
1341 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1342 # Prep DynNamelist
1343 $DynNameList = @($Script:CypherDog."${TargetType}List")
1344 # Prep DynP
1345 $DynName = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 3 -Pipe 1 -PipeProp 1 -VSet $DynNameList
1346 $DynMax = DynP -Name 'Degree' -Type 'String' -Mandat 0 -Pos 4 -Pipe 0 -PipeProp 0 -VSet @('1','2','3','4','5','6','7','8','9','*')
1347 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
1348 # DynP to Dico
1349 $Dico.Add("Name" ,$DynName)
1350 $Dico.Add("Degree" ,$DynMax)
1351 $Dico.Add("Cypher" ,$DynCypher)
1352 # Return Dico
1353 Return $Dico
1354 }
1355 Begin{
1356 # if Max
1357 If($DynMax.IsSet){
1358 if($DynMax.Value -eq '*'){$Max='..'}
1359 else{$Max=".."+(([int]$DynMax.Value))}
1360 }
1361 Else{$Max=$Null}
1362 # EdgeString
1363 If($EdgeType -ne 'MemberOf' -AND $DynMax.Value){
1364 $Query = "MATCH (A:$SourceType), (B:$TargetType {name: {NAME}}), p=shortestPath((A)-[r:${EdgeType}|:MemberOf*1$Max]->(B)) RETURN DISTINCT(A) ORDER BY A.name"
1365 }
1366 Else{$Query = "MATCH (A:$SourceType), (B:$TargetType {name: {NAME}}), p=(A)-[r:${EdgeType}*1$Max]->(B) RETURN DISTINCT(A) ORDER BY A.name"}
1367 }
1368 Process{Foreach($Name in $DynName.Value){
1369 $Param = @{NAME="$Name"}
1370 if(-Not$DynCypher.IsSet){DogPost $Query $Param}
1371 }}
1372 End{if($DynCypher.IsSet){ClipThis ($Query-replace"RETURN.+$",'RETURN p') $Param}}
1373 }
1374#End
1375
1376############################################### CrossDomain
1377function Get-BloodHoundEdgeCrossDomain{
1378<#
1379.Synopsis
1380 BloodHound Edge - Get CrossDomain
1381.DESCRIPTION
1382 Get BloodHound Cross Domain Member|Session Relationships
1383.EXAMPLE
1384 Get-BloodHoundCrossDomain Session
1385.EXAMPLE
1386 CrossDomain Member
1387#>
1388 [CmdletBinding()]
1389 [Alias('CrossDomain')]
1390 Param(
1391 [Validateset('Session','Member')]
1392 [Parameter(Mandatory=1)][String]$Type,
1393 [Parameter(Mandatory=0)][Switch]$Cypher
1394 )
1395 # Prep vars
1396 Switch($Type){
1397 Member {$Source='User' ;$target='Group';$Edge='MemberOf'}
1398 Session {$Source='Computer';$target='User' ;$Edge='HasSession'}
1399 }
1400 $PathQ = "MATCH p=((S:$Source)-[r:$Edge*1]->(T:$Target))
1401WHERE NOT S.domain = T.domain"
1402 if($Cypher){$Clip = "$PathQ`r`nRETURN p"; Set-Clipboard $Clip; Return $Clip}
1403 #Call
1404 dogpost "$PathQ
1405WITH p,
1406S.name AS Sname,
1407S.domain AS Sdomain,
1408T.name AS Tname,
1409T.domain AS Tdomain
1410RETURN {
1411From: Sdomain,
1412To: Tdomain,
1413Source: Sname,
1414Target: Tname
1415} as Obj" -Expand Data |
1416 Select -expand Syncroot |
1417 Add-Member -MemberType NoteProperty -Name Edge -Value $Edge -PassThru |
1418 Select From,to,Source,Edge,Target | Sort From,To,Target
1419 }
1420#End
1421
1422################################################# EdgeCount
1423function Get-BloodHoundEdgeCount{
1424<#
1425.Synopsis
1426 BloodHound Edge - Get Count
1427.DESCRIPTION
1428 Get Top Nodes By Edge Count
1429.EXAMPLE
1430 EdgeCount Membership
1431#>
1432 [CmdletBinding()]
1433 [Alias('EdgeCount','TopNode')]
1434 Param(
1435 [ValidateSet('AdminTo','AdminBy','Session','Logon','Member','Membership')]
1436 [Parameter(Mandatory=1,Position=0)][String]$type,
1437 [Parameter(Mandatory=0)][Int]$Limit=5,
1438 [Parameter(Mandatory=0)][Switch]$Cypher
1439 )
1440 DynamicParam{
1441 # DynDico
1442 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
1443 $Pos=1
1444 # Prep DynParam
1445 if($type -match "AdminTo|AdminBy"){
1446 $DynSub=DynP -Name 'SubType' -Type 'String' -Mandat 0 -Pos $Pos -VSet @('Direct','Delegated','Derivative')
1447 $Dico.add('SubType',$DynSub)
1448 $Pos=2
1449 }
1450 $DynDom = DynP -Name 'Domain' -Type 'String' -Pos $Pos -Mandat 0 -VSet @($Script:CypherDog.DomainList)
1451 $Dico.Add('Domain',$DynDom)
1452 # Return Dico
1453 Return $Dico
1454 }
1455 Process{
1456 Switch ($DynSub.Value){
1457 Direct {$E=':AdminTo*1'}
1458 Delegated {$E=':MemberOf*1..]->(g:Group)-[r2:AdminTo'}
1459 Derivative{$E=':MemberOf|:AdminTo|:HasSession*1..'}
1460 Default {$E=':MemberOf|:AdminTo|:HasSession*1..'}
1461 }
1462 if($DynDom.Value){$Dom=" {domain: '$($DynDom.Value)'}"}
1463 if($Limit -eq '0'){$Lim = $Null}Else{$Lim = "LIMIT $Limit"}
1464 # AdminBy
1465 if($type -eq 'AdminTo'){
1466 $Q1 = "MATCH p=((U:User)-[r$E]->(C:Computer$Dom))"
1467 $Q2 = "$Q1
1468WITH
1469C.name as c,
1470COUNT(DISTINCT(U)) as t
1471RETURN {Name: c, Count: t} as SingleColumn
1472ORDER BY t DESC
1473$Lim"
1474 }
1475 # AdminTo
1476 if($type -eq 'AdminBy'){
1477 $Q1 = "MATCH p=((S:User$Dom)-[r$E]->(T:Computer))"
1478 $Q2 = "$Q1
1479WITH
1480S.name as s,
1481COUNT(DISTINCT(T)) as t
1482RETURN {Name: s, Count: t} as SingleColumn
1483ORDER BY t DESC
1484$Lim"
1485 }
1486 # Session
1487 if($Type -eq 'Logon'){
1488 $Q1 = "MATCH p=shortestPath((U:User$Dom)<-[r:HasSession*1..]-(C:Computer))"
1489 $Q2 = "$Q1
1490WITH
1491U.name as n,
1492COUNT(DISTINCT(C)) as c
1493RETURN {Name: n, Count: c} as SingleColumn
1494ORDER BY c DESC
1495$Lim"
1496 }
1497 # Logon
1498 if($Type -eq 'Session'){
1499 $Q1 = "MATCH p=shortestPath((A:User)<-[r:HasSession*1]-(B:Computer$Dom))"
1500 $Q2 = "$Q1
1501WITH B.name as n,
1502COUNT(DISTINCT(A)) as c
1503RETURN {Name: n, Count: c} as SingleColumn
1504ORDER BY c DESC
1505$Lim"
1506 }
1507 # Group
1508 if($Type -eq 'Membership'){
1509 $Q1 = "MATCH p=shortestPath((A:User$Dom)-[r:MemberOf*1..]->(B:Group))"
1510 $Q2 = "$Q1
1511WITH A.name as n,
1512COUNT(DISTINCT(B)) as c
1513RETURN {Name: n, Count: c} as SingleColumn
1514ORDER BY c DESC
1515$Lim"
1516 }
1517 # Group
1518 if($Type -eq 'Member'){
1519 $Q1 = "MATCH p=shortestPath((A:User)-[r:MemberOf*1..]->(B:Group$Dom))"
1520 $Q2 = "$Q1
1521WITH B.name as n,
1522COUNT(DISTINCT(A)) as c
1523RETURN {Name: n, Count: c} as SingleColumn
1524ORDER BY c DESC
1525$Lim"
1526 }
1527 # Output
1528 If($cypher){$Q = "$Q1 RETURN p";Set-clipBoard $Q;Return $Q}
1529 Else{
1530 DogPost $Q2 -Expand Data| Select -Expand SyncRoot
1531 }}}
1532#########End
1533
1534################################################## EdgeInfo
1535function Get-BloodHoundEdgeInfo{
1536<#
1537.Synopsis
1538 BloodHound Edge - Get Info
1539.DESCRIPTION
1540 Get BloodHound Edge Info [online]
1541.EXAMPLE
1542 EdgeInfo MemberOf
1543.EXAMPLE
1544 EdgeInfo MemberOf -Online
1545#>
1546 [Alias('Get-EdgeInfo','EdgeInfo')]
1547 Param(
1548 [Parameter(Mandatory=1)][Edgetype]$Type,
1549 [Parameter(Mandatory=0)][Switch]$Online
1550 )
1551 Switch($Type){
1552#################################################################################
1553MemberOf{
1554$Info='Groups in active directory grant their members any privileges the group itself has. If a group has rights to another principal, users/computers in the group, as well as other groups inside the group inherit those permissions.'
1555#
1556$Abuse='No abuse is necessary. This edge simply indicates that a principal belongs to a security group.'
1557#
1558$Opsec='No opsec considerations apply to this edge.'
1559#
1560$Ref=@(
1561'https://adsecurity.org/?tag=ad-delegation'
1562'https://www.itprotoday.com/management-mobility/view-or-remove-active-directory-delegated-permissions'
1563)
1564}
1565#################################################################################
1566AdminTo{
1567$Info='By default, administrators have several ways to perform remote code execution on Windows systems,including via RDP, WMI, WinRM, the Service Control Manager, and remote DCOM execution.
1568Further, administrators have several options for impersonating other users logged onto the system,including plaintext password extraction, token impersonation, and injecting into processes running as another user.
1569Finally, administrators can often disable host-based security controls that would otherwise prevent the aforementioned techniques'
1570#
1571$Abuse="There are several ways to pivot to a Windows system.
1572If using Cobalt Strike's beacon, check the help info for the commands 'psexec', 'psexec_psh', 'wmi', and 'winrm'.
1573With Empire, consider the modules for Invoke-PsExec, Invoke-DCOM, and Invoke-SMBExec.
1574With Metasploit, consider the modules 'exploit/windows/smb/psexec', 'exploit/windows/winrm/winrm_script_exec', and 'exploit/windows/local/ps_wmi_exec'.
1575Additionally, there are several manual methods for remotely executing code on the machine, including via RDP, with the service control binary and interaction with the remote machine's service control manager, and remotely instantiating DCOM objects.
1576For more information about these lateral movement techniques, see the References tab."
1577#
1578$Opsec='There are several forensic artifacts generated by the techniques described above.
1579For instance, lateral movement via PsExec will generate 4697 events on the target system.
1580If the target organization is collecting and analyzing those events, they may very easily detect lateral movement via PsExec.
1581Additionally, an EDR product may detect your attempt to inject into lsass and alert a SOC analyst.
1582There are many more opsec considerations to keep in mind when abusing administrator privileges.
1583For more information, see the References tab.'
1584#
1585$Ref=@(
1586'https://attack.mitre.org/wiki/Lateral_Movement'
1587'http://blog.gentilkiwi.com/mimikatz'
1588'https://github.com/gentilkiwi/mimikatz'
1589'https://adsecurity.org/?page_id=1821'
1590'https://attack.mitre.org/wiki/Credential_Access'
1591'https://labs.mwrinfosecurity.com/assets/BlogFiles/mwri-security-implications-of-windows-access-tokens-2008-04-14.pdf'
1592'https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-TokenManipulation.ps1'
1593'https://attack.mitre.org/wiki/Technique/T1134'
1594'https://blog.netspi.com/10-evil-user-tricks-for-bypassing-anti-virus/'
1595'https://www.blackhillsinfosec.com/bypass-anti-virus-run-mimikatz/'
1596'https://blog.cobaltstrike.com/2017/06/23/opsec-considerations-for-beacon-commands/'
1597)
1598}
1599#################################################################################
1600HasSession{
1601#
1602$Info="When users authenticate to a computer, they often leave credentials exposed on the system, which can be retrieved through LSASS injection, token manipulation/theft, or injecting into a user's process.
1603Any user that is an administrator to the system has the capability to retrieve the credential material from memory if it still exists.
1604Note: A session does not guarantee credential material is present, only possible."
1605#
1606$Abuse="# Password Theft
1607When a user has a session on the computer, you may be able to obtain credentials for the user via credential dumping or token impersonation. You must be able to move laterally to the computer, have administrative access on the computer, and the user must have a non-network logon session on the computer.
1608Once you have established a Cobalt Strike Beacon, Empire agent, or other implant on the target, you can use mimikatz to dump credentials of the user that has a session on the computer. While running in a high integrity process with SeDebugPrivilege, execute one or more of mimikatz's credential gathering techniques (e.g.: sekurlsa::wdigest, sekurlsa::logonpasswords, etc.), then parse or investigate the output to find clear-text credentials for other users logged onto the system.
1609You may also gather credentials when a user types them or copies them to their clipboard! Several keylogging capabilities exist, several agents and toolsets have them built-in. For instance, you may use meterpreter's 'keyscan_start' command to start keylogging a user, then 'keyscan_dump' to return the captured keystrokes. Or, you may use PowerSploit's Invoke-ClipboardMonitor to periodically gather the contents of the user's clipboard.
1610
1611# Token Impersonation
1612You may run into a situation where a user is logged onto the system, but you can't gather that user's credential. This may be caused by a host-based security product, lsass protection, etc. In those circumstances, you may abuse Windows' token model in several ways. First, you may inject your agent into that user's process, which will give you a process token as that user, which you can then use to authenticate to other systems on the network. Or, you may steal a process token from a remote process and start a thread in your agent's process with that user's token. For more information about token abuses, see the References tab.
1613User sessions can be short lived and only represent the sessions that were present at the time of collection. A user may have ended their session by the time you move to the computer to target them. However, users tend to use the same machines, such as the workstations or servers they are assigned to use for their job duties, so it can be valuable to check multiple times if a user session has started."
1614#
1615$Opsec="An EDR product may detect your attempt to inject into lsass and alert a SOC analyst. There are many more opsec considerations to keep in mind when stealing credentials or tokens. For more information, see the References tab."
1616#
1617$Ref=@("http://blog.gentilkiwi.com/mimikatz"
1618"https://github.com/gentilkiwi/mimikatz"
1619"https://adsecurity.org/?page_id=1821"
1620"https://attack.mitre.org/wiki/Credential_Access"
1621"https://labs.mwrinfosecurity.com/assets/BlogFiles/mwri-security-implications-of-windows-access-tokens-2008-04-14.pdf"
1622"https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-TokenManipulation.ps1"
1623"https://attack.mitre.org/wiki/Technique/T1134")
1624}
1625#################################################################################
1626TrustedBy{Return}
1627#################################################################################
1628ForceChangePassword{
1629#
1630$Info="The capability to change the user password without knowing that user's current password."
1631#
1632$Abuse="There are at least two ways to execute this attack. The first and most obvious is by using the built-in net.exe binary in Windows (e.g.: net user dfm.a Password123! /domain).
1633See the opsec considerations tab for why this may be a bad idea.
1634The second, and highly recommended method, is by using the Set-DomainUserPassword function in PowerView.
1635This function is superior to using the net.exe binary in several ways.
1636For instance, you can supply alternate credentials, instead of needing to run a process as or logon as the user with the ForceChangePassword privilege.
1637Additionally, you have much safer execution options than you do with spawning net.exe (see the opsec tab).
1638To abuse this privilege with PowerView's Set-DomainUserPassword, first import PowerView into your agent session or into a PowerShell instance at the console. You may need to authenticate to the Domain Controller as a member of DC_3.DOMAIN.LOCAL if you are not running a process as a member. To do this in conjunction with Set-DomainUserPassword, first create a PSCredential object (these examples comes from the PowerView help documentation):
1639
1640`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1641`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1642
1643Then create a secure string object for the password you want to set on the target user:
1644
1645`$UserPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1646
1647Finally, use Set-DomainUserPassword, optionally specifying `$Cred if you are not already running a process as DC_3.DOMAIN.LOCAL:
1648
1649Set-DomainUserPassword -Identity andy -AccountPassword `$UserPassword -Credential `$Cred
1650
1651Now that you know the target user's plain text password, you can either start a new agent as that user, or use that user's credentials in conjunction with PowerView's ACL abuse functions, or perhaps even RDP to a system the target user has access to. For more ideas and information, see the references tab"
1652#
1653$Opsec="Executing this abuse with the net binary will necessarily require command line execution. If your target organization has command line logging enabled, this is a detection opportunity for their analysts.
1654Regardless of what execution procedure you use, this action will generate a 4724 event on the domain controller that handled the request. This event may be centrally collected and analyzed by security analysts, especially for users that are obviously very high privilege groups (i.e.: Domain Admin users). Also be mindful that PowerShell v5 introduced several key security features such as script block logging and AMSI that provide security analysts another detection opportunity. You may be able to completely evade those features by downgrading to PowerShell v2.
1655Finally, by changing a service account password, you may cause that service to stop functioning properly. This can be bad not only from an opsec perspective, but also a client management perspective. Be careful!"
1656#
1657$Ref=@("https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1658"https://www.youtube.com/watch?v=z8thoG7gPd0"
1659"https://www.sixdub.net/?p=579"
1660"https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4724")
1661}
1662#################################################################################
1663AddMember{
1664#
1665$Info='The User X has the ability to add arbitrary principals, including itself, to the Group Y. Because of security group delegation, the members of a security group have the same privileges as that group.
1666
1667By adding itself to the group, User X will gain the same privileges that Group Y already has.'
1668#
1669$Abuse="There are at least two ways to execute this attack. The first and most obvious is by using the built-in net.exe binary in Windows (e.g.: net group 'Domain Admins' dfm.a /add /domain). See the opsec considerations tab for why this may be a bad idea. The second, and highly recommended method, is by using the Add-DomainGroupMember function in PowerView. This function is superior to using the net.exe binary in several ways. For instance, you can supply alternate credentials, instead of needing to run a process as or logon as the user with the AddMember privilege. Additionally, you have much safer execution options than you do with spawning net.exe (see the opsec tab).
1670
1671To abuse this privilege with PowerView's Add-DomainGroupMember, first import PowerView into your agent session or into a PowerShell instance at the console. You may need to authenticate to the Domain Controller as AZALEE_CASALE@DOMAIN.LOCAL if you are not running a process as that user if you are not running a process as that user. To do this in conjunction with Add-DomainGroupMember, first create a PSCredential object (these examples comes from the PowerView help documentation):
1672
1673`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1674`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1675
1676Then, use Add-DomainGroupMember, optionally specifying `$Cred if you are not already running a process as User X:
1677
1678Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential `$Cred
1679
1680Finally, verify that the user was successfully added to the group with PowerView's Get-DomainGroupMember:
1681
1682Get-DomainGroupMember -Identity 'Domain Admins'"
1683#
1684$Opsec='Executing this abuse with the net binary will require command line execution. If your target organization has command line logging enabled, this is a detection opportunity for their analysts.
1685Regardless of what execution procedure you use, this action will generate a 4728 event on the domain controller that handled the request. This event may be centrally collected and analyzed by security analysts, especially for groups that are obviously very high privilege groups (i.e.: Domain Admins). Also be mindful that Powershell 5 introduced several key security features such as script block logging and AMSI that provide security analysts another detection opportunity.
1686You may be able to completely evade those features by downgrading to PowerShell v2.'
1687#
1688$Ref=@(
1689"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1690"https://www.youtube.com/watch?v=z8thoG7gPd0"
1691"https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4728"
1692)
1693}
1694#################################################################################
1695GenericAll{
1696$Info='This is also known as full control. This privilege allows the trustee to manipulate the target object however they wish.'
1697$Abuse="Full control of a group allows you to directly modify group membership of the group.
1698
1699There are at least two ways to execute this attack. The first and most obvious is by using the built-in net.exe binary in Windows (e.g.: net group 'Domain Admins' harmj0y /add /domain). See the opsec considerations tab for why this may be a bad idea. The second, and highly recommended method, is by using the Add-DomainGroupMember function in PowerView. This function is superior to using the net.exe binary in several ways. For instance, you can supply alternate credentials, instead of needing to run a process as or logon as the user with the AddMember privilege. Additionally, you have much safer execution options than you do with spawning net.exe (see the opsec tab).
1700
1701To abuse this privilege with PowerView's Add-DomainGroupMember, first import PowerView into your agent session or into a PowerShell instance at the console. You may need to authenticate to the Domain Controller as SYBLE_LEININGER@DOMAIN.LOCAL if you are not running a process as that user. To do this in conjunction with Add-DomainGroupMember, first create a PSCredential object (these examples comes from the PowerView help documentation):
1702
1703`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1704`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1705
1706Then, use Add-DomainGroupMember, optionally specifying `$Cred if you are not already running a process as SYBLE_LEININGER@DOMAIN.LOCAL:
1707
1708Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential `$Cred
1709
1710Finally, verify that the user was successfully added to the group with PowerView's Get-DomainGroupMember:
1711
1712Get-DomainGroupMember -Identity 'Domain Admins'"
1713$Opsec='This depends on the target object and how to take advantage of this privilege. Opsec considerations for each abuse primitive are documented on the specific abuse edges and on the BloodHound wiki.'
1714$Ref=@(
1715"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1716"https://www.youtube.com/watch?v=z8thoG7gPd0"
1717"https://adsecurity.org/?p=1729"
1718"http://www.harmj0y.net/blog/activedirectory/targeted-kerberoasting/"
1719"https://posts.specterops.io/a-red-teamers-guide-to-gpos-and-ous-f0d03976a31e"
1720"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
1721"https://github.com/GhostPack/Rubeus#s4u"
1722"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
1723"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
1724"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1725"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
1726)
1727}
1728#################################################################################
1729GenericWrite{
1730$Info='Generic Write access grants you the ability to write to any non-protected attribute on the target object, including "members" for a group, and "serviceprincipalnames" for a user'
1731$Abuse="GenericWrite to a group allows you to directly modify group membership of the group.
1732
1733There are at least two ways to execute this attack. The first and most obvious is by using the built-in net.exe binary in Windows (e.g.: net group 'Domain Admins' harmj0y /add /domain). See the opsec considerations tab for why this may be a bad idea. The second, and highly recommended method, is by using the Add-DomainGroupMember function in PowerView. This function is superior to using the net.exe binary in several ways. For instance, you can supply alternate credentials, instead of needing to run a process as or logon as the user with the AddMember privilege. Additionally, you have much safer execution options than you do with spawning net.exe (see the opsec tab).
1734
1735To abuse this privilege with PowerView's Add-DomainGroupMember, first import PowerView into your agent session or into a PowerShell instance at the console. You may need to authenticate to the Domain Controller as a member of DOMAIN ADMINS@DOMAIN.LOCAL if you are not running a process as a member. To do this in conjunction with Add-DomainGroupMember, first create a PSCredential object (these examples comes from the PowerView help documentation):
1736
1737`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1738`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1739
1740Then, use Add-DomainGroupMember, optionally specifying `$Cred if you are not already running a process as DOMAIN ADMINS@DOMAIN.LOCAL:
1741
1742Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential `$Cred
1743
1744Finally, verify that the user was successfully added to the group with PowerView's Get-DomainGroupMember:
1745
1746Get-DomainGroupMember -Identity 'Domain Admins'"
1747$Opsec='This depends on the target object and how to take advantage of this privilege. Opsec considerations for each abuse primitive are documented on the specific abuse edges and on the BloodHound wiki.'
1748$Ref=@(
1749"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1750"https://www.youtube.com/watch?v=z8thoG7gPd0"
1751"http://www.harmj0y.net/blog/activedirectory/targeted-kerberoasting/"
1752"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
1753"https://github.com/GhostPack/Rubeus#s4u"
1754"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
1755"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
1756"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1757"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
1758)
1759}
1760#################################################################################
1761WriteOwner{
1762#
1763$Info="Object owners retain the ability to modify object security descriptors, regardless of permissions on the object's DACL."
1764#
1765$Abuse="To change the ownership of the object, you may use the Set-DomainObjectOwner function in PowerView.
1766
1767You may need to authenticate to the Domain Controller as a member of DOMAIN ADMINS@DOMAIN.LOCAL if you are not running a process as a member. To do this in conjunction with Set-DomainObjectOwner, first create a PSCredential object (these examples comes from the PowerView help documentation):
1768
1769`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1770`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1771
1772Then, use Set-DomainObjectOwner, optionally specifying `$Cred if you are not already running a process as Target group:
1773
1774Set-DomainObjectOwner -Credential `$Cred -TargetIdentity testlab.local -OwnerIdentity harmj0y
1775
1776To abuse ownership of a domain object, you may grant yourself the DcSync privileges.
1777
1778You may need to authenticate to the Domain Controller as a member of the Target Group if you are not running a process as a member. To do this in conjunction with Add-DomainObjectAcl, first create a PSCredential object (these examples comes from the PowerView help documentation):
1779
1780`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1781`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1782
1783Then, use Add-DomainObjectAcl, optionally specifying `$Cred if you are not already running a process as the target Group:
1784
1785Add-DomainObjectAcl -Credential `$Cred -TargetIdentity testlab.local -Rights DCSync
1786
1787Once you have granted yourself this privilege, you may use the mimikatz dcsync function to dcsync the password of arbitrary principals on the domain
1788
1789sekurlsa::dcsync /domain:testlab.local /user:Administrator
1790
1791Cleanup can be done using the Remove-DomainObjectAcl function:
1792Remove-DomainObjectAcl -Credential `$Cred -TargetIdentity testlab.local -Rights DCSync
1793
1794Cleanup for the owner can be done by using Set-DomainObjectOwner once again"
1795#
1796$Opsec='This depends on the target object and how to take advantage of this privilege. Opsec considerations for each abuse primitive are documented on the specific abuse edges and on the BloodHound wiki.'
1797#
1798$Ref=@(
1799"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1800"http://www.selfadsi.org/deep-inside/ad-security-descriptors.htm"
1801"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
1802"https://github.com/GhostPack/Rubeus#s4u"
1803"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
1804"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
1805"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1806"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
1807)
1808}
1809#################################################################################
1810WriteDacl{
1811#
1812$Info="With write access to the target object's DACL, you can grant yourself any privilege you want on the object."
1813#
1814$Abuse="To abuse WriteDacl to a domain object, you may grant yourself the DcSync privileges.
1815
1816You may need to authenticate to the Domain Controller as a member of DOMAIN ADMINS@DOMAIN.LOCAL if you are not running a process as a member. To do this in conjunction with Add-DomainObjectAcl, first create a PSCredential object (these examples comes from the PowerView help documentation):
1817
1818`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1819`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1820
1821Then, use Add-DomainObjectAcl, optionally specifying `$Cred if you are not already running a process as DOMAIN ADMINS@DOMAIN.LOCAL:
1822
1823Add-DomainObjectAcl -Credential `$Cred -TargetIdentity testlab.local -Rights DCSync
1824
1825Once you have granted yourself this privilege, you may use the mimikatz dcsync function to dcsync the password of arbitrary principals on the domain
1826
1827sekurlsa::dcsync /domain:testlab.local /user:Administrator
1828
1829Cleanup can be done using the Remove-DomainObjectAcl function:
1830Remove-DomainObjectAcl -Credential `$Cred -TargetIdentity testlab.local -Rights DCSync"
1831#
1832$Opsec="When using the PowerView functions, keep in mind that PowerShell v5 introduced several security mechanisms that make it much easier for defenders to see what's going on with PowerShell in their network, such as script block logging and AMSI. You can bypass those security mechanisms by downgrading to PowerShell v2, which all PowerView functions support.
1833Modifying permissions on an object will generate 4670 and 4662 events on the domain controller that handled the request.
1834Additional opsec considerations depend on the target object and how to take advantage of this privilege. Opsec considerations for each abuse primitive are documented on the specific abuse edges and on the BloodHound wiki."
1835#
1836$Ref=@(
1837"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1838"https://www.youtube.com/watch?v=z8thoG7gPd0"
1839"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
1840"https://github.com/GhostPack/Rubeus#s4u"
1841"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
1842"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
1843"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1844"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
1845)
1846}
1847#################################################################################
1848AllExtendedRights{
1849#
1850$Info='Extended rights are special rights granted on objects which allow reading of privileged attributes, as well as performing special actions.'
1851$Abuse="The AllExtendedRights privilege grants DOMAIN ADMINS@DOMAIN.LOCAL both the DS-Replication-Get-Changes and DS-Replication-Get-Changes-All privileges, which combined allow a principal to replicate objects from the domain DOMAIN.LOCAL. This can be abused using the lsadump::dcsync command in mimikatz."
1852$Opsec="When using the PowerView functions, keep in mind that PowerShell v5 introduced several security mechanisms that make it much easier for defenders to see what's going on with PowerShell in their network, such as script block logging and AMSI. You can bypass those security mechanisms by downgrading to PowerShell v2, which all PowerView functions support."
1853$Ref=@(
1854"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1855"https://www.youtube.com/watch?v=z8thoG7gPd0"
1856)
1857}
1858#################################################################################
1859GpLink{
1860$Info='A linked GPO applies its settings to objects in the linked container.'
1861$Abuse='There is no abuse info related to this edge.'
1862$Opsec='There are no opsec considerations related to this edge.'
1863$Ref=@(
1864"https://wald0.com/?p=179"
1865"https://blog.cptjesus.com/posts/bloodhound15"
1866)
1867}
1868#################################################################################
1869Owns{
1870$Info="Object owners retain the ability to modify object security descriptors, regardless of permissions on the object's DACL"
1871$Abuse="To abuse ownership of a domain object, you may grant yourself the DcSync privileges.
1872
1873You may need to authenticate to the Domain Controller as a member of DOMAIN ADMINS@DOMAIN.LOCAL if you are not running a process as a member. To do this in conjunction with Add-DomainObjectAcl, first create a PSCredential object (these examples comes from the PowerView help documentation):
1874
1875`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1876`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1877
1878Then, use Add-DomainObjectAcl, optionally specifying `$Cred if you are not already running a process as DOMAIN ADMINS@DOMAIN.LOCAL:
1879
1880Add-DomainObjectAcl -Credential `$Cred -TargetIdentity TestGPO -Rights All
1881
1882With full control of a GPO, you may make modifications to that GPO which will then apply to the users and computers affected by the GPO. Select the target object you wish to push an evil policy down to, then use the gpedit GUI to modify the GPO, using an evil policy that allows item-level targeting, such as a new immediate scheduled task. Then wait at least 2 hours for the group policy client to pick up and execute the new evil policy. See the references tab for a more detailed write up on this abuse
1883
1884Cleanup can be done using the Remove-DomainObjectAcl function:
1885Remove-DomainObjectAcl -Credential `$Cred -TargetIdentity TestGPO -Rights All"
1886$Opsec="When using the PowerView functions, keep in mind that PowerShell v5 introduced several security mechanisms that make it much easier for defenders to see what's going on with PowerShell in their network, such as script block logging and AMSI. You can bypass those security mechanisms by downgrading to PowerShell v2, which all PowerView functions support.
1887Modifying permissions on an object will generate 4670 and 4662 events on the domain controller that handled the request.
1888Additional opsec considerations depend on the target object and how to take advantage of this privilege. Opsec considerations for each abuse primitive are documented on the specific abuse edges and on the BloodHound wiki."
1889$Ref=@(
1890"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1891"https://www.youtube.com/watch?v=z8thoG7gPd0"
1892"http://www.selfadsi.org/deep-inside/ad-security-descriptors.htm"
1893"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
1894"https://github.com/GhostPack/Rubeus#s4u"
1895"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
1896"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
1897"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
1898"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
1899)
1900}
1901#################################################################################
1902Contains{
1903$Info='GPOs linked to a container apply to all objects that are contained by the container.'
1904$Abuse="There is no abuse info related to this edge."
1905$Opsec='There are no opsec considerations related to this edge.'
1906$Ref=@(
1907"https://wald0.com/?p=179"
1908"https://blog.cptjesus.com/posts/bloodhound15"
1909)
1910}
1911#################################################################################
1912ReadLAPSPassword{
1913$Info='The local administrator password for a computer managed by LAPS is stored in the confidential LDAP attribute, “ms-mcs-AdmPwdâ€.'
1914$Abuse="To abuse this privilege with PowerView's Get-DomainObject, first import PowerView into your agent session or into a PowerShell instance at the console. You may need to authenticate to the Domain Controller as AZALEE_CASALE@DOMAIN.LOCAL if you are not running a process as that user. To do this in conjunction with Get-DomainObject, first create a PSCredential object (these examples comes from the PowerView help documentation):
1915
1916`$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
1917`$Cred = New-Object System.Management.Automation.PSCredential('TESTLABdfm.a', `$SecPassword)
1918
1919Then, use Get-DomainObject, optionally specifying `$Cred if you are not already running a process as AZALEE_CASALE@DOMAIN.LOCAL:
1920
1921Get-DomainObject windows1 -Credential `$Cred -Properties 'ms-mcs-AdmPwd',name"
1922$Opsec="Reading properties from LDAP is an extremely low risk operation."
1923$Ref=@(
1924"https://www.specterops.io/assets/resources/an_ace_up_the_sleeve.pdf"
1925"https://adsecurity.org/?p=3164"
1926)
1927}
1928#################################################################################
1929CanRDP{
1930$Info='Remote Desktop access allows you to enter an interactive session with the target computer. If authenticating as a low privilege user, a privilege escalation may allow you to gain high privileges on the system.
1931Note: This edge does not guarantee privileged execution.'
1932$Abuse="Abuse of this privilege will depend heavily on the type of access you have.
1933
1934# PlainText Credentials with Interactive Access
1935With plaintext credentials, the easiest way to exploit this privilege is using the built in Windows Remote Desktop Client (mstsc.exe). Open mstsc.exe and input the computer DC_1.DOMAIN.LOCAL. When prompted for credentials, input the credentials for EDELMIRA_LACY@DOMAIN.LOCAL to initiate the remote desktop connection.
1936
1937# Password Hash with Interactive Access
1938With a password hash, exploitation of this privilege will require local administrator privileges on a system, and the remote server must allow Restricted Admin Mode.
1939First, inject the NTLM credential for the user you're abusing into memory using mimikatz:
1940
1941sekurlsa::pth /user:dfm /domain:testlab.local /ntlm:<ntlm hash> /run:'mstsc.exe /restrictedadmin'
1942
1943This will open a new RDP window. Input the computer DC_1.DOMAIN.LOCAL to initiate the remote desktop connection. If the target server does not support Restricted Admin Mode, the session will fail.
1944
1945#Plaintext Credentials without Interactive Access
1946This method will require some method of proxying traffic into the network, such as the socks command in cobaltstrike, or direct internet connection to the target network, as well as the xfreerdp (suggested because of support of Network Level Authentication (NLA)) tool, which can be installed from the freerdp-x11 package. If using socks, ensure that proxychains is configured properly. Initiate the remote desktop connection with the following command:
1947
1948(proxychains) xfreerdp /u:dfm /d:testlab.local /v:<computer ip>
1949
1950xfreerdp will prompt you for a password, and then initiate the remote desktop connection.
1951
1952# Password Hash without Interactive Access
1953This method will require some method of proxying traffic into the network, such as the socks command in cobaltstrike, or direct internet connection to the target network, as well as the xfreerdp (suggested because of support of Network Level Authentication (NLA)) tool, which can be installed from the freerdp-x11 package. Additionally, the target computer must allow Restricted Admin Mode. If using socks, ensure that proxychains is configured properly. Initiate the remote desktop connection with the following command:
1954
1955(proxychains) xfreerdp /pth:<ntlm hash> /u:dfm /d:testlab.local /v:<computer ip>
1956
1957This will initiate the remote desktop connection, and will fail if Restricted Admin Mode is not enabled."
1958$Opsec="If the target computer is a workstation and a user is currently logged on, one of two things will happen. If the user you are abusing is the same user as the one logged on, you will effectively take over their session and kick the logged on user off, resulting in a message to the user. If the users are different, you will be prompted to kick the currently logged on user off the system and log on. If the target computer is a server, you will be able to initiate the connection without issue provided the user you are abusing is not currently logged in.
1959Remote desktop will create Logon and Logoff events with the access type RemoteInteractive."
1960$Ref=@(
1961"https://michael-eder.net/post/2018/native_rdp_pass_the_hash/"
1962"https://www.kali.org/penetration-testing/passing-hash-remote-desktop/"
1963)
1964}
1965#################################################################################
1966ExecuteDCOM{
1967$Info='Membership in the Distributed COM Users local group can allow code execution under certain conditions by instantiating a COM object on a remote machine and invoking its methods.'
1968$Abuse='The PowerShell script Invoke-DCOM implements lateral movement using a variety of different COM objects (ProgIds: MMC20.Application, ShellWindows, ShellBrowserWindow, ShellBrowserWindow, and ExcelDDE). LethalHTA implements lateral movement using the HTA COM object (ProgId: htafile).
1969
1970One can manually instantiate and manipulate COM objects on a remote machine using the following PowerShell code. If specifying a COM object by its CLSID:
1971
1972$ComputerName = CHERRY_FAGUNDES@DOMAIN.LOCAL # Remote computer
1973$clsid = “{fbae34e8-bf95-4da8-bf98-6c6e580aa348}†# GUID of the COM object
1974$Type = [Type]::GetTypeFromCLSID($clsid, $ComputerName)
1975$ComObject = [Activator]::CreateInstance($Type)
1976
1977If specifying a COM object by its ProgID:
1978
1979$ComputerName = CHERRY_FAGUNDES@DOMAIN.LOCAL # Remote computer
1980$ProgId = Ҡ# GUID of the COM object
1981$Type = [Type]::GetTypeFromProgID($ProgId, $ComputerName)
1982$ComObject = [Activator]::CreateInstance($Type)'
1983$Opsec='The artifacts generated when using DCOM vary depending on the specific COM object used.
1984
1985DCOM is built on top of the TCP/IP RPC protocol (TCP ports 135 + high ephemeral ports) and may leverage several different RPC interface UUIDs(outlined here). In order to use DCOM, one must be authenticated. Consequently, logon events and authentication-specific logs(Kerberos, NTLM, etc.) will be generated when using DCOM.
1986
1987Processes may be spawned as the user authenticating to the remote system, as a user already logged into the system, or may take advantage of an already spawned process.
1988
1989Many DCOM servers spawn under the process “svchost.exe -k DcomLaunch†and typically have a command line containing the string “ -Embedding†or are executing inside of the DLL hosting process “DllHost.exe /Processid:{}“ (where AppId is the AppId the COM object is registered to use). Certain COM services are implemented as service executables; consequently, service-related event logs may be generated.'
1990$Ref=@(
1991"https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/ "
1992"https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/"
1993"https://enigma0x3.net/2017/09/11/lateral-movement-using-excel-application-and-dcom/"
1994"https://enigma0x3.net/2017/11/16/lateral-movement-using-outlooks-createobject-method-and-dotnettojscript/"
1995"https://www.cybereason.com/blog/leveraging-excel-dde-for-lateral-movement-via-dcom"
1996"https://www.cybereason.com/blog/dcom-lateral-movement-techniques"
1997"https://bohops.com/2018/04/28/abusing-dcom-for-yet-another-lateral-movement-technique/"
1998"https://attack.mitre.org/wiki/Technique/T1175"
1999"https://github.com/rvrsh3ll/Misc-Powershell-Scripts/blob/master/Invoke-DCOM.ps1"
2000"https://codewhitesec.blogspot.com/2018/07/lethalhta.html"
2001"https://github.com/codewhitesec/LethalHTA/"
2002)
2003}
2004#################################################################################
2005AllowedToDelegate{
2006$Info ='The constrained delegation primitive allows a principal to authenticate as any user to specific services (found in the msds-AllowedToDelegateTo LDAP property in the source node tab) on the target computer. That is, a node with this privilege can impersonate any domain principal (including Domain Admins) to the specific service on the target host. One caveat- impersonated users can not be in the "Protected Users" security group or otherwise have delegation privileges revoked.
2007An issue exists in the constrained delegation where the service name (sname) of the resulting ticket is not a part of the protected ticket information, meaning that an attacker can modify the target service name to any service of their choice. For example, if msds-AllowedToDelegateTo is “HTTP/host.domain.comâ€, tickets can be modified for LDAP/HOST/etc. service names, resulting in complete server compromise, regardless of the specific service listed.'
2008$Abuse="Abusing this privilege can utilize Benjamin Delpy’s Kekeo project, proxying in traffic generated from the Impacket library, or using the Rubeus project's s4u abuse.
2009
2010In the following example, *victim* is the attacker-controlled account (i.e. the hash is known) that is configured for constrained delegation. That is, *victim* has the 'HTTP/PRIMARY.testlab.local' service principal name (SPN) set in its msds-AllowedToDelegateTo property. The command first requests a TGT for the *victim* user and executes the S4U2self/S4U2proxy process to impersonate the 'admin' user to the 'HTTP/PRIMARY.testlab.local' SPN. The alternative sname 'cifs' is substituted in to the final service ticket and the ticket is submitted to the current logon session. This grants the attacker the ability to access the file system of PRIMARY.testlab.local as the 'admin' user.
2011
2012Rubeus.exe s4u /user:victim /rc4:2b576acbe6bcfda7294d6bd18041b8fe /impersonateuser:admin /msdsspn:'HTTP/PRIMARY.testlab.local' /altservice:cifs /ptt"
2013$Opsec='As mentioned in the abuse info, in order to currently abuse this primitive the Rubeus C# assembly needs to be executed on some system with the ability to send/receive traffic in the domain. See the References for more information.'
2014$Ref=@(
2015"https://github.com/GhostPack/Rubeus#s4u"
2016"https://labs.mwrinfosecurity.com/blog/trust-years-to-earn-seconds-to-break/"
2017"http://www.harmj0y.net/blog/activedirectory/s4u2pwnage/"
2018"https://twitter.com/gentilkiwi/status/806643377278173185"
2019"https://www.coresecurity.com/blog/kerberos-delegation-spns-and-more"
2020"http://www.harmj0y.net/blog/redteaming/from-kekeo-to-rubeus/"
2021"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
2022)
2023}
2024
2025###########
2026AllowedTOAct{
2027$info='An attacker can use this account to execute a modified S4U2self/S4U2proxy abuse chain to impersonate any domain user to the target computer system and receive a valid service ticket "as" this user.
2028One caveat is that impersonated users can not be in the "Protected Users" security group or otherwise have delegation privileges revoked. Another caveat is that the principal added to the msDS-AllowedToActOnBehalfOfOtherIdentity DACL *must* have a service pricipal name (SPN) set in order to successfully abuse the S4U2self/S4U2proxy process. If an attacker does not currently control an account with a SPN set, an attacker can abuse the default domain MachineAccountQuota settings to add a computer account that the attacker controls via the Powermad project.'
2029$Abuse='Abusing this primitive is currently only possible through the Rubeus project.
2030To use this attack, the controlled account MUST have a service principal name set, along with access to either the plaintext or the RC4_HMAC hash of the account.
2031If the plaintext password is available, you can hash it to the RC4_HMAC version using Rubeus:
2032
2033Rubeus.exe hash /password:Summer2018!
2034
2035Use Rubeus *s4u* module to get a service ticket for the service name (sname) we want to "pretend" to be "admin" for. This ticket is injected (thanks to /ptt), and in this case grants us access to the file system of the TARGETCOMPUTER:
2036
2037Rubeus.exe s4u /user:AZALEE_CASALE@DOMAIN.LOCAL$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt'
2038$Opsec='To execute this attack, the Rubeus C# assembly needs to be executed on some system with the ability to send/receive traffic in the domain.'
2039$Ref=@(
2040"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
2041"https://github.com/GhostPack/Rubeus#s4u"
2042"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
2043"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
2044"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
2045"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
2046)
2047#########
2048}
2049AddAllowedTOAct{
2050$info='The ability to modify the msDS-AllowedToActOnBehalfOfOtherIdentity property allows an attacker to abuse resource-based constrained delegation to compromise the remote computer system. This property is a binary DACL that controls what security principals can pretend to be any domain user to the particular computer object.
2051If the msDS-AllowedToActOnBehalfOfOtherIdentity DACL is set to allow an attack-controller account, the attacker can use said account to execute a modified S4U2self/S4U2proxy abuse chain to impersonate any domain user to the target computer system and receive a valid service ticket "as" this user.
2052One caveat is that impersonated users can not be in the "Protected Users" security group or otherwise have delegation privileges revoked. Another caveat is that the principal added to the msDS-AllowedToActOnBehalfOfOtherIdentity DACL *must* have a service pricipal name (SPN) set in order to successfully abuse the S4U2self/S4U2proxy process. If an attacker does not currently control an account with a SPN set, an attacker can abuse the default domain MachineAccountQuota settings to add a computer account that the attacker controls via the Powermad project.'
2053$Abuse="Abusing this primitive is currently only possible through the Rubeus project.
2054First, if an attacker does not control an account with an SPN set, Kevin Robertson's Powermad project can be used to add a new attacker-controlled computer account:
2055
2056New-MachineAccount -MachineAccount attackersystem -Password `$(ConvertTo-SecureString 'Summer2018!' -AsPlainText -Force)
2057
2058PowerView can be used to then retrieve the security identifier (SID) of the newly created computer account:
2059
2060`$ComputerSid = Get-DomainComputer attackersystem -Properties objectsid | Select -Expand objectsid
2061
2062We now need to build a generic ACE with the attacker-added computer SID as the pricipal, and get the binary bytes for the new DACL/ACE:
2063
2064`$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList 'O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;`$(`$ComputerSid))'
2065`$SDBytes = New-Object byte[] (`$SD.BinaryLength)
2066`$SD.GetBinaryForm(`$SDBytes, 0)
2067
2068Next, we need to set this newly created security descriptor in the msDS-AllowedToActOnBehalfOfOtherIdentity field of the comptuer account we're taking over, again using PowerView in this case:
2069
2070`$RawBytes = Get-DomainComputer 'TARGETCOMPUTER' -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity
2071
2072We can then use Rubeus to hash the plaintext password into its RC4_HMAC form:
2073
2074Rubeus.exe hash /password:Summer2018!
2075
2076And finally we can use Rubeus' *s4u* module to get a service ticket for the service name (sname) we want to 'pretend' to be 'admin' for. This ticket is injected (thanks to /ptt), and in this case grants us access to the file system of the TARGETCOMPUTER:
2077
2078Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt"
2079$Opsec='To execute this attack, the Rubeus C# assembly needs to be executed on some system with the ability to send/receive traffic in the domain. Modification of the *msDS-AllowedToActOnBehalfOfOtherIdentity* property against the target also must occur, whether through PowerShell or another method. The property should be cleared (or reset to its original value) after attack execution in order to prevent easy detection.'
2080$Ref=@(
2081"https://eladshamir.com/2019/01/28/Wagging-the-Dog.html"
2082"https://github.com/GhostPack/Rubeus#s4u"
2083"https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff"
2084"http://www.harmj0y.net/blog/redteaming/another-word-on-delegation/"
2085"https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1"
2086"https://github.com/Kevin-Robertson/Powermad#new-machineaccount"
2087)
2088}
2089#########
2090 }
2091 if($Online){$ref|%{Start-Process $_}}
2092 else{Return [PSCustomObject]@{
2093 Edge = $type
2094 Info = $Info
2095 Abuse = $Abuse
2096 Opsec = $Opsec
2097 Ref = $Ref
2098 }}
2099 }
2100#End
2101
2102################################################ EdgeCreate
2103function New-BloodHoundEdge{
2104<#
2105.Synopsis
2106 BloodHound Edge - Create Edge
2107.DESCRIPTION
2108 Create Edges Between nodes
2109.EXAMPLE
2110 EdgeCreate User MemberOf Group ALBINA_BRASHEAR@DOMAIN.LOCAL ADMINISTRATORS@DOMAIN.LOCAL
2111#>
2112 [CmdletBinding()]
2113 [Alias('New-Edge','EdgeCreate')]
2114 Param(
2115 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$SourceType,
2116 [Parameter(Mandatory=1,Position=1,ValuefromPipeline=0)][EdgeType]$EdgeType,
2117 [Parameter(Mandatory=1,Position=2,ValuefromPipeline=0)][NodeType]$TargetType
2118 )
2119 DynamicParam{
2120 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
2121 # Prep DynNamelist
2122 $DynSourceList = @($Script:CypherDog."${SourceType}List")
2123 $DynTargetList = @($Script:CypherDog."${TargetType}List")
2124 # Prep DynP
2125 $DynSource = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 3 -Pipe 1 -PipeProp 1 -VSet $DynSourceList
2126 $DynTarget = DynP -Name 'To' -Type 'string[]' -Mandat 1 -Pos 4 -Pipe 0 -PipeProp 0 -VSet $DynTargetList
2127 $DynCypher = DynP -Name 'Cypher'-Type 'Switch' -Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
2128 # DynP to Dico
2129 $Dico.Add("Name" ,$DynSource)
2130 $Dico.Add("To" ,$DynTarget)
2131 $Dico.Add("Cypher",$DynCypher)
2132 # Return Dico
2133 Return $Dico
2134 }
2135 Begin{
2136 $Query = "MATCH (A:$SourceType) WHERE A.name = {SRC} MATCH (B:$TargetType) WHERE B.name = {TGT} MERGE (A)-[R:$EdgeType]->(B)"
2137 }
2138 Process{
2139 Foreach($SourceName in $DynSource.Value){
2140 Foreach($TargetName in $DynTarget.Value){
2141 $Param = @{
2142 SRC = "$SourceName"
2143 TGT = "$TargetName"
2144 }}
2145 if(-Not$DynCypher.IsSet){DogPost $Query $Param}
2146 }}
2147 End{if($DynCypher.IsSet){ClipThis $Query $Param}}
2148 }
2149#End
2150
2151################################################ EdgeRemove
2152function Remove-BloodHoundEdge{
2153<#
2154.Synopsis
2155 BloodHound Edge - Delete Edge
2156.DESCRIPTION
2157 Remove Edge between nodes
2158.EXAMPLE
2159 EdgeDelete User MemberOf Group ALBINA_BRASHEAR@DOMAIN.LOCAL ADMINISTRATORS@DOMAIN.LOCAL
2160#>
2161 [CmdletBinding(SupportsShouldProcess=1,ConfirmImpact='High')]
2162 [Alias('Remove-Edge','EdgeDelete')]
2163 Param(
2164 [Parameter(Mandatory=1,Position=0,ValuefromPipeline=0)][NodeType]$SourceType,
2165 [Parameter(Mandatory=1,Position=1,ValuefromPipeline=0)][EdgeType]$EdgeType,
2166 [Parameter(Mandatory=1,Position=2,ValuefromPipeline=0)][NodeType]$TargetType
2167 )
2168 DynamicParam{
2169 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
2170 # Prep DynNamelist
2171 $DynSourceList = @($Script:CypherDog."${SourceType}List")
2172 $DynTargetList = @($Script:CypherDog."${TargetType}List")
2173 # Prep DynP
2174 $DynSource = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 3 -Pipe 1 -PipeProp 1 -VSet $DynSourceList
2175 $DynTarget = DynP -Name 'To' -Type 'string[]' -Mandat 1 -Pos 4 -Pipe 0 -PipeProp 0 -VSet $DynTargetList
2176 $DynCypher = DynP -Name 'Cypher'-Type 'Switch' -Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
2177 # DynP to Dico
2178 $Dico.Add("Name" ,$DynSource)
2179 $Dico.Add("To" ,$DynTarget)
2180 $Dico.Add("Cypher",$DynCypher)
2181 # Return Dico
2182 Return $Dico
2183 }
2184 Begin{$Query = "MATCH (A:$SourceType) WHERE A.name = {SRC} MATCH (B:$TargetType) WHERE B.name = {TGT} MATCH (A)-[R:$EdgeType]->(B) DELETE R"}
2185 Process{
2186 Foreach($SourceName in $DynSource.Value){
2187 Foreach($TargetName in $DynTarget.Value){
2188 $Param = @{
2189 SRC = "$SourceName"
2190 TGT = "$TargetName"
2191 }}
2192 if(-Not$DynCypher.IsSet){DogPost $Query $Param}
2193 }}
2194 End{if($DynCypher.IsSet){ClipThis $Query $Param}}
2195 }
2196#End
2197
2198#endregion ################################################
2199
2200
2201###########################################################
2202#region ############################################## PATH
2203
2204# Get-BloodHoundPathShort
2205# Get-BloodHoundPathAny
2206# Get-BloodHoundPathCost
2207# Get-BloodHoundPathCheap
2208# Get-BloodHoundWald0IO
2209
2210################################################# PathShort
2211function Get-BloodHoundPathShort{
2212<#
2213.Synopsis
2214 BloodHound Path - Get Shortest
2215.DESCRIPTION
2216 Get BloodHound Shortest/AllShortest Path
2217.EXAMPLE
2218 Path user Group ALBINA_BRASHEAR@DOMAIN.LOCAL 'SCHEMA ADMINS@DOMAIN.LOCAL'
2219#>
2220 [Cmdletbinding()]
2221 [OutputType([BHEdge])]
2222 [Alias('Get-PathShort','Path')]
2223 Param(
2224 [Parameter(Mandatory=1,Position=0)][NodeType]$SourceType,
2225 [Parameter(Mandatory=1,Position=1)][NodeType]$TargetType
2226 )
2227 DynamicParam{
2228 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
2229 # Prep DynNamelist
2230 $DynSourceList = @($Script:CypherDog."${SourceType}List")
2231 $DynTargetList = @($Script:CypherDog."${TargetType}List")
2232 # Prep DynP
2233 $DynSource = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 2 -Pipe 1 -PipeProp 1 -VSet ($DynSourceList+'*')
2234 $DynTarget = DynP -Name 'To' -Type 'string[]' -Mandat 1 -Pos 3 -Pipe 0 -PipeProp 0 -VSet ($DynTargetList+'*')
2235 $DynEdge = DynP -Name 'Edge' -Type 'string[]' -Mandat 0 -Pos 4 -Pipe 0 -PipeProp 0 -VSet @('NoDef','NoACL','NoGPO','NoSpc')
2236 $DynExclude = DynP -Name 'Exclude'-Type 'EdgeType[]'-Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
2237 $DynInclude = DynP -Name 'Include'-Type 'EdgeType[]'-Mandat 0 -Pos 6 -Pipe 0 -PipeProp 0 -VSet $Null
2238 $DynBlackL = DynP -Name 'BlackL' -Type 'Switch' -Mandat 0 -Pos 7 -Pipe 0 -PipeProp 0 -VSet $Null
2239 $DynMax = DynP -Name 'MaxHop' -Type 'Int' -Mandat 0 -Pos 8 -Pipe 0 -PipeProp 0 -VSet $Null
2240 $DynAll = DynP -Name 'All' -Type 'Switch' -Mandat 0 -Pos 9 -Pipe 0 -PipeProp 0 -VSet $Null
2241 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 10 -Pipe 0 -PipeProp 0 -VSet $Null
2242 # DynP to Dico
2243 $Dico.Add("Name" ,$DynSource)
2244 $Dico.Add("To" ,$DynTarget)
2245 $Dico.Add("Edge" ,$DynEdge)
2246 $Dico.Add("Exclude",$DynExclude)
2247 $Dico.Add("Include",$DynInclude)
2248 $Dico.Add("MaxHop" ,$DynMax)
2249 $Dico.Add("BlackL" ,$DynBlackL)
2250 $Dico.Add("All" ,$DynAll)
2251 $Dico.Add("Cypher" ,$DynCypher)
2252 # Return Dico
2253 Return $Dico
2254 }
2255 Begin{
2256 # Path Type
2257 if($DynAll.IsSet){$PType = 'allShortestPaths'}
2258 else{$PType = 'shortestPath'}
2259 # EdgeString
2260 if(-Not$DynEdge.Value){$E = ':'+(GenEdgeStr -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2261 else{$E = ':'+(GenEdgeStr $DynEdge.Value -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2262 if($E -eq ':'){$E=$null}
2263 # Max Hop
2264 $M=$DynMax.Value
2265 # Blacklist
2266 If($DynBlackL.IsSet){$BL = " WHERE NONE(x in NODES(p) WHERE x:Blacklist)"}Else{$BL=$Null}
2267 }
2268 Process{foreach($SRC in $DynSource.Value){foreach($TGT in $DynTarget.Value){
2269 # Any Source - Any Target
2270 if($SRC -eq '*' -AND $TGT -eq '*'){
2271 #Write-Warning "Heavy Q - No Names Specified"
2272 $Query = "MATCH (A:$SourceType), (B:$TargetType), p=$PType((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2273 if(-Not$DynCypher.IsSet){DogPost $Query -Expand Data | ToPathObj}
2274 }
2275 # Any Source - Spec Target
2276 if($SRC -eq '*' -AND $TGT -ne '*'){
2277 $Query = "MATCH (A:$SourceType), (B:$TargetType {name: {TGT}}), p=$PType((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2278 $Param=@{TGT="$TGT"}
2279 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2280 }
2281 # Spec Source - Any Target
2282 if($SRC -ne '*' -AND $TGT -eq '*'){
2283 $Query = "MATCH (A:$SourceType {name: {SRC}}), (B:$TargetType), p=$PType((A)-[r$E*1..$M]->(B))$BL RETURN p"
2284 $Param=@{SRC="$SRC"}
2285 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2286 }
2287 # Spec Source - Spec Source
2288 if($SRC -ne '*' -AND $TGT -ne '*'){
2289 $Query = "MATCH (A:$SourceType {name: {SRC}}), (B:$TargetType {name: {TGT}}), p=$PType((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2290 $Param=@{
2291 SRC="$SRC"
2292 TGT="$TGT"
2293 }
2294 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2295 }}}}
2296 End{if($DynCypher.IsSet){clipThis $Query $Param}}
2297 }
2298#End
2299
2300################################################### PathAny
2301function Get-BloodHoundPathAny{
2302<#
2303.Synopsis
2304 BloodHound Path - Get Any
2305.DESCRIPTION
2306 Get 'Any' Path
2307.EXAMPLE
2308 PathAny user Group ALBINA_BRASHEAR@DOMAIN.LOCAL 'SCHEMA ADMINS@DOMAIN.LOCAL'
2309#>
2310 [Cmdletbinding()]
2311 [OutputType([BHEdge])]
2312 [Alias('Get-PathAny','PathAny')]
2313 Param(
2314 [Parameter(Mandatory=1,Position=0)][NodeType]$SourceType,
2315 [Parameter(Mandatory=1,Position=1)][NodeType]$TargetType
2316 )
2317 DynamicParam{
2318 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
2319 # Prep DynNamelist
2320 $DynSourceList = @($Script:CypherDog."${SourceType}List")
2321 $DynTargetList = @($Script:CypherDog."${TargetType}List")
2322 # Prep DynP
2323 $DynSource = DynP -Name 'Name' -Type 'String[]' -Mandat 1 -Pos 2 -Pipe 1 -PipeProp 1 -VSet ($DynSourceList+'*')
2324 $DynTarget = DynP -Name 'To' -Type 'string[]' -Mandat 1 -Pos 3 -Pipe 0 -PipeProp 0 -VSet ($DynTargetList+'*')
2325 $DynEdge = DynP -Name 'Edge' -Type 'string[]' -Mandat 0 -Pos 4 -Pipe 0 -PipeProp 0 -VSet @('NoDef','NoACL','NoGPO','NoSpc')
2326 $DynExclude= DynP -Name 'Exclude'-Type 'EdgeType[]'-Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet $Null
2327 $DynInclude= DynP -Name 'Include'-Type 'EdgeType[]'-Mandat 0 -Pos 6 -Pipe 0 -PipeProp 0 -VSet $Null
2328 $DynMax = DynP -Name 'MaxHop' -Type 'Int' -Mandat 0 -Pos 7 -Pipe 0 -PipeProp 0 -VSet $Null
2329 $DynBlackL = DynP -Name 'BlackL' -Type 'Switch' -Mandat 0 -Pos 8 -Pipe 0 -PipeProp 0 -VSet $Null
2330 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 9 -Pipe 0 -PipeProp 0 -VSet $Null
2331 # DynP to Dico
2332 $Dico.Add("Name" ,$DynSource)
2333 $Dico.Add("To" ,$DynTarget)
2334 $Dico.Add("Edge" ,$DynEdge)
2335 $Dico.Add("Exclude",$DynExclude)
2336 $Dico.Add("Include",$DynInclude)
2337 $Dico.Add("MaxHop" ,$DynMax)
2338 $Dico.Add("BlackL" ,$DynBlackL)
2339 $Dico.Add("Cypher" ,$DynCypher)
2340 # Return Dico
2341 Return $Dico
2342 }
2343 Begin{
2344 # EdgeString
2345 if(-Not$DynEdge.Value){$E = ':'+ (GenEdgeStr -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2346 else{$E = ':'+ (GenEdgeStr $DynEdge.Value -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2347 # Max Hop
2348 $M=$DynMax.Value
2349 # Blacklist
2350 If($DynBlackL.IsSet){$BL = " WHERE NONE(x in NODES(p) WHERE x:Blacklist)"}
2351 }
2352 Process{foreach($SRC in $DynSource.Value){foreach($TGT in $DynTarget.Value){
2353 # Any Source - Any Target
2354 if($SRC -eq '*' -AND $TGT -eq '*'){
2355 if(!$M){Write-Warning "Heavy Query - Setting MaxHop to 3";$M=3}
2356 $Query = "MATCH (A:$SourceType) MATCH (B:$TargetType) MATCH p=((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2357 if(-Not$DynCypher.IsSet){DogPost $Query -Expand Data | ToPathObj}
2358 }
2359 # Any Source - Spec Target
2360 if($SRC -eq '*' -AND $TGT -ne '*'){
2361 if(!$M){Write-Warning "Heavy Query - Setting MaxHop to 7"; $M=7}
2362 $Query = "MATCH (A:$SourceType) MATCH (B:$TargetType {name: {TGT}}) MATCH p=((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2363 $Param=@{TGT="$TGT"}
2364 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2365 }
2366 # Spec Source - Any Target
2367 if($SRC -ne '*' -AND $TGT -eq '*'){
2368 if(!$M){Write-Warning "Heavy Query - Setting MaxHop to 7";$M=7}
2369 $Query = "MATCH (A:$SourceType {name: {SRC}}) MATCH (B:$TargetType) MATCH p=((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2370 $Param=@{SRC="$SRC"}
2371 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2372 }
2373 # Spec Source - Spec Source
2374 if($SRC -ne '*' -AND $TGT -ne '*'){
2375 if(!$M){Write-Warning "Heavy Query - Setting MaxHop to 9";$M=9}
2376 $Query = "MATCH (A:$SourceType {name: {SRC}}), (B:$TargetType {name: {TGT}}), p=((A)-[r$E*1..$M]->(B))$BL RETURN DISTINCT(p)"
2377 $Param=@{
2378 SRC="$SRC"
2379 TGT="$TGT"
2380 }
2381 if(-Not$DynCypher.IsSet){DogPost $Query $Param -Expand Data | ToPathObj}
2382 }}}}
2383 End{if($DynCypher.IsSet){clipThis $Query $Param}}
2384 }
2385#End
2386
2387################################################## PathCost
2388function Get-BloodHoundPathCost{
2389<#
2390.Synopsis
2391 BloodHound Path - Get Cost
2392.DESCRIPTION
2393 Get BloodHound Path Cost
2394.EXAMPLE
2395 path user group GARY_CATANIA@SUB.DOMAIN.LOCAL 'RDS ENDPOINT SERVERS@DOMAIN.LOCAL' -all | pathcost
2396#>
2397 [Alias('PathCost')]
2398 Param(
2399 [Parameter(Mandatory=1,ValueFromPipeline=1)][BHEdge]$Path
2400 )
2401 Begin{$Collection=@()}
2402 Process{$Collection += $Path}
2403 End{
2404 $Result = $Collection | group Id | %{
2405 [PSCustomObject]@{
2406 ID=$_.name
2407 Cost=($_.Group.Edge | Where {$_ -notmatch 'MemberOf'}).count
2408 Hop=$_.Count
2409 Path=$_.Group
2410 }}
2411 Return $Result | Sort Cost,Hop
2412 }}
2413#####End
2414
2415################################################# PathCheap
2416function Get-BloodHoundPathCheap{
2417<#
2418.Synopsis
2419 BloodHound Path - Get Cheapest
2420.DESCRIPTION
2421 Get BloodHound Cheapest Path
2422.EXAMPLE
2423 pathcheap user group GARY_CATANIA@SUB.DOMAIN.LOCAL 'RDS ENDPOINT SERVERS@DOMAIN.LOCAL'
2424#>
2425 [Cmdletbinding()]
2426 [OutputType([BHEdge])]
2427 [Alias('Get-PathCheap','PathCheap')]
2428 Param(
2429 [Parameter(Mandatory=1,Position=0)][NodeType]$SourceType,
2430 [Parameter(Mandatory=1,Position=1)][NodeType]$TargetType
2431 )
2432 DynamicParam{
2433 $Dico = New-Object Management.Automation.RuntimeDefinedParameterDictionary
2434 # Prep DynNamelist
2435 $DynSourceList = @($Script:CypherDog."${SourceType}List")
2436 $DynTargetList = @($Script:CypherDog."${TargetType}List")
2437 # Prep DynP
2438 $DynSource = DynP -Name 'Name' -Type 'String' -Mandat 1 -Pos 2 -Pipe 1 -PipeProp 1 -VSet ($DynSourceList)
2439 $DynTarget = DynP -Name 'To' -Type 'string' -Mandat 1 -Pos 3 -Pipe 0 -PipeProp 0 -VSet ($DynTargetList)
2440 $DynEdge = DynP -Name 'Edge' -Type 'string[]' -Mandat 0 -Pos 5 -Pipe 0 -PipeProp 0 -VSet @('NoDef','NoACL','NoGPO','NoSpc')
2441 $DynExclude= DynP -Name 'Exclude'-Type 'EdgeType[]'-Mandat 0 -Pos 6 -Pipe 0 -PipeProp 0 -VSet $Null
2442 $DynInclude= DynP -Name 'Include'-Type 'EdgeType[]'-Mandat 0 -Pos 7 -Pipe 0 -PipeProp 0 -VSet $Null
2443 $DynBlackL = DynP -Name 'BlackL' -Type 'Switch' -Mandat 0 -Pos 8 -Pipe 0 -PipeProp 0 -VSet $Null
2444 $DynExpand = DynP -Name 'Expand' -Type 'Int' -Mandat 0 -Pos 9 -Pipe 0 -PipeProp 0 -VSet @(1..9)
2445 $DynCypher = DynP -Name 'Cypher' -Type 'Switch' -Mandat 0 -Pos 10 -Pipe 0 -PipeProp 0 -VSet $Null
2446 $DynLimit = DynP -Name 'Limit' -Type 'Int' -Mandat 0 -Pos 11 -Pipe 0 -PipeProp 0 -VSet $Null
2447 # DynP to Dico
2448 $Dico.Add("Name" ,$DynSource)
2449 $Dico.Add("To" ,$DynTarget)
2450 $Dico.Add("Edge" ,$DynEdge)
2451 $Dico.Add("Exclude",$DynExclude)
2452 $Dico.Add("Include",$DynInclude)
2453 $Dico.Add('Expand', $DynExpand)
2454 $Dico.Add("BlackL" ,$DynBlackL)
2455 $Dico.Add("Cypher" ,$DynCypher)
2456 $Dico.Add("Limit" ,$DynLimit)
2457 # Return Dico
2458 Return $Dico
2459 }
2460 Begin{
2461 # EdgeString
2462 if(-Not$DynEdge.Value){$E = ':'+ (GenEdgeStr -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2463 else{$E = ':'+ (GenEdgeStr $DynEdge.Value -Exclude $DynExclude.Value -Include $DynInclude.Value)}
2464 # Blacklist
2465 if($DynBlackL.IsSet){$BL = " WHERE NONE(x in NODES(p) WHERE x:Blacklist)"}Else{$BL=$Null}
2466 if($DynLimit.Value){$L=$DynLimit.Value}else{$L=1}
2467 }
2468 Process{
2469 # Get length Cheapest
2470 $Q = "MATCH (S:$SourceType {name: '$($DynSource.Value)'}), (T:$TargetType {name: '$($DynTarget.Value)'}), p=shortestPath((S)-[r$E*1..]->(T))$BL RETURN LENGTH(p)"
2471 try{$Max = (DogPost $Q -Expand data)[0]}catch{}
2472 # if expand
2473 if($Max){$Max += $DynExpand.value
2474 # Query Cheapest all path max length
2475 $Q = "MATCH (S:$SourceType {name: '$($DynSource.Value)'}),
2476(T:$TargetType {name: '$($DynTarget.Value)'}),
2477p=((S)-[r$E*1..$Max]->(T))$BL
2478WITH p,
2479LENGTH(FILTER(x IN EXTRACT(r in RELATIONSHIPS(p)|TYPE(r)) WHERE x <>'MemberOf')) as Cost
2480RETURN p
2481ORDER BY Cost
2482LIMIT $L"
2483 if(-Not$DynCypher.IsSet){DogPost $Q -Expand Data | TopathObj}
2484 }}
2485 End{if($DynCypher.IsSet){clipThis $Q $Param}}
2486 }
2487#End
2488
2489
2490
2491################################################# Wald0IO
2492Class Wald0IO{
2493 [String]$Domain
2494 [String]$Type
2495 [Int]$Total
2496 [String]$Direction
2497 $Target
2498 [Int]$Count
2499 [Float]$Percent
2500 [Float]$Hop
2501 [Float]$Touch
2502 [Float]$Cost
2503 }
2504
2505
2506function Get-BloodHoundWald0IO{
2507<#
2508.Synopsis
2509 BloodHound Path - Get Wald0 Index
2510.DESCRIPTION
2511 Calculate wald0 Index for specified Group
2512.EXAMPLE
2513 Node Group ADMINISTRATORS@DOMAIN.LOCAL | Wlad0IO
2514#>
2515 [CmdletBinding()]
2516 [Alias('Get-Wald0IO','Wald0IO')]
2517 Param(
2518 [Parameter(ValueFromPipeline=1,ValueFromPipelineByPropertyName=1,Mandatory=0,Position=0)][Alias('TargetGroup')][String]$Name,
2519 [ValidateSet('Inbound','Outbound')]
2520 [Parameter(Mandatory=0,Position=1)][String]$Direction,
2521 [ValidateSet('User','Computer')]
2522 [Parameter(Mandatory=0,Position=2)][String]$Type,
2523 [ValidateSet('NoDef','NoACL','NoGPO','NoSpc')]
2524 [Parameter(Mandatory=0,Position=3)][String[]]$Edge,
2525 [Parameter(Mandatory=0)][EdgeType[]]$Exclude,
2526 [Parameter(Mandatory=0)][EdgeType[]]$Include,
2527 [Parameter(Mandatory=0)][Switch]$DomainOnly,
2528 [Parameter(Mandatory=0)][Switch]$BlackL,
2529 [Parameter(Mandatory=0)][Switch]$Cypher
2530 )
2531 Begin{
2532 # EdgeString
2533 if($Edge.count -eq 0){$E = ':'+(GenEdgeStr -Exclude $Exclude -Include $Include)}
2534 else{$E = ':'+(GenEdgeStr $Edge -Exclude $Exclude -Include $Include)}
2535 if($E -eq ':'){$E=$null}
2536 # BlackL
2537 If($BlackL){$BL = " WHERE NONE(x in NODES(p) WHERE x:Blacklist)"}
2538 }
2539 Process{
2540 $Splat = @{}
2541 $PSBoundParameters.Keys -notmatch "Name|Direction|Type" | %{$Splat.add($_,$PSBoundParameters.$_)}
2542 if(-Not$Type -AND -Not$Direction){
2543 Get-Wald0IO -Name $Name -Direction Inbound -Type User @Splat
2544 Get-Wald0IO -Name $Name -Direction Outbound -Type Computer @Splat
2545 }
2546 elseif($Direction -AND -Not$Type){
2547 Get-Wald0IO -Name $Name -Direction $Direction -Type User @Splat
2548 Get-Wald0IO -Name $Name -Direction $Direction -Type Computer @Splat
2549 }
2550 elseif($Type -AND -Not$Direction){
2551 Get-Wald0IO -Name $Name -Direction Inbound -Type $Type @Splat
2552 Get-Wald0IO -Name $Name -Direction Outbound -Type $type @Splat
2553 }
2554 ## ACTION ##
2555 elseif($Type -AND $Direction){
2556 # TargetFolder
2557 $TargetGroup = $Name
2558 # Split Domain
2559 $TargetDomain = $Name.split('@')[1]
2560 # Dom
2561 if($DomainOnly){
2562 $Dom=" {domain: '$TargetDomain'}"
2563 $Scope = "$TargetDomain"
2564 }
2565 else{$Scope='*'}
2566 # Query Parts Inbound|Outbound
2567 if($Direction -eq 'Inbound'){
2568 $Q1 = "p = shortestPath((x:$Type$Dom)-[r$E*1..]->(g:Group {name:'$TargetGroup'}))$BL"
2569 $Q2 = "MATCH (tx:$type$Dom), $Q1"
2570 }
2571 if($Direction -eq 'Outbound'){
2572 $Q1 = "p = shortestPath((g:Group {name:'$TargetGroup'})-[r$E*1..]->(x:$type$Dom))$BL"
2573 $Q2 = "MATCH (tx:$type$Dom), $Q1"
2574 }
2575 # Full Cypher Query
2576 $Wald0IO = "$Q2
2577WITH
2578g.name as G,
2579COUNT(DISTINCT(tx)) as TX,
2580COUNT(DISTINCT(x)) as X,
2581ROUND(100*AVG(LENGTH(RELATIONSHIPS(p))))/100 as H,
2582ROUND(100*AVG(LENGTH(FILTER(z IN EXTRACT(r IN RELATIONSHIPS(p)|TYPE(r)) WHERE z<>'MemberOf'))))/100 AS C,
2583ROUND(100*AVG(LENGTH(FILTER(y IN EXTRACT(n IN NODES(p)|LABELS(n)[0]) WHERE y='Computer'))))/100 AS T
2584WITH G,TX,X,H,C,T,
2585ROUND(100*(100.0*X/TX))/100 as P
2586RETURN {
2587TotalCount: TX,
2588PathCount: X,
2589Percent: P,
2590HopAvg: H,
2591CostAvg: C,
2592TouchAvg: T
2593} AS Wald0IndexIO"
2594 # If Cypher > Set Clipboard
2595 if($Cypher){ClipThis "MATCH $Q1 RETURN p";Return}
2596 # Else Return Object
2597 else{
2598 # Call
2599 $Data = DogPost $Wald0IO -x data | Select -Expand Syncroot | select PathCount,TotalCount,Percent,HopAvg,CostAvg,TouchAvg
2600 Return [Wald0IO]@{
2601 Domain = $Scope
2602 Type = $Type
2603 Total = $Data.TotalCount
2604 Direction = $Direction
2605 Target = $TargetGroup
2606 Count = $Data.PathCount
2607 Percent = $Data.Percent
2608 Hop = $Data.HopAvg
2609 Touch = $Data.TouchAvg
2610 Cost = $Data.CostAvg
2611 }}}}
2612 End{}###########
2613 }
2614#End
2615
2616
2617function Measure-BloodhoundWald0IOAvg{
2618<#
2619.Synopsis
2620 BloodHound Path - Get Wald0 Index - AVG
2621.DESCRIPTION
2622 Calculate Average Wald0 Index for specified Groups
2623.EXAMPLE
2624 Node Group ADMINISTRATORS@DOMAIN.LOCAL | Wlad0IO | wald0IOAvg
2625#>
2626 [Alias('Wald0IOAvg')]
2627 Param(
2628 [Parameter(Mandatory=1,Position=0,ValueFromPipeline=1)][Wald0IO[]]$Wald0IO
2629 )
2630 Begin{[Collections.ArrayList]$Collect=@()}
2631 Process{foreach($Obj in $Wald0IO){
2632 $Null=$Collect.add($Obj)
2633 }}
2634 End{
2635 foreach($Dom in ($Collect.Domain|Sort -unique)){
2636 foreach($Dir in 'Inbound','Outbound'){
2637 $x = $Collect | where Domain -eq $Dom |Where Direction -eq $Dir
2638 if($X){
2639 [Wald0IO]@{
2640 Domain = $Dom
2641 Type = $x.Type[0]
2642 Total = $x.Total[0]
2643 Direction = $Dir
2644 Count = [Math]::Round(($x | Measure-Object -Property Count -average).Average,2)
2645 Percent = [Math]::Round(($x | Measure-Object -Property Percent -average).Average,2)
2646 Hop = [Math]::Round(($x | Measure-Object -Property Hop -average).Average,2)
2647 Touch = [Math]::Round(($x | Measure-Object -Property Touch -average).Average,2)
2648 Cost = [Math]::Round(($x | Measure-Object -Property Cost -average).Average,2)
2649 Target = '+'
2650 }}}}}}
2651#####################End
2652
2653
2654#endregion ################################################
2655
2656
2657###########################################################
2658###################################################### INIT
2659$ASCII
2660CacheNode
2661
2662###########################################################
2663####################################################### EOF