├── BH21 ├── BH4_SharpHound_Cheat.pdf ├── BH4_SharpHound_Cheat_Dark.pdf ├── BHW21_Slides_v1.pdf ├── Cypher101.cql ├── DogWhisperer4.pdf └── readme.md ├── Preparation ├── BH_Install.pdf └── ReadMe.md ├── README.md ├── SampleData ├── ReadMe.md └── graph.db.practice.zip └── Workshop ├── HandsOnBloodHound.pdf ├── Invoke-Cypher.ps1 └── ReadMe.md /BH21/BH4_SharpHound_Cheat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/BH21/BH4_SharpHound_Cheat.pdf -------------------------------------------------------------------------------- /BH21/BH4_SharpHound_Cheat_Dark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/BH21/BH4_SharpHound_Cheat_Dark.pdf -------------------------------------------------------------------------------- /BH21/BHW21_Slides_v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/BH21/BHW21_Slides_v1.pdf -------------------------------------------------------------------------------- /BH21/Cypher101.cql: -------------------------------------------------------------------------------- 1 | // ### CYPHER SYNTAX 101 ### 2 | 3 | ///////////////////////////////// 4 | 5 | // ## NODES ## 6 | 7 | // Node by Type 8 | MATCH (x:User) RETURN x 9 | 10 | // Node by Name 11 | MATCH (x:User {name:'KRBTGT@CONTOSO.LOCAL'}) RETURN x 12 | 13 | // Node by Props 14 | MATCH (x:User {enabled:True}) RETURN x 15 | MATCH (x:User {int:123}) RETURN x 16 | MATCH (x:User {enabled:True}) RETURN x 17 | 18 | // Multiple props 19 | MATCH (x:User {int:123,enabled:True}) RETURN x 20 | MATCH (x:User {enabled:True,owned:True}) RETURN x 21 | 22 | 23 | // # RETURN # 24 | 25 | // Return node 26 | MATCH (x:User) RETURN x 27 | 28 | // Return specific prop 29 | MATCH (x:User) RETURN x.name 30 | 31 | // multiple values 32 | MATCH (x:User) RETURN x.name,x.objectid 33 | 34 | // multiple props as obj << :) 35 | MATCH (x:User) RETURN {Name:x.name,ID:x.objectid} 36 | 37 | // Return node count 38 | MATCH (x:User) RETURN COUNT(x) 39 | 40 | // Return node labels 41 | MATCH (x:User) RETURN LABELS(x) 42 | MATCH (x:User) RETURN LABELS(x)[1] 43 | MATCH (x:User) RETURN {Name:x.name,Type:LABELS(x)[1]} 44 | 45 | // Order 46 | MATCH (x:User) RETURN x.name ORDER BY x.name 47 | // Limit 48 | MATCH (x:User) RETURN x.name LIMIT 2 49 | MATCH (x:User) RETURN x.name LIMIT 2 50 | 51 | // # WHERE (WHERE NOT)# 52 | 53 | // Where: property=value 54 | MATCH (x:User) WHERE x.name='BOB@LAB.TEST' RETURN x 55 | MATCH (x:User) WHERE x.name<>'BOB@LAB.TEST' RETURN x 56 | MATCH (x:User) WHERE NOT x.name='BOB@LAB.TEST' RETURN x 57 | MATCH (x:User) WHERE x.number=123 RETURN x 58 | MATCH (x:User) WHERE NOT x.enabled=false RETURN x 59 | MATCH (x:User) WHERE x.enabled RETURN x 60 | 61 | // COMPARAISON OPERATORS 62 | 63 | // equals to : = 64 | // not equal to : <> 65 | // less than : < 66 | // less or equal : <= 67 | // greater : > 68 | // greater or equal : >= 69 | // null : IS NULL 70 | // not null : IS NOT NULL 71 | // prefix* : STARTS WITH 72 | // suffix* : ENS WITH 73 | // inclusion* : CONTAINS 74 | // regex* : =~ 75 | 76 | // * = string specific 77 | 78 | MATCH (x:User) WHERE x.name =~ "(?i).*AdMin.*" RETURN x 79 | 80 | // Where: Operators 81 | MATCH (x:User) WHERE x.objectid ENDS WITH '-500' RETURN x 82 | MATCH (x:User) WHERE x.objectid =~ '^S.*-500$' RETURN x 83 | 84 | MATCH (x:User) WHERE x.lastlogon<=0 RETURN x 85 | MATCH (x:User) WHERE x.lastlogon < (datetime().epochseconds - (90 * 86400)) RETURN x // older than 90 days 86 | MATCH (x:User) WHERE NOT x.lastlogon IN [0,-1] AND NOT x.lastlogon > (datetime().epochseconds - (90 * 86400)) RETURN x.name 87 | 88 | 89 | // Where: Operator/Map Combo 90 | MATCH (x:User {enabled:True}) WHERE x.lastlogon<=0 AND x.objectid ENDS WITH '-500' RETURN x 91 | 92 | 93 | // Where: Array... 94 | // contains something (not empty) 95 | MATCH (x:User) 96 | WHERE SIZE(x.serviceprincipalnames)>0 97 | RETURN x 98 | // contains specific value 99 | MATCH (x:User) 100 | WHERE 'kadmin/changepw' IN x.serviceprincipalnames 101 | RETURN x 102 | // element matches 103 | MATCH (x:User) 104 | WHERE [mytemp IN x.serviceprincipalnames WHERE mytemp CONTAINS 'SQL'] 105 | RETURN x 106 | 107 | 108 | // Where: Property exists 109 | MATCH (x:User) 110 | WHERE NOT EXISTS(x.description) 111 | RETURN x 112 | 113 | 114 | 115 | // Where: path condition 116 | MATCH (x:User) 117 | WHERE (x)-[:AdminTo]->(:Computer{name:'DC1@TEST.LAB'}) 118 | RETURN x 119 | 120 | 121 | //////////////////////////////////// 122 | 123 | // ## PATH ## 124 | 125 | // Shortest path: Any edges / no max hop 126 | MATCH (x:User) 127 | MATCH (y:Group {name:'DOMAIN ADMINS@COFFEE.CUP'}) 128 | MATCH p=shortestPath((x)-[*1..]->(y)) 129 | RETURN p 130 | 131 | 132 | // Any Path : Single edge / single hop 133 | MATCH (x:User) 134 | MATCH (y:Group {name:'DOMAIN ADMINS@COFFEE.CUP'}) 135 | MATCH p=(x)-[:MemberOf*1]->(y) 136 | RETURN p 137 | 138 | 139 | // All Shortest : multiple edges / max 4 hops 140 | MATCH (x:User) 141 | MATCH (y:Group {name:'DOMAIN ADMINS@COFFEE.CUP'}) 142 | MATCH p=shortestPaths((x)-[:MemberOf|:AdminTo|:HasSession*1..4]->(y)) 143 | RETURN p 144 | 145 | 146 | // Return count of paths 147 | MATCH (x:User) 148 | MATCH (y:Group {name:'DOMAIN ADMINS@COFFEE.CUP'}) 149 | MATCH p=shortestPath((x)-[*1..]->(y)) 150 | RETURN COUNT(p) 151 | 152 | 153 | // Return path relationships 154 | MATCH (x:User) 155 | MATCH (y:Group {name:'DOMAIN ADMINS@COFFEE.CUP'}) 156 | MATCH p=shortestPath((x)-[*1..]->(y)) 157 | WITH p, [r in RELATIONSHIPS(p)| TYPE(r)] as type 158 | RETURN {rels:type} 159 | 160 | 161 | // Return custom objects 162 | MATCH (x:User) 163 | MATCH (y:Group {highvalue:True}) 164 | MATCH p=shortestPath((x)-[*1..]->(y)) 165 | RETURN {Length:LENGTH(p),Source:x.name,Target:y.name} 166 | 167 | 168 | // Compact style (same as above) 169 | MATCH p=shortestPath((x:User)-[*1..]->(y:Group {highvalue:True})) 170 | RETURN {Length:LENGTH(p),Source:x.name,Target:y.name} 171 | 172 | 173 | // Edge by edge property 174 | MATCH p=(x:User)-[{isacl:True}]->(y:Computer) RETURN p 175 | 176 | 177 | // Path where path 178 | MATCH p=(x:Computer)-[:HasSession]->(y:User) 179 | WHERE (y)-[:MemberOf|AdminTo*1..]->(x) 180 | RETURN p 181 | 182 | 183 | // Multiple return / optional path 184 | MATCH p1=(:User)-[:MemberOf*1..]->(:Group)-[{isacl:True}]->(:GPO) 185 | OPTIONAL MATCH p2=(:User)-[{isacl:True}]->(:GPO) 186 | RETURN p1,p2 187 | 188 | 189 | // Chained edges 190 | MATCH p=()-[:Contains*1..]->(:OU) RETURN p 191 | MATCH p=()-[:GpLink]->()-[:Contains*1..]->(:OU) RETURN p 192 | MATCH p=()-[{isacl:True}]->()-[:GpLink]->()-[:Contains*1..]->(:OU) RETURN p 193 | MATCH p=()-[:MemberOf*1..]->()-[{isacl:True}]->()-[:GpLink]->()-[:Contains*1..]->(:OU) RETURN p 194 | 195 | 196 | // Filter on path nodes collection 197 | MATCH (dc:Computer)-[:MemberOf]->(g:Group) 198 | WHERE g.objectid ENDS WITH '-516' 199 | WITH COLLECT(dc) AS dcgroup 200 | MATCH (x:Computer) WHERE NOT x IN dcgroup 201 | RETURN x.name 202 | 203 | 204 | /////////////////////////////////////////// 205 | 206 | // ## Create / Modify / Delete ## 207 | 208 | // ## NODES 209 | 210 | // # Modify: single prop 211 | MATCH (x:User {name:'TEST@TEST.LAB'}) SET x.enabled = True 212 | // # Modify: mutiple props (mutate) < Safe 213 | MATCH (x:User {name:'TEST@TEST.LAB'}) 214 | SET += {enabled=True,Highvalue=False} 215 | // # Modify: mutiple props (force) 216 | MATCH (x:User {name:'TEST@TEST.LAB'}) 217 | SET = {enabled=True} 218 | 219 | // Create 220 | MERGE (x:User {name:'Test'}) 221 | // Delete 222 | MATCH (x:User {name:'Test'}) DETACH DELETE x 223 | 224 | 225 | // Create: Advanced 226 | MERGE (x:Base {x.objectid:'ThisIsATest'}) 227 | SET x:User, x.name='Test' 228 | SET += { 229 | bool = True 230 | int = 123 231 | string = 'abc' 232 | array = ['abc','def','ghi'] 233 | } 234 | RETURN x 235 | 236 | // Remove: Node property 237 | MATCH (x:User {name:'Test'}) REMOVE x.string 238 | // Remove Node Label 239 | MATCH (x:User {name:'Test'}) REMOVE x:User 240 | 241 | 242 | 243 | /////////////// 244 | 245 | MATCH (x:Computer {name:'PCI-SERVER-001.CONTOSO.LOCAL'}) RETURN x 246 | MATCH (x:Computer) WHERE x.name = 'PCI-SERVER-001.CONTOSO.LOCAL' RETURN x 247 | 248 | MATCH (x:User) x.name STARTS WITH 'SQL' 249 | MATCH (x:User) xname =~ '^SQL.*' RETURN x 250 | 251 | MATCH (x:Group) WHERE x.name CONTAINS 'ADMIN' RETURN x 252 | 253 | 254 | MATCH (x:User {name:'DHOHNSTEIN@CONTOSO.LOCAL'}) 255 | MATCH (y:Group {name:'DOMAIN ADMINS@CONTOSO.LOCAL'}) 256 | MATCH path = shortestPath(x)-[*1..]->(y) RETURN path 257 | 258 | MATCH path = shortestPath(x:User {name:'DHOHNSTEIN@CONTOSO.LOCAL'})-[*1..]->(y:Group {name:'DOMAIN ADMINS@CONTOSO.LOCAL'}) RETURN path 259 | 260 | MATCH (x:User {name:'ACHILLES@CONTOSO.LOCAL'}) 261 | MATCH (y:Group {highvalue:True}) 262 | MATCH path = shortestPath(x)-[*1..]->(y) RETURN path 263 | 264 | 265 | MATCH (x:User {name:'JEFFMCJUNKIN@CONTOSO.LOCAL'}) 266 | MATCH (y:Computer) WHERE y.operatingsystem CONTAINS '10' 267 | MATCH path = shortestPath(x)-[*1..]->(y) RETURN path 268 | 269 | MATCH (x:Group) 270 | 271 | 272 | 273 | //================== Bits of stuffs 274 | 275 | MATCH (u:User {name:'JEFFMCJUNKIN@CONTOSO.LOCAL'}) 276 | MATCH (g:Group {name:'DOMAIN ADMINS@CONTOSO.LOCAL'}) 277 | MATCH p=shortestPath((u)-[*1..]->(g)) 278 | RETURN p 279 | 280 | 281 | MATCH p=shortestPath((u:User {name:'JEFFMCJUNKIN@CONTOSO.LOCAL'})-[:MemberOf|HasSession|AdminTo*1..5]->(g:Group {name:'DOMAIN ADMINS@CONTOSO.LOCAL'}) ) 282 | RETURN p 283 | 284 | 285 | 286 | MATCH (x:Computer {name:'PCI-SERVER-001.CONTOSO.LOCAL'}) RETURN x 287 | MATCH (x:User) WHERE x.name STARTS WITH 'SQL' RETURN x 288 | MATCH (x:Group) WHERE x.name CONTAINS 'ADMIN' RETURN x 289 | 290 | MATCH (x:Group) 291 | MATCH (y:Computer) WHERE NOT y.name<>'DESKTOP-4AMBQF0.CONTOSO.LOCAL' 292 | MATCH p=shortestPath((x)-[*1..]->(y)) 293 | RETURN p 294 | 295 | 296 | 297 | MATCH p=(n:Domain)-->(m:Domain) RETURN p 298 | 299 | MATCH p=(n:Domain)-->(m:Domain) RETURN p 300 | 301 | 302 | MATCH p=(n:Group)<-[:MemberOf]-(m) WHERE n.objectid =~ "(?i)S-1-5-.*-512" RETURN p 303 | 304 | MATCH (n1)-[:MemberOf|GetChanges*1..]->(u:Domain {name: "CONTOSO.LOCAL"}) 305 | WITH n1,u 306 | MATCH (n1)-[:MemberOf|GetChangesAll*1..]->(u) 307 | WITH n1,u 308 | MATCH p = (n1)-[:MemberOf|GetChanges|GetChangesAll*1..]->(u) 309 | RETURN p 310 | 311 | 312 | MATCH (n:User) WHERE n.hasspn=true RETURN n 313 | MATCH (n:User) WHERE n.hasspn RETURN n 314 | MATCH (n:User {hasspn:true}) RETURN n 315 | 316 | 317 | 318 | MATCH (u:User {owned:True}) 319 | MATCH (g:Group {highvalue:True}) 320 | MATCH p=shortestPath((u)-[*1..]->(g)) 321 | RETURN p 322 | 323 | 324 | 325 | MERGE (x:User {name:'JD'}) SET x.objectid='S-234567' RETURN x 326 | 327 | MATCH (x:User {name:'SadProcessor'}) SET x.objectid=12345 RETRUn x 328 | 329 | MATCH (x:User {name:'SadProcessor'}) 330 | MATCH (y:User {name:'JD'}) 331 | MERGE (x)-[:IsOnline]->(y) 332 | 333 | MATCH (n:User {objectid: "S-12345"}) 334 | MATCH (m:User {objectid: "S-234567"}) 335 | MATCH p=allShortestPaths((n)-[r:IsOnline*1..]->(m)) 336 | RETURN p 337 | 338 | MATCH (n:User {objectid: "S-12345"}) 339 | MATCH (m:User {objectid: "S-234567"}) 340 | DETACH DELETE n,m 341 | 342 | 343 | 344 | 345 | MATCH (x:User {hasspn:True}) RETURN COUNT(x) 346 | 347 | MATCH (x:User) WHERE x.hasspn=True 348 | WITH COUNT(x) AS KerbUserCount 349 | RETURN KerbUserCount 350 | 351 | MATCH p=shortestPath((x:User)-[:MemberOf|HasSession|AdminTo|AllExtendedRights|AddMember|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|ReadLAPSPassword|ReadGMSAPassword*1..]->(y:Group {highvalue:True})) RETURN COUNT(x) 352 | 353 | MATCH (x:Computer) WHERE x.operatingsystem CONTAINS '2016' RETURN x 354 | 355 | 356 | 357 | MATCH (n1)-[:MemberOf|GetChanges*1..]->(u:Domain {name: "BSC.LOCAL"}) 358 | WITH n1,u MATCH (n1)-[:MemberOf|GetChangesAll*1..]->(u) 359 | WITH n1,u 360 | MATCH p = (n1)-[:MemberOf|GetChanges|GetChangesAll*1..]->(u) RETURN p 361 | 362 | 363 | MATCH p=shortestPath((x:User)-[:MemberOf|GetChanges*1..]->(y:Domain {name: 'DomainGoesHere'})) 364 | WHERE (x)-[:MemberOf|GetChangesAll*1..]->(y) 365 | RETURN p 366 | 367 | 368 | 369 | 370 | // ----------------------------------------------------- Freestyle 371 | 372 | // Matching node per property 373 | MATCH (pc:Computer {operatingsytem:'Windows Server 2016 Standard'}) RETURN pc 374 | MATCH (pc:Computer) WHERE pc.operatingsystem='value' RETURN pc 375 | // use other comp operators 376 | 377 | // Enabled user accounts 378 | MATCH (u:User) WHERE u.enabled=True RETURN u 379 | MATCH (u:User) WHERE u.enabled RETURN u 380 | // NOT enabled 381 | MATCH (u:User) WHERE NOT u.enabled = True RETURN u 382 | MATCH (u:User) WHERE NOT u.enabled = True RETURN u 383 | MATCH (u:User) WHERE u.enabled = False RETURN u 384 | MATCH (u:User) WHERE u.enabled <> True RETURN u 385 | 386 | // enabled and match Alice 387 | MATCH (u:User {name:'ALICE@COFFEE.CUP',enabled:True}) RETURN u 388 | 389 | MATCH (u:User) 390 | WHERE u.name CONTAINS 'ALICE' AND u.enabled 391 | RETURN u 392 | 393 | // PATH QUERIES 394 | 395 | // Map Domain Trusts 396 | MATCH p=(:Domain)-[:TrustedBy*1]->() RETURN p 397 | 398 | // Map Domain/OU struct 399 | MATCH p=(:Domain)-[:Contains*1..]->(:OU) RETURN p 400 | 401 | // GPO to container map 402 | MATCH p=(:GPO)-[:Contains|GpLink*1..]->(:OU) RETURN p 403 | MATCH p=(:GPO)-[:Contains|GpLink*1..]->(:Computer) RETURN p 404 | 405 | // User Can RDP? 406 | MATCH p=shortestpath((u:User)-[*1..]->(c:Computer)) RETURN p 407 | 408 | 409 | 410 | 411 | // 412 | 413 | MATCH (x:Computer {name: "DC02.COFFEE.CUP"}) 414 | MATCH (y:Group {name: "DOMAIN ADMINS@COFFEE.CUP"}) 415 | MATCH p=shortestPath((x)-[r*1..]->(y)) 416 | RETURN p 417 | 418 | // -------------------------------------------------------- 419 | // All DA 420 | MATCH p=(x)-[:MemberOf*1..]->(y:Group) WHERE y.objectid =~ "(?i)S-1-5-.*-512" RETURN p 421 | 422 | // Shortest Path to DA 423 | MATCH p=shortestPath((x)-[*1..]->(y:Group {name:"DOMAIN ADMINS@COFFEE.CUP"})) WHERE NOT x=y RETURN p 424 | 425 | // DCSync 426 | MATCH (n1:User)-[:MemberOf|GetChanges*1..]->(u:Domain {name: "COFFEE.CUP"}) 427 | WITH n1,u 428 | MATCH (n1)-[:MemberOf|GetChangesAll*1..]->(u) 429 | WITH n1,u 430 | MATCH p = (n1)-[:MemberOf|GetChanges|GetChangesAll*1..]->(u) RETURN p 431 | 432 | MATCH p=(u:User)-[:MemberOf|GetChanges*1..]->(d:Domain {name:'COFFEE.CUP'}) 433 | WHERE (u)-[:MemberOf|GetChangesAll*1..]->(d) 434 | RETURN p 435 | 436 | MATCH p=(u:User)-[:MemberOf|GetChanges*1..]->(d:Domain {name:'COFFEE.CUP'}) 437 | WHERE (u)-[:MemberOf|GetChangesAll*1..]->(d) 438 | WITH * 439 | MATCH p2=(u)-[:MemberOf|GetChangesAll*1..]->(d) 440 | RETURN p,p2 441 | 442 | // Foreign Group mbr 443 | MATCH p=(u:User)-[:MemberOf]->(g:Group) WHERE g.domain<>u.domain 444 | RETURN p 445 | 446 | // Domain Trust 447 | MATCH p=()-[:TrustedBy]->() RETURN p 448 | 449 | // Unconstrained Deleg 450 | MATCH p=shortestPath((n)-[*1..]->(m:Computer {unconstraineddelegation: true})) 451 | WHERE NOT n=m 452 | RETURN p 453 | 454 | // Kerberoastable users 455 | MATCH (x:User {domain:'COFFEE.CUP', hasspn:true}) RETURN x 456 | 457 | // Path From Owned to Computer 458 | MATCH p=shortestPath((x:User {owned:true})-[*1..]->(y:Computer)) RETURN p 459 | 460 | // 461 | match p=(g:Group {name:"DOMAIN USERS@COFFEE.CUP"})-[:CanRDP]->(c:Computer) 462 | where NOT c.operatingsystem CONTAINS 'Server' 463 | return p 464 | 465 | // DA logon to non-DC 466 | MATCH (c:Computer)-[:MemberOf]->(t:Group) 467 | WHERE NOT t.objectid ENDS WITH '-516' 468 | WITH c as NonDC 469 | MATCH p=(NonDC)-[:HasSession]->(n:User)-[:MemberOf]-> (g:Group) 470 | WHERE g.name ENDS WITH '-512' 471 | RETURN p 472 | 473 | // old OS 474 | MATCH (n:Computer) 475 | WHERE n.operatingsystem =~ '(?i).*\\b(2000|2003|2008|xp|vista|7|me)\\b.*' 476 | RETURN n 477 | 478 | 479 | 480 | // Create User 481 | MERGE (x:Base {objectid:'test-123'}) SET x:User, x.name='testuser123' RETURN x 482 | 483 | // Owned Users to HighValue Groups 484 | MATCH (u:User {owned:True}) 485 | MATCH (g:Group {highvalue:True}) 486 | MATCH p=shortestPath((u)-[*1..]->(g)) 487 | 488 | // Count of kerbies 489 | MATCH (u:User) WHERE u.hasspn 490 | RETURN COUNT(DISTINCT(u)) 491 | 492 | //Usr path to DA ALS/Default < count 493 | MATCH p=shortestPath((u:User)-[:MemberOf|HasSession|AdminTo|AllExtendedRights|AddMember|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|ReadLAPSPassword|ReadGMSAPassword*1]->(g:Group {name:'DOMAIN ADMINS@COFFE.CUP'})) 494 | WITH COUNT(p) AS pathcount 495 | RETURN pathcount 496 | 497 | // COunt Comp OS 2016 498 | MATCH (x:Computer) WHERE x.operatingsystem CONTAINS '2016' RETURN COUNT(x) 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | -------------------------------------------------------------------------------- /BH21/DogWhisperer4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/BH21/DogWhisperer4.pdf -------------------------------------------------------------------------------- /BH21/readme.md: -------------------------------------------------------------------------------- 1 | Updated files for BHW21 2 | -------------------------------------------------------------------------------- /Preparation/BH_Install.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/Preparation/BH_Install.pdf -------------------------------------------------------------------------------- /Preparation/ReadMe.md: -------------------------------------------------------------------------------- 1 | Please follow these instruction to prepare for training. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HandsOnBloodHound 2 | Material for the "Hands-On BloodHound" Workshop 3 | -------------------------------------------------------------------------------- /SampleData/ReadMe.md: -------------------------------------------------------------------------------- 1 | This is the BloodHound Sample Data for the Workshop exercises. 2 | -------------------------------------------------------------------------------- /SampleData/graph.db.practice.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/SampleData/graph.db.practice.zip -------------------------------------------------------------------------------- /Workshop/HandsOnBloodHound.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SadProcessor/HandsOnBloodHound/8cdbc4ffefc522b317724f673d334dc70d37d645/Workshop/HandsOnBloodHound.pdf -------------------------------------------------------------------------------- /Workshop/Invoke-Cypher.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Invoke Cypher 4 | .DESCRIPTION 5 | Post Cypher Query to BloodHound REST API 6 | .EXAMPLE 7 | Cypher "MATCH (x:User) RETURN x" 8 | .EXAMPLE 9 | $Query="MATCH (n:User) RETURN n" 10 | Invoke-Cypher $Query -Expand $Null 11 | #> 12 | function Invoke-Cypher{ 13 | [CmdletBinding()] 14 | [Alias('Cypher')] 15 | Param( 16 | [Parameter(Mandatory=1)][string]$Query, 17 | [Parameter(Mandatory=0)][Hashtable]$Params, 18 | [Parameter(Mandatory=0)][Alias('x')][String[]]$Expand=@('data','data') 19 | ) 20 | # Uri 21 | $Uri = "http://localhost:7474/db/data/cypher" 22 | # Header 23 | $Header=@{'Accept'='application/json; charset=UTF-8';'Content-Type'='application/json'} 24 | # Body 25 | if($Params){$Body = @{params=$Params; query=$Query}|Convertto-Json} 26 | else{$Body = @{query=$Query}|Convertto-Json} 27 | # Call 28 | Write-Verbose "[$(Get-date -f h:m:s)] $Query" 29 | $Reply = Try{Invoke-RestMethod -Uri $Uri -Method Post -Headers $Header -Body $Body -verbose:$false}Catch{$Oops = $Error[0].ErrorDetails.Message} 30 | # Format obj 31 | if($Oops){Write-Warning "$((ConvertFrom-Json $Oops).message)";Return} 32 | if($Expand){$Expand | %{$Reply = $Reply.$_}} 33 | # Output Reply 34 | if($Reply){Return $Reply} 35 | } 36 | #End -------------------------------------------------------------------------------- /Workshop/ReadMe.md: -------------------------------------------------------------------------------- 1 | This is the material for the workshop. 2 | --------------------------------------------------------------------------------