[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
104 lines · 2.4 KB
| 1 | package v5 |
| 2 | |
| 3 | import ( |
| 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 | |
| 15 | func GenerateZipFilename() string { |
| 16 | now := time.Now().UTC() |
| 17 | return fmt.Sprintf("BloodHound_%s.zip", now.Format("20060102150405")) |
| 18 | } |
| 19 | |
| 20 | func 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 | } |