● 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
Code 21 Commits 1 Releases 5
ntds-to-bloodhound / pkg / bloodhound / v5 / writer.go
← Back to directory
91 lines · 2.0 KB
Raw
1package v5
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "path/filepath"
8
9 "github.com/shehzade/ntds-to-bloodhound/pkg/ad"
10 "github.com/shehzade/ntds-to-bloodhound/pkg/bloodhound"
11)
12
13type Writer struct {
14 outDir string
15}
16
17func NewWriter(outDir string) *Writer {
18 return &Writer{outDir: outDir}
19}
20
21func (w *Writer) WriteSnapshot(snap *ad.ADSnapshot) error {
22 if err := os.MkdirAll(w.outDir, 0755); err != nil {
23 return fmt.Errorf("failed to create output directory %s: %w", w.outDir, err)
24 }
25
26 usersData := make([]interface{}, len(snap.Users))
27 for i, u := range snap.Users {
28 usersData[i] = u
29 }
30 if err := w.writeFile("users.json", "users", usersData); err != nil {
31 return err
32 }
33
34 groupsData := make([]interface{}, len(snap.Groups))
35 for i, g := range snap.Groups {
36 groupsData[i] = g
37 }
38 if err := w.writeFile("groups.json", "groups", groupsData); err != nil {
39 return err
40 }
41
42 computersData := make([]interface{}, len(snap.Computers))
43 for i, c := range snap.Computers {
44 computersData[i] = c
45 }
46 if err := w.writeFile("computers.json", "computers", computersData); err != nil {
47 return err
48 }
49
50 domainsData := make([]interface{}, len(snap.Domains))
51 for i, d := range snap.Domains {
52 domainsData[i] = d.ToV5()
53 }
54 if err := w.writeFile("domains.json", "domains", domainsData); err != nil {
55 return err
56 }
57
58 ousData := make([]interface{}, len(snap.OUs))
59 for i, o := range snap.OUs {
60 ousData[i] = o
61 }
62 if err := w.writeFile("ous.json", "ous", ousData); err != nil {
63 return err
64 }
65
66 return nil
67}
68
69func (w *Writer) writeFile(filename, objType string, data []interface{}) error {
70 container := bloodhound.Container{
71 Data: data,
72 Meta: bloodhound.Meta{
73 Methods: 0,
74 Type: objType,
75 Count: len(data),
76 Version: 5,
77 },
78 }
79
80 bytes, err := json.MarshalIndent(container, "", " ")
81 if err != nil {
82 return fmt.Errorf("failed to marshal %s: %w", filename, err)
83 }
84
85 filePath := filepath.Join(w.outDir, filename)
86 if err := os.WriteFile(filePath, bytes, 0644); err != nil {
87 return fmt.Errorf("failed to write %s: %w", filePath, err)
88 }
89
90 return nil
91}