-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMatch-Hash-To-User.go
More file actions
146 lines (124 loc) · 4.6 KB
/
Match-Hash-To-User.go
File metadata and controls
146 lines (124 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"bufio"
"encoding/hex"
"fmt"
"os"
"regexp"
"strings"
"sync"
)
// Configurable settings
const (
passwordMinLength = 1 // Filter: Only save passwords longer than X chars
numWorkers = 10 // Number of concurrent workers for processing NTDS
)
func decodeHex(hexStr string) (string, error) {
decoded, err := hex.DecodeString(hexStr)
if err != nil {
return "", err
}
return string(decoded), nil
}
func main() {
// Ensure correct usage
if len(os.Args) < 4 {
fmt.Println("Usage: ./match-hash-to-user NTDS-DUMP HASHCAT-POTFILE OUTPUT-FILE")
os.Exit(1)
}
ntdsFile := os.Args[1]
hashFilePath := os.Args[2]
outputFilePath := os.Args[3]
// Compile regex for NTLM hash validation
ntlmRegex := regexp.MustCompile(`(?i)^[0-9a-f]{32}$`)
// Load hashes from hashcat.potfile into a map
hashes := make(map[string]string)
hashFileHandle, err := os.Open(hashFilePath)
if err != nil {
fmt.Println("Error opening hashcat.potfile:", err)
os.Exit(1)
}
defer hashFileHandle.Close()
scanner := bufio.NewScanner(hashFileHandle)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 && ntlmRegex.MatchString(parts[0]) {
password := strings.TrimSpace(parts[1])
if strings.HasPrefix(strings.ToUpper(password), "$HEX[") {
hexValue := strings.TrimSuffix(strings.TrimPrefix(password, "$HEX["), "]")
decoded, err := decodeHex(hexValue)
if err == nil {
password = decoded
}
}
if len(password) >= passwordMinLength {
hashes[parts[0]] = password
}
}
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading hashcat.potfile:", err)
os.Exit(1)
}
// Open NTDS dump
ntdsHandle, err := os.Open(ntdsFile)
if err != nil {
fmt.Println("Error opening NTDS-DUMP file:", err)
os.Exit(1)
}
defer ntdsHandle.Close()
// Open output file for writing results
outputHandle, err := os.Create(outputFilePath)
if err != nil {
fmt.Println("Error creating output file:", err)
os.Exit(1)
}
defer outputHandle.Close()
writer := bufio.NewWriter(outputHandle)
// Concurrent processing with a worker pool
lines := make(chan string, 100)
results := make(chan string, 100)
var wg sync.WaitGroup
// Worker function
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for line := range lines {
parts := strings.Split(line, ":")
if len(parts) != 7 {
continue
}
username := parts[0]
ntlmHash := parts[3]
if password, exists := hashes[ntlmHash]; exists {
results <- fmt.Sprintf("%s:%s\n", username, password)
}
}
}()
}
// Start a separate goroutine to write results
go func() {
for result := range results {
writer.WriteString(result)
}
writer.Flush()
}()
// Read NTDS file line by line and distribute work
scanner = bufio.NewScanner(ntdsHandle)
for scanner.Scan() {
lines <- scanner.Text()
}
close(lines)
// Wait for workers to finish processing
wg.Wait()
close(results)
if err := scanner.Err(); err != nil {
fmt.Println("Error reading NTDS-DUMP file:", err)
os.Exit(1)
}
fmt.Println("Matching complete! Results saved to:", outputFilePath)
}
fmt.Println("Matching complete! Results saved to:", outputFilePath)
}