● 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
← Back to directory
104 lines · 2.4 KB
Raw
1package v5
2
3import (
4 "archive/zip"
5 "encoding/json"
6 "fmt"
7 "os"
8 "path/filepath"
9 "time"
10
11 "github.com/shehzade/ntds-to-bloodhound/pkg/ad"
12 "github.com/shehzade/ntds-to-bloodhound/pkg/bloodhound"
13)
14
15func GenerateZipFilename() string {
16 now := time.Now().UTC()
17 return fmt.Sprintf("BloodHound_%s.zip", now.Format("20060102150405"))
18}
19
20func CreateZipArchive(snap *ad.ADSnapshot, targetZipPath string) error {
21 dir := filepath.Dir(targetZipPath)
22 if err := os.MkdirAll(dir, 0755); err != nil {
23 return fmt.Errorf("failed to create directory %s: %w", dir, err)
24 }
25
26 zipFile, err := os.Create(targetZipPath)
27 if err != nil {
28 return fmt.Errorf("failed to create zip file %s: %w", targetZipPath, err)
29 }
30 defer zipFile.Close()
31
32 zipWriter := zip.NewWriter(zipFile)
33 defer zipWriter.Close()
34
35 writeEntry := func(filename, objType string, data []interface{}) error {
36 container := bloodhound.Container{
37 Data: data,
38 Meta: bloodhound.Meta{
39 Methods: 0,
40 Type: objType,
41 Count: len(data),
42 Version: 5,
43 },
44 }
45
46 bytes, err := json.MarshalIndent(container, "", " ")
47 if err != nil {
48 return fmt.Errorf("failed to marshal %s: %w", filename, err)
49 }
50
51 w, err := zipWriter.Create(filename)
52 if err != nil {
53 return fmt.Errorf("failed to create zip entry %s: %w", filename, err)
54 }
55
56 if _, err := w.Write(bytes); err != nil {
57 return fmt.Errorf("failed to write zip entry %s: %w", filename, err)
58 }
59
60 return nil
61 }
62
63 usersData := make([]interface{}, len(snap.Users))
64 for i, u := range snap.Users {
65 usersData[i] = u
66 }
67 if err := writeEntry("users.json", "users", usersData); err != nil {
68 return err
69 }
70
71 groupsData := make([]interface{}, len(snap.Groups))
72 for i, g := range snap.Groups {
73 groupsData[i] = g
74 }
75 if err := writeEntry("groups.json", "groups", groupsData); err != nil {
76 return err
77 }
78
79 computersData := make([]interface{}, len(snap.Computers))
80 for i, c := range snap.Computers {
81 computersData[i] = c
82 }
83 if err := writeEntry("computers.json", "computers", computersData); err != nil {
84 return err
85 }
86
87 domainsData := make([]interface{}, len(snap.Domains))
88 for i, d := range snap.Domains {
89 domainsData[i] = d.ToV5()
90 }
91 if err := writeEntry("domains.json", "domains", domainsData); err != nil {
92 return err
93 }
94
95 ousData := make([]interface{}, len(snap.OUs))
96 for i, o := range snap.OUs {
97 ousData[i] = o
98 }
99 if err := writeEntry("ous.json", "ous", ousData); err != nil {
100 return err
101 }
102
103 return nil
104}