1 of 71

2 of 71

BloodHound and the Adversary Resilience Methodology

3 of 71

HELLO!

I am Andy Robbins

I work at SpecterOps

You can find me at @_wald0

3

4 of 71

HELLO!

I am Rohan Vazarkar

I work at SpecterOps

You can find me at @CptJesus

4

5 of 71

Defenders think in lists. Attackers think in graphs. As long as this is true, attackers win.

John Lambert

Distinguished Engineer, Microsoft

d

5

6 of 71

What’s New in BloodHound 2.1

7 of 71

Resource Based Constrained Delegation

  • Based on work by Elad Shamir (@elad_shamir) and Will Schroeder (@harmj0y)
  • Super Awesome!!!
  • Allows a takeover of a computer object if you have write permissions to and control of a user with an SPN set
  • msds-AllowedToActOnBehalfOfOtherIdentity
  • By default, users can create 10(!) computer objects

7

8 of 71

8

9 of 71

UI Improvements

  • Database Warmup Button to speed up queries
  • Optimized Queries
  • Faster Data Upload (Yay)
  • Better Query Debug Mode
  • Lots of bug fixes
  • Updated Help Text

9

10 of 71

10

11 of 71

Collector Improvements

  • ACL Logic Sucks Less
  • More reliable enumeration
  • Lots of bug fixes
  • More properties (Sensitive/NoReqPreAuth)
  • Bad EDR Evasion

11

12 of 71

The Better Basic Permissions Audit

13 of 71

BloodHound Greatly Simplifies:

  • Local admin rights auditing
  • Privileged user behavior auditing
  • AD object permissions auditing
  • Group Policy control auditing
  • Finding Kerberos configuration issues

13

14 of 71

BloodHound Greatly Simplifies:

  • Local admin rights auditing
  • Privileged user behavior auditing
  • AD object permissions auditing
  • Group Policy control auditing
  • Finding Kerberos configuration issues

14

15 of 71

15

16 of 71

16

17 of 71

17

18 of 71

18

19 of 71

Just tell me which non-DAs can control any DA and how

19

20 of 71

MATCH p1 = (daPrincipal)-[:MemberOf*1..]->(daGroup:Group {name:'DOMAIN ADMINS@DOMAIN.COM'})

WITH p1,daPrincipal,daGroup

OPTIONAL MATCH p2 = (l)-[{isacl:true}]->(daPrincipal)

WHERE NOT l = daGroup AND NOT l = daPrincipal

OPTIONAL MATCH p3 = (m)-[:MemberOf*1..]->(g:Group)-[{isacl:true}]->(daPrincipal)

WHERE NOT m = daPrincipal AND NOT m = daGroup AND NOT g = daGroup AND NOT (m)-[:MemberOf*1..]->(daGroup)

RETURN p1,p2,p3

*Slides available afterwards

20

21 of 71

21

22 of 71

BloodHound Greatly Simplifies:

  • Local admin rights auditing
  • Privileged user behavior auditing
  • AD object permissions auditing
  • Group Policy control auditing
  • Finding Kerberos configuration issues

22

23 of 71

Group Policy Control Audit at a Glance

  • Find each domain admin user and each computer they use
  • Find those objects from step 1 in the OU tree
  • Enumerate the Group Policy links to those OUs, sites, domains containing objects from step 1
  • Work out whether each GPO actually applies to the object
  • Audit the permissions on each GPO

23

24 of 71

24

25 of 71

25

26 of 71

26

27 of 71

27

28 of 71

28

29 of 71

29

30 of 71

30

31 of 71

Of course this can all be scripted

31

32 of 71

Or….

32

33 of 71

Just tell me which non-DAs can control any GPO that applies to any DA and how

33

34 of 71

MATCH (g3:Group {name:'DOMAIN ADMINS@DOMAIN.COM'})

OPTIONAL MATCH p1 = (g1:GPO)-[:GpLink {enforced:false}]->(container)-[:Contains*1..]->(u1:User)-[:MemberOf*1..]->(g3)

WHERE NONE (x in NODES(p1) WHERE x.blocksinheritance = true AND x:OU AND NOT (g1)-->(x))

OPTIONAL MATCH p2 = (g2:GPO)-[:GpLink {enforced:true}]->(container)-[:Contains*1..]->(u2:User)-[:MemberOf*1..]->(g3)

RETURN p1,p2

34

35 of 71

35

36 of 71

MATCH (g3:Group {name:'DOMAIN ADMINS@DOMAIN.COM'})

OPTIONAL MATCH p1 = (g1:GPO)-[:GpLink {enforced:false}]->(container)-[:Contains*1..]->(u1:User)-[:MemberOf*1..]->(g3)

WHERE NONE (x in NODES(p1) WHERE x.blocksinheritance = true AND x:OU AND NOT (g1)-->(x))

WITH p1,g1,g3,u1

OPTIONAL MATCH p2 = (g2:GPO)-[:GpLink {enforced:true}]->(container)-[:Contains*1..]->(u2:User)-[:MemberOf*1..]->(g3)

WITH p1,p2,g1,g2,g3,COLLECT(u1) + COLLECT(u2) as u

OPTIONAL MATCH p3 = (l)-[{isacl:true}]->(g1)

WHERE NOT l = g3

OPTIONAL MATCH p4 = (m)-[:MemberOf*1..]->(g4:Group)-[{isacl:true}]->(g1)

WHERE NOT (m)-[:MemberOf*1..]->(g3) AND NOT m = g3

OPTIONAL MATCH p5 = (n)-[{isacl:true}]->(g2)

WHERE NOT n = g3

OPTIONAL MATCH p6 = (o)-[:MemberOf*1..]->(g5:Group)-[{isacl:true}]->(g2)

WHERE NOT o IN u AND NOT o = g3 AND NOT g5 = g3

RETURN p1,p2,p3,p4,p5,p6

36

37 of 71

37

38 of 71

Finding Systemic Issues

39 of 71

Finding Systemic Issues

  • Least privilege violations
  • Cross-domain attack paths
  • Attack paths to “DA”

39

40 of 71

Finding Systemic Issues

  • Least privilege violations
  • Cross-domain attack paths
  • Attack paths to “DA”

40

41 of 71

Least Privilege Violations: Two Perspectives

Inbound permissions

Outbound permissions

41

42 of 71

Least Privilege Violations: Two Perspectives

Inbound permissions

  • Too many admins
  • Too many controllers

Outbound permissions

42

43 of 71

Least Privilege Violations: Two Perspectives

Inbound permissions

  • Too many admins
  • Too many controllers

Outbound permissions

43

44 of 71

MATCH (c:Computer {domain:'DOMAIN.COM'})

OPTIONAL MATCH (n1:User)-[:AdminTo]->(c)

OPTIONAL MATCH (n2:User)-[:MemberOf*1..]->(:Group)-[:AdminTo]->(c)

WITH COLLECT(n1) + COLLECT(n2) as tempVar,c

UNWIND tempVar as Admins

RETURN c.name,COUNT(DISTINCT(Admins)) as Admins

ORDER BY Admins DESC

44

45 of 71

45

46 of 71

Least Privilege Violations: Two Perspectives

Inbound permissions

Outbound permissions

  • Stale admin rights
  • Stale RDP rights
  • Stale object control

46

47 of 71

Least Privilege Violations: Two Perspectives

Inbound permissions

Outbound permissions

  • Stale admin rights
  • Stale RDP rights
  • Stale object control

47

48 of 71

https://dirkjanm.io/abusing-exchange-one-api-call-away-from-domain-admin/

48

49 of 71

49

50 of 71

50

51 of 71

51

52 of 71

What is this going to break?

52

53 of 71

53

54 of 71

54

55 of 71

https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=5136

55

56 of 71

56

57 of 71

Result

Total events after 14 months: 0

We removed the offending ACEs

Nothing broke!

5 months before PrivExchange hit the headlines

57

58 of 71

Finding Systemic Issues

  • Least privilege violations
  • Cross-domain attack paths
  • Attack paths to “DA”

58

59 of 71

59

60 of 71

MATCH (c:Computer {domain:'DOMAIN.COM'})

OPTIONAL MATCH (n1:User)-[:AdminTo]->(c)

WHERE NOT n1.domain = c.domain

OPTIONAL MATCH (n2:User)-[:MemberOf*1..]->(:Group)-[:AdminTo]->(c)

WHERE NOT n2.domain = c.domain

WITH COLLECT(n1) + COLLECT(n2) as tempVar,c

UNWIND tempVar as foreignAdmins

RETURN c.name,COUNT(DISTINCT(foreignAdmins)) as foreignAdmins

ORDER BY foreignAdmins DESC

60

61 of 71

61

62 of 71

62

63 of 71

Introducing:�BloodHound Analytics

64 of 71

bloodhoundanalytics.pbix

  • POC charting in a Power BI workbook
  • Several built-in queries and visualizations
  • Upcoming blog post showing how to expand this
  • GPLv3

  • On Github after this talk

64

65 of 71

65

66 of 71

66

67 of 71

67

68 of 71

68

69 of 71

bloodhoundanalytics.py

  • QA checks (data completeness)
  • Basic statistics regarding nodes and edges
  • Critical asset checks
  • Low hanging fruit
  • Cross-domain attacks
  • GPLv3

  • On Github after this talk

69

70 of 71

Attackers DEFENDERS think in graphs

You can find us at:

@SpecterOps

@_wald0

@CptJesus

Join the BloodHound Slack:

https://bloodhoundgang.herokuapp.com

70

71 of 71

CREDITS

Special thanks to all the people who made and released these awesome resources for free:

  • Presentation template by SlidesCarnival
  • Photographs by Unsplash

71