Files
CrowsNest/internal/dehashed/dehashed.go
T

335 lines
9.3 KiB
Go
Raw Normal View History

2025-05-16 15:33:29 -04:00
package dehashed
2025-05-14 22:00:38 -04:00
import (
2025-05-17 10:00:59 -04:00
"crowsnest/internal/debug"
"crowsnest/internal/export"
2025-05-17 12:58:37 -04:00
"crowsnest/internal/pretty"
2025-05-17 10:00:59 -04:00
"crowsnest/internal/sqlite"
2025-05-14 22:00:38 -04:00
"fmt"
"go.uber.org/zap"
"os"
2025-05-17 12:58:37 -04:00
"strings"
2025-05-14 22:00:38 -04:00
)
2026-04-07 09:09:12 -04:00
const (
maxSearchResultsPerPage = 10000
maxSearchResultsPerQuery = 50000
)
2025-05-14 22:00:38 -04:00
// Dehasher is a struct for querying the Dehashed API
type Dehasher struct {
2026-04-07 09:09:12 -04:00
options sqlite.QueryOptions
nextPage int
debug bool
balance int
maxResults int
request *DehashedSearchRequest
client *DehashedClientV2
2025-05-14 22:00:38 -04:00
}
// NewDehasher creates a new Dehasher
func NewDehasher(options *sqlite.QueryOptions) *Dehasher {
dh := &Dehasher{
options: *options,
nextPage: options.StartingPage + 1,
2025-05-16 15:33:29 -04:00
debug: options.Debug,
balance: 0,
2025-05-14 22:00:38 -04:00
}
dh.setQueries()
2025-05-16 15:33:29 -04:00
dh.request = NewDehashedSearchRequest(dh.options.StartingPage, dh.options.MaxRecords, dh.options.WildcardMatch, dh.options.RegexMatch, false, options.Debug)
2025-05-14 22:00:38 -04:00
dh.buildRequest()
return dh
}
// SetClientCredentials sets the client credentials for the dehasher
func (dh *Dehasher) SetClientCredentials(key string) {
2025-05-16 15:33:29 -04:00
dh.client = NewDehashedClientV2(key, dh.debug)
2025-05-14 22:00:38 -04:00
}
func (dh *Dehasher) getNextPage() int {
2025-05-16 15:33:29 -04:00
if dh.debug {
debug.PrintInfo(fmt.Sprintf("getting next page: %d", dh.nextPage))
}
2025-05-14 22:00:38 -04:00
nextPage := dh.nextPage
dh.nextPage += 1
return nextPage
}
// setQueries sets the number of queries to make based on the number of records and requests
func (dh *Dehasher) setQueries() {
2025-05-16 15:33:29 -04:00
if dh.debug {
debug.PrintInfo("setting queries")
}
2026-04-07 09:09:12 -04:00
if dh.options.MaxRequests == 0 {
2025-05-14 22:00:38 -04:00
zap.L().Error("max requests cannot be zero")
fmt.Println("[!] Max Requests cannot be zero")
os.Exit(1)
}
2026-04-07 09:09:12 -04:00
requestedMaxResults := dh.options.MaxRecords
if requestedMaxResults <= 0 {
requestedMaxResults = maxSearchResultsPerQuery
}
if requestedMaxResults > maxSearchResultsPerQuery {
requestedMaxResults = maxSearchResultsPerQuery
}
pageSize := requestedMaxResults
if pageSize > maxSearchResultsPerPage {
pageSize = maxSearchResultsPerPage
}
numQueries := (requestedMaxResults + pageSize - 1) / pageSize
if dh.options.MaxRequests > 0 && dh.options.MaxRequests < numQueries {
numQueries = dh.options.MaxRequests
}
dh.maxResults = requestedMaxResults
if requestLimit := numQueries * pageSize; requestLimit < dh.maxResults {
dh.maxResults = requestLimit
}
dh.options.MaxRecords = pageSize
2025-05-14 22:00:38 -04:00
dh.options.MaxRequests = numQueries
2025-05-16 15:33:29 -04:00
2026-04-07 09:09:12 -04:00
zap.L().Info("dehashed_search_pagination",
zap.Int("max_results", dh.maxResults),
zap.Int("page_size", dh.options.MaxRecords),
zap.Int("max_requests", dh.options.MaxRequests),
)
2025-05-16 15:33:29 -04:00
if dh.debug {
debug.PrintInfo(fmt.Sprintf("setting max requests: %d", numQueries))
2026-04-07 09:09:12 -04:00
debug.PrintInfo(fmt.Sprintf("setting page size: %d", dh.options.MaxRecords))
debug.PrintInfo(fmt.Sprintf("setting max results: %d", dh.maxResults))
2025-05-16 15:33:29 -04:00
}
2026-04-07 09:09:12 -04:00
fmt.Printf("Making %d Requests for up to %d Records (%d per request)\n", dh.options.MaxRequests, dh.maxResults, dh.options.MaxRecords)
2025-05-14 22:00:38 -04:00
}
// Start starts the querying process
func (dh *Dehasher) Start() {
2025-05-15 21:11:24 -04:00
fmt.Printf("[*] Querying Dehashed API...\n")
2025-05-14 22:00:38 -04:00
for i := 0; i < dh.options.MaxRequests; i++ {
2025-05-15 21:11:24 -04:00
fmt.Printf(" [*] Performing Request...\n")
2025-05-16 15:33:29 -04:00
count, balance, err := dh.client.Search(*dh.request)
2025-05-14 22:00:38 -04:00
if err != nil {
2025-05-16 15:33:29 -04:00
if dh.debug {
debug.PrintInfo("error performing request")
debug.PrintError(err)
}
// Check if it's a DehashError
if dhErr, ok := err.(*DehashError); ok {
2025-05-16 15:33:29 -04:00
fmt.Printf(" [!] Dehashed API Error: %s (Code: %d)\n", dhErr.Message, dhErr.Code)
zap.L().Error("dehashed_api_error",
zap.String("message", dhErr.Message),
zap.Int("code", dhErr.Code),
)
} else {
2025-05-15 21:11:24 -04:00
fmt.Printf(" [!] Error performing request: %v\n", err)
zap.L().Error("request_error",
zap.String("message", "failed to perform request"),
zap.Error(err),
)
}
2025-05-16 15:33:29 -04:00
if len(dh.client.results) > 0 {
fmt.Printf(" [!] Partial results retrieved. Storing Results...\n")
2025-05-17 10:25:16 -04:00
err := sqlite.StoreDehashedResults(dh.client.GetResults())
2025-05-16 15:33:29 -04:00
if err != nil {
zap.L().Error("store_results",
zap.String("message", "failed to store results"),
zap.Error(err),
)
fmt.Printf(" [!] Error storing results: %v\n", err)
}
}
dh.parseResults()
2025-05-14 22:00:38 -04:00
os.Exit(-1)
}
2025-05-16 15:33:29 -04:00
dh.balance = balance
2025-05-14 22:00:38 -04:00
if count < dh.options.MaxRecords {
2025-05-15 21:11:24 -04:00
fmt.Printf(" [+] Retrieved %d records\n", count)
fmt.Printf(" [-] Not enough entries, ending queries\n")
2025-05-14 22:00:38 -04:00
break
} else {
2026-04-07 09:09:12 -04:00
fmt.Printf(" [+] Retrieved %d records\n", count)
2025-05-14 22:00:38 -04:00
}
2025-05-16 15:33:29 -04:00
if dh.options.PrintBalance {
fmt.Printf(" [*] Balance: %d\n", balance)
}
2025-05-14 22:00:38 -04:00
dh.request.Page = dh.getNextPage()
}
dh.parseResults()
}
// buildRequest constructs the query map
func (dh *Dehasher) buildRequest() {
if len(dh.options.UsernameQuery) > 0 {
dh.request.AddUsernameQuery(dh.options.UsernameQuery)
}
if len(dh.options.EmailQuery) > 0 {
dh.request.AddEmailQuery(dh.options.EmailQuery)
}
if len(dh.options.IpQuery) > 0 {
dh.request.AddIpAddressQuery(dh.options.IpQuery)
}
if len(dh.options.HashQuery) > 0 {
dh.request.AddHashedPasswordQuery(dh.options.HashQuery)
}
if len(dh.options.PassQuery) > 0 {
dh.request.AddPasswordQuery(dh.options.PassQuery)
}
if len(dh.options.NameQuery) > 0 {
dh.request.AddNameQuery(dh.options.NameQuery)
}
if len(dh.options.DomainQuery) > 0 {
dh.request.AddDomainQuery(dh.options.DomainQuery)
}
if len(dh.options.VinQuery) > 0 {
dh.request.AddVinQuery(dh.options.VinQuery)
}
if len(dh.options.LicensePlateQuery) > 0 {
dh.request.AddLicensePlateQuery(dh.options.LicensePlateQuery)
}
if len(dh.options.AddressQuery) > 0 {
dh.request.AddAddressQuery(dh.options.AddressQuery)
}
if len(dh.options.PhoneQuery) > 0 {
dh.request.AddPhoneQuery(dh.options.PhoneQuery)
}
if len(dh.options.SocialQuery) > 0 {
dh.request.AddSocialQuery(dh.options.SocialQuery)
}
if len(dh.options.CryptoAddressQuery) > 0 {
dh.request.AddCryptoAddressQuery(dh.options.CryptoAddressQuery)
}
}
// parseResults parses the results and writes them to a file
func (dh *Dehasher) parseResults() {
zap.L().Info("extracting_credentials")
results := dh.client.GetResults()
2026-04-07 09:09:12 -04:00
if dh.maxResults > 0 && len(results.Results) > dh.maxResults {
results.Results = results.Results[:dh.maxResults]
}
creds := results.ExtractUsers()
2025-05-17 12:58:37 -04:00
fmt.Printf(" [+] Discovered %d Credentials\n", len(creds))
err := sqlite.StoreUsers(creds)
2025-05-14 22:00:38 -04:00
if err != nil {
zap.L().Error("store_creds",
zap.String("message", "failed to store creds"),
zap.Error(err),
)
}
zap.L().Info("creds_stored", zap.Int("count", len(creds)))
zap.L().Info("storing_results")
2025-05-17 10:25:16 -04:00
err = sqlite.StoreDehashedResults(results)
2025-05-14 22:00:38 -04:00
if err != nil {
zap.L().Error("store_results",
zap.String("message", "failed to store results"),
zap.Error(err),
)
}
zap.L().Info("results_stored", zap.Int("count", len(results.Results)))
if len(results.Results) > 0 {
2025-05-17 12:58:37 -04:00
var (
headers = []string{"Email", "Username", "Password"}
rows [][]string
)
fmt.Printf(" [*] Writing entries to file: %s.%s\n", dh.options.OutputFile, dh.options.OutputFormat.String())
2025-05-14 22:00:38 -04:00
if !dh.options.CredsOnly {
err := export.WriteToFile(results, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil {
2025-05-17 12:58:37 -04:00
fmt.Printf("[!] Error Writing to file: %v Outputting to terminal.\n", err)
zap.L().Error("write_results",
zap.String("message", "failed to write results to file"),
zap.Error(err),
)
2025-05-14 22:00:38 -04:00
} else {
2025-05-17 12:58:37 -04:00
fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing results table")
2025-05-14 22:00:38 -04:00
}
2025-05-17 12:58:37 -04:00
headers = []string{"Email", "Username", "Password", "Phone", "Company"}
2025-05-17 12:58:37 -04:00
if len(results.Results) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
r := results.Results[i]
rows = append(rows, []string{
strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "),
strings.Join(r.Password, ", "),
strings.Join(r.Phone, ", "),
2025-05-17 12:58:37 -04:00
strings.Join(r.Company, ", ")})
}
} else {
for _, r := range results.Results {
rows = append(rows, []string{
strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "),
strings.Join(r.Password, ", "),
strings.Join(r.Phone, ", "),
2025-05-17 12:58:37 -04:00
strings.Join(r.Company, ", ")})
}
}
// Print Table
pretty.Table(headers, rows)
2025-05-14 22:00:38 -04:00
} else {
2025-05-17 12:58:37 -04:00
if dh.debug {
debug.PrintInfo("extracting credentials")
}
creds := results.ExtractUsers()
2025-05-17 12:58:37 -04:00
if dh.debug {
debug.PrintInfo("writing credentials to file")
}
2025-05-14 22:00:38 -04:00
err := export.WriteCredsToFile(creds, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil {
2025-05-17 12:58:37 -04:00
fmt.Printf("[!] Error Writing to file: %v\n Outputting to terminal.", err)
zap.L().Error("write_creds",
zap.String("message", "failed to write creds to file"),
zap.Error(err),
)
} else {
fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing credentials table")
}
headers = []string{"Email", "Username", "Password"}
if len(creds) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
c := creds[i]
rows = append(rows, []string{c.Email, c.Username, c.Password})
}
2025-05-14 22:00:38 -04:00
} else {
2025-05-17 12:58:37 -04:00
for _, c := range creds {
rows = append(rows, []string{c.Email, c.Username, c.Password})
}
2025-05-14 22:00:38 -04:00
}
2025-05-17 12:58:37 -04:00
// Print Table
pretty.Table(headers, rows)
2025-05-14 22:00:38 -04:00
}
2025-05-17 12:58:37 -04:00
} else {
fmt.Println(" [-] No results found")
2025-05-14 22:00:38 -04:00
}
}