[SHEHZADE//ABDULLAH ANSARI]
Red Team Consultant @ Google | MS in Cybersecurity @ NYU
● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORGo v1.0.0
Offline Active Directory database (ntds.dit) parser and BloodHound graph reconstructor in pure Go. Extracts directory objects, group memberships, domain trusts, and binary DACLs from dead-disk ESE database pages and registry hives.
$
git clone https://shehzade.io/repos/ntds-to-bloodhound.git
Copy
Source ZIP (246.9 KB)
Prebuilt Binaries
276 lines · 9.5 KB
| 1 | package ad |
| 2 | |
| 3 | import "github.com/shehzade/ntds-to-bloodhound/pkg/acl" |
| 4 | |
| 5 | type MemberReference struct { |
| 6 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 7 | ObjectType string `json:"ObjectType"` |
| 8 | } |
| 9 | |
| 10 | type TypedPrincipal struct { |
| 11 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 12 | ObjectType string `json:"ObjectType"` |
| 13 | } |
| 14 | |
| 15 | type SessionCollection struct { |
| 16 | Collected bool `json:"Collected"` |
| 17 | FailureReason *string `json:"FailureReason"` |
| 18 | Results []interface{} `json:"Results"` |
| 19 | } |
| 20 | |
| 21 | func EmptySessionCollection() SessionCollection { |
| 22 | return SessionCollection{ |
| 23 | Collected: false, |
| 24 | FailureReason: nil, |
| 25 | Results: make([]interface{}, 0), |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | type DomainTrust struct { |
| 30 | TargetDomainName string |
| 31 | TargetDomainSid string |
| 32 | IsTransitive bool |
| 33 | TrustDirection int |
| 34 | TrustType int |
| 35 | SidFilteringEnabled bool |
| 36 | } |
| 37 | |
| 38 | type DomainTrustV4 struct { |
| 39 | TargetDomainName string `json:"TargetDomainName"` |
| 40 | TargetDomainSid string `json:"TargetDomainSid"` |
| 41 | IsTransitive bool `json:"IsTransitive"` |
| 42 | TrustDirection int `json:"TrustDirection"` |
| 43 | TrustType int `json:"TrustType"` |
| 44 | SidFilteringEnabled bool `json:"SidFilteringEnabled"` |
| 45 | } |
| 46 | |
| 47 | type DomainTrustV5 struct { |
| 48 | TargetDomainName string `json:"TargetDomainName"` |
| 49 | TargetDomainSid string `json:"TargetDomainSid"` |
| 50 | IsTransitive bool `json:"IsTransitive"` |
| 51 | TrustDirection string `json:"TrustDirection"` |
| 52 | TrustType string `json:"TrustType"` |
| 53 | SidFilteringEnabled bool `json:"SidFilteringEnabled"` |
| 54 | } |
| 55 | |
| 56 | func (t DomainTrust) DirectionString() string { |
| 57 | switch t.TrustDirection { |
| 58 | case 0: |
| 59 | return "Disabled" |
| 60 | case 1: |
| 61 | return "Inbound" |
| 62 | case 2: |
| 63 | return "Outbound" |
| 64 | case 3: |
| 65 | return "Bidirectional" |
| 66 | default: |
| 67 | return "Disabled" |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | func (t DomainTrust) TypeString() string { |
| 72 | switch t.TrustType { |
| 73 | case 0: |
| 74 | return "ParentChild" |
| 75 | case 1: |
| 76 | return "CrossLink" |
| 77 | case 2: |
| 78 | return "Forest" |
| 79 | case 3: |
| 80 | return "External" |
| 81 | default: |
| 82 | return "Unknown" |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | func (t DomainTrust) ToV4() DomainTrustV4 { |
| 87 | return DomainTrustV4{ |
| 88 | TargetDomainName: t.TargetDomainName, |
| 89 | TargetDomainSid: t.TargetDomainSid, |
| 90 | IsTransitive: t.IsTransitive, |
| 91 | TrustDirection: t.TrustDirection, |
| 92 | TrustType: t.TrustType, |
| 93 | SidFilteringEnabled: t.SidFilteringEnabled, |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | func (t DomainTrust) ToV5() DomainTrustV5 { |
| 98 | return DomainTrustV5{ |
| 99 | TargetDomainName: t.TargetDomainName, |
| 100 | TargetDomainSid: t.TargetDomainSid, |
| 101 | IsTransitive: t.IsTransitive, |
| 102 | TrustDirection: t.DirectionString(), |
| 103 | TrustType: t.TypeString(), |
| 104 | SidFilteringEnabled: t.SidFilteringEnabled, |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | type UserObject struct { |
| 109 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 110 | PrimaryGroupSID string `json:"PrimaryGroupSID,omitempty"` |
| 111 | AllowedToDelegate []TypedPrincipal `json:"AllowedToDelegate"` |
| 112 | SPNTargets []interface{} `json:"SPNTargets"` |
| 113 | HasSIDHistory []TypedPrincipal `json:"HasSIDHistory"` |
| 114 | Aces []acl.Ace `json:"Aces"` |
| 115 | IsACLProtected bool `json:"IsACLProtected"` |
| 116 | IsDeleted bool `json:"IsDeleted"` |
| 117 | Properties map[string]interface{} `json:"Properties"` |
| 118 | DNT uint64 `json:"-"` |
| 119 | PDNT uint64 `json:"-"` |
| 120 | NTHash string `json:"-"` |
| 121 | LMHash string `json:"-"` |
| 122 | RawDelegateTargets []string `json:"-"` |
| 123 | RawSIDHistory []string `json:"-"` |
| 124 | } |
| 125 | |
| 126 | type GroupObject struct { |
| 127 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 128 | Members []MemberReference `json:"Members"` |
| 129 | Aces []acl.Ace `json:"Aces"` |
| 130 | IsACLProtected bool `json:"IsACLProtected"` |
| 131 | IsDeleted bool `json:"IsDeleted"` |
| 132 | Properties map[string]interface{} `json:"Properties"` |
| 133 | DNT uint64 `json:"-"` |
| 134 | PDNT uint64 `json:"-"` |
| 135 | Synthetic bool `json:"-"` |
| 136 | } |
| 137 | |
| 138 | type ComputerObject struct { |
| 139 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 140 | PrimaryGroupSID string `json:"PrimaryGroupSID,omitempty"` |
| 141 | AllowedToDelegate []TypedPrincipal `json:"AllowedToDelegate"` |
| 142 | AllowedToAct []TypedPrincipal `json:"AllowedToAct"` |
| 143 | HasSIDHistory []TypedPrincipal `json:"HasSIDHistory"` |
| 144 | DumpSMSAPassword []TypedPrincipal `json:"DumpSMSAPassword"` |
| 145 | Sessions SessionCollection `json:"Sessions"` |
| 146 | PrivilegedSessions SessionCollection `json:"PrivilegedSessions"` |
| 147 | RegistrySessions SessionCollection `json:"RegistrySessions"` |
| 148 | LocalGroups []interface{} `json:"LocalGroups"` |
| 149 | UserRights []interface{} `json:"UserRights"` |
| 150 | Aces []acl.Ace `json:"Aces"` |
| 151 | IsACLProtected bool `json:"IsACLProtected"` |
| 152 | IsDeleted bool `json:"IsDeleted"` |
| 153 | IsDC bool `json:"IsDC"` |
| 154 | Status *string `json:"Status"` |
| 155 | Properties map[string]interface{} `json:"Properties"` |
| 156 | NTHash string `json:"-"` |
| 157 | LMHash string `json:"-"` |
| 158 | DNT uint64 `json:"-"` |
| 159 | PDNT uint64 `json:"-"` |
| 160 | RawDelegateTargets []string `json:"-"` |
| 161 | RawAllowedToAct []string `json:"-"` |
| 162 | RawSIDHistory []string `json:"-"` |
| 163 | } |
| 164 | |
| 165 | type DomainObject struct { |
| 166 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 167 | ChildObjects []TypedPrincipal `json:"ChildObjects"` |
| 168 | Trusts []DomainTrust `json:"Trusts"` |
| 169 | Links []interface{} `json:"Links"` |
| 170 | Aces []acl.Ace `json:"Aces"` |
| 171 | IsACLProtected bool `json:"IsACLProtected"` |
| 172 | IsDeleted bool `json:"IsDeleted"` |
| 173 | Properties map[string]interface{} `json:"Properties"` |
| 174 | DNT uint64 `json:"-"` |
| 175 | } |
| 176 | |
| 177 | type DomainObjectV4 struct { |
| 178 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 179 | ChildObjects []TypedPrincipal `json:"ChildObjects"` |
| 180 | Trusts []DomainTrustV4 `json:"Trusts"` |
| 181 | Links []interface{} `json:"Links"` |
| 182 | Aces []acl.Ace `json:"Aces"` |
| 183 | IsACLProtected bool `json:"IsACLProtected"` |
| 184 | IsDeleted bool `json:"IsDeleted"` |
| 185 | Properties map[string]interface{} `json:"Properties"` |
| 186 | } |
| 187 | |
| 188 | type DomainObjectV5 struct { |
| 189 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 190 | ChildObjects []TypedPrincipal `json:"ChildObjects"` |
| 191 | Trusts []DomainTrustV5 `json:"Trusts"` |
| 192 | Links []interface{} `json:"Links"` |
| 193 | Aces []acl.Ace `json:"Aces"` |
| 194 | IsACLProtected bool `json:"IsACLProtected"` |
| 195 | IsDeleted bool `json:"IsDeleted"` |
| 196 | Properties map[string]interface{} `json:"Properties"` |
| 197 | } |
| 198 | |
| 199 | func (d *DomainObject) ToV4() DomainObjectV4 { |
| 200 | trusts := make([]DomainTrustV4, len(d.Trusts)) |
| 201 | for i, t := range d.Trusts { |
| 202 | trusts[i] = t.ToV4() |
| 203 | } |
| 204 | return DomainObjectV4{ |
| 205 | ObjectIdentifier: d.ObjectIdentifier, |
| 206 | ChildObjects: d.ChildObjects, |
| 207 | Trusts: trusts, |
| 208 | Links: d.Links, |
| 209 | Aces: d.Aces, |
| 210 | IsACLProtected: d.IsACLProtected, |
| 211 | IsDeleted: d.IsDeleted, |
| 212 | Properties: d.Properties, |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | func (d *DomainObject) ToV5() DomainObjectV5 { |
| 217 | trusts := make([]DomainTrustV5, len(d.Trusts)) |
| 218 | for i, t := range d.Trusts { |
| 219 | trusts[i] = t.ToV5() |
| 220 | } |
| 221 | return DomainObjectV5{ |
| 222 | ObjectIdentifier: d.ObjectIdentifier, |
| 223 | ChildObjects: d.ChildObjects, |
| 224 | Trusts: trusts, |
| 225 | Links: d.Links, |
| 226 | Aces: d.Aces, |
| 227 | IsACLProtected: d.IsACLProtected, |
| 228 | IsDeleted: d.IsDeleted, |
| 229 | Properties: d.Properties, |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | type OUObject struct { |
| 234 | ObjectIdentifier string `json:"ObjectIdentifier"` |
| 235 | ChildObjects []TypedPrincipal `json:"ChildObjects"` |
| 236 | Links []interface{} `json:"Links"` |
| 237 | Aces []acl.Ace `json:"Aces"` |
| 238 | IsACLProtected bool `json:"IsACLProtected"` |
| 239 | IsDeleted bool `json:"IsDeleted"` |
| 240 | Properties map[string]interface{} `json:"Properties"` |
| 241 | DNT uint64 `json:"-"` |
| 242 | PDNT uint64 `json:"-"` |
| 243 | } |
| 244 | |
| 245 | type ADSnapshot struct { |
| 246 | DomainName string |
| 247 | DomainSID string |
| 248 | Users []*UserObject |
| 249 | Groups []*GroupObject |
| 250 | Computers []*ComputerObject |
| 251 | Domains []*DomainObject |
| 252 | OUs []*OUObject |
| 253 | |
| 254 | UsersBySID map[string]*UserObject |
| 255 | GroupsBySID map[string]*GroupObject |
| 256 | ComputersBySID map[string]*ComputerObject |
| 257 | OUsByGUID map[string]*OUObject |
| 258 | ObjectsByDNT map[uint64]TypedPrincipal |
| 259 | PDNTByDNT map[uint64]uint64 |
| 260 | } |
| 261 | |
| 262 | func NewADSnapshot() *ADSnapshot { |
| 263 | return &ADSnapshot{ |
| 264 | Users: make([]*UserObject, 0), |
| 265 | Groups: make([]*GroupObject, 0), |
| 266 | Computers: make([]*ComputerObject, 0), |
| 267 | Domains: make([]*DomainObject, 0), |
| 268 | OUs: make([]*OUObject, 0), |
| 269 | UsersBySID: make(map[string]*UserObject), |
| 270 | GroupsBySID: make(map[string]*GroupObject), |
| 271 | ComputersBySID: make(map[string]*ComputerObject), |
| 272 | OUsByGUID: make(map[string]*OUObject), |
| 273 | ObjectsByDNT: make(map[uint64]TypedPrincipal), |
| 274 | PDNTByDNT: make(map[uint64]uint64), |
| 275 | } |
| 276 | } |