commit a4dffe61bf61701f67a381a71bfc42109f1bf560 Author: Ar1ste1a <107807560+Ar1ste1a@users.noreply.github.com> Date: Wed May 14 22:00:38 2025 -0400 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8196a7d --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Dehasher +## A cli tool built for interaction with the Dehash API + +
+ Ar1ste1a +
+ +# Features +- Output Format Control +- Request Limiting +- Record Limiting +- Regular Expression Handling +- Exact Match Handling +- Error Handling +- Credential Dumping +- Intelligent Token Usage +# Options + +```bash-session +usage: Dehasher [-h --help] {-k --key} {-a --authorized-email} [-h --help] [-m --max-records] [-r --max-requests] [-B --print-balance] [-X --exact-match] [-R --regex-match] [-t --list-tokens] [-o --output-file-name] [-T --output-txt] [-J --output-json] [-Y --output-yaml] [-x --output-xml] [-U --username-query] [-E --email-query] [-I --ip-address-query] [-P --password-query] [-Q --hashed-password-query] [-N --name-query] [-C --creds-only] + +Dehashed Tool + +options: + -h --help show this help message and exit + -m --max-records Maximum amount of records to return + -r --max-requests Maximum number of requests to make + -B --print-balance Print remaining balance after requests + -X --exact-match Use Exact Matching on fields + -R --regex-match Use Regex Matching on fields + -t --list-tokens List the number of tokens remaining + -o --output-file-name File to output results to + -T --output-txt Output to text file + -J --output-json Output to JSON file + -Y --output-yaml Output to YAML file + -x --output-xml Output to XML file + -U --username-query Username Query + -E --email-query Email Query + -I --ip-address-query IP Address Query + -P --password-query Password Query + -Q --hashed-password-query Hashed Password Query + -N --name-query Name Query + -C --creds-only Return Credentials Only + -k --key API Key + -a --authorized-email Email to pair with key for authentication + + +v1.0 +``` + +# Sample Run +```bash-session +-k ddq -a ar1ste1a@ -E @example.com -C -o example_creds +Making 3 Requests for 10000 Records (30000 Total) + [*] Performing Request... + [*] Retrieved 60 Records +[-] Not Enough Entries, ending queries +[+] Discovered 60 Records + [*] Writing entries file: example_creds.json + [*] Success + +``` + +# Getting Started + +To begin, clone the repository +``` bash-session +git clone https://github.com/Ar1ste1a/Dehasher.git +cd Dehasher +go build dehasher.go +``` + +# Crafting a query + +## Simple Query +``` go +# Provide credentials for emails matching @target.com +dehasher -k ddq -a ar1ste1a@domain.tld -E @target.com +``` + +## Simple Credentials Query +``` go +# Provide credentials for emails matching @target.com +dehasher -k ddq -a ar1ste1a@domain.tld -E @target.com -C +``` + +## Simple Query Returning Balance +``` go +# Provide credentials for emails matching @target.com +dehasher -k ddq -a ar1ste1a@domain.tld -E @target.com -C -B +``` + +## Regex Query +``` go +# Return matches for emails matching this given regex query +# -R e: Specify the '-E' field as a regex entry +dehasher -k ddq -a ar1ste1a@domain.tld -E '[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)?@target.com' -C -B -R e +``` + +## Exact Match Query +``` go +# Return matches for usernames exactly matching "admin" +# -X u: Specify the '-U' field as an exact match entry +dehasher -k ddq -a ar1ste1a@domain.tld -C -B -U admin -X u +``` + +## Output Text (default JSON) +``` go +# Return matches for usernames exactly matching "admin" and write to text file 'admins_file.txt' +dehasher -k ddq -a ar1ste1a@domain.tld -C -B -U admin -X u -T -o admins_file +``` + +## Output YAML +``` go +# Return matches for usernames exactly matching "admin" and write to yaml file 'admins_file.yaml' +dehasher -k ddq -a ar1ste1a@domain.tld -C -B -U admin -X u -Y -o admins_file +``` + +## Output XML +``` go +# Return matches for usernames exactly matching "admin" and write to xml file 'admins_file.xml' +dehasher -k ddq -a ar1ste1a@domain.tld -C -B -U admin -X u -x -o admins_file +``` + diff --git a/cmd/db.go b/cmd/db.go new file mode 100644 index 0000000..d8361c9 --- /dev/null +++ b/cmd/db.go @@ -0,0 +1,656 @@ +package cmd + +import ( + "dehasher/internal/export" + "dehasher/internal/files" + "dehasher/internal/pretty" + "dehasher/internal/sqlite" + "encoding/json" + "fmt" + "github.com/spf13/cobra" + "go.uber.org/zap" + "strings" + "time" +) + +var ( + // DB command flags + dbPath string + + // DB query command flags + usernameDBQuery string + emailDBQuery string + ipDBQuery string + passwordDBQuery string + hashDBQuery string + nameDBQuery string + vinDBQuery string + licensePlateDBQuery string + addressDBQuery string + phoneDBQuery string + socialDBQuery string + cryptoCurrencyAddressDBQuery string + domainDBQuery string + limitResultsDB int + exactMatchDBQuery bool + outputFormatDB string + nonEmptyFieldsDBQuery string + displayFieldsDBQuery string + tableTypeDBQuery string + + // DB runs command flags + startDateDBRuns string + endDateDBRuns string + containsQueryDBRuns string + lastXRunsDBRuns int + + // DB command + dbCmd = &cobra.Command{ + Use: "db", + Short: "Database operations for Dehasher", + Long: `Perform database operations like export, import, and query on the local Dehasher database.`, + } +) + +func init() { + // Add subcommands to db command + dbCmd.AddCommand(dbExportCmd) + dbCmd.AddCommand(dbQueryCmd) + dbCmd.AddCommand(dbRunsCmd) + dbCmd.AddCommand(dbCredsCmd) + + // Add flags specific to db command + dbCmd.PersistentFlags().StringVarP(&dbPath, "db-path", "D", "", "Path to database (default: ~/.local/share/Dehasher/dehashed.db)") + + // Add flags specific to db query command + dbQueryCmd.Flags().StringVarP(&usernameDBQuery, "username", "u", "", "Filter by username") + dbQueryCmd.Flags().StringVarP(&emailDBQuery, "email", "e", "", "Filter by email") + dbQueryCmd.Flags().StringVarP(&ipDBQuery, "ip", "i", "", "Filter by IP address") + dbQueryCmd.Flags().StringVarP(&passwordDBQuery, "password", "p", "", "Filter by password") + dbQueryCmd.Flags().StringVarP(&hashDBQuery, "hash", "H", "", "Filter by hashed password") + dbQueryCmd.Flags().StringVarP(&nameDBQuery, "name", "n", "", "Filter by name") + dbQueryCmd.Flags().StringVarP(&vinDBQuery, "vin", "v", "", "Filter by VIN") + dbQueryCmd.Flags().StringVarP(&licensePlateDBQuery, "license", "L", "", "Filter by license plate") + dbQueryCmd.Flags().StringVarP(&addressDBQuery, "address", "a", "", "Filter by address") + dbQueryCmd.Flags().StringVarP(&phoneDBQuery, "phone", "P", "", "Filter by phone number") + dbQueryCmd.Flags().StringVarP(&socialDBQuery, "social", "s", "", "Filter by social media handle") + dbQueryCmd.Flags().StringVarP(&cryptoCurrencyAddressDBQuery, "crypto", "c", "", "Filter by cryptocurrency address") + dbQueryCmd.Flags().StringVarP(&domainDBQuery, "domain", "d", "", "Filter by domain/URL") + dbQueryCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results") + dbQueryCmd.Flags().BoolVarP(&exactMatchDBQuery, "exact", "x", false, "Use exact matching instead of partial matching") + dbQueryCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)") + dbQueryCmd.Flags().StringVar(&nonEmptyFieldsDBQuery, "non-empty", "", "Filter for non-empty fields (comma-separated list, e.g., 'password,email')") + dbQueryCmd.Flags().StringVar(&displayFieldsDBQuery, "display", "", "Fields to display in output (comma-separated list, e.g., 'username,email,password')") + dbQueryCmd.Flags().StringVarP(&tableTypeDBQuery, "table", "t", "results", "Table to query (results, runs, creds)") + + // Add flags specific to db runs command + dbRunsCmd.Flags().StringVarP(&startDateDBRuns, "start-date", "s", "", "Start date for filtering runs (format: YYYY-MM-DD)") + dbRunsCmd.Flags().StringVarP(&endDateDBRuns, "end-date", "e", "", "End date for filtering runs (format: YYYY-MM-DD)") + dbRunsCmd.Flags().StringVarP(&containsQueryDBRuns, "contains", "c", "", "Filter runs containing this query string") + dbRunsCmd.Flags().IntVarP(&lastXRunsDBRuns, "last", "x", 0, "Show the last X runs") + dbRunsCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results") + dbRunsCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)") + + // Add flags specific to db creds command + dbCredsCmd.Flags().StringVarP(&usernameDBQuery, "username", "u", "", "Filter by username") + dbCredsCmd.Flags().StringVarP(&emailDBQuery, "email", "e", "", "Filter by email") + dbCredsCmd.Flags().StringVarP(&passwordDBQuery, "password", "p", "", "Filter by password") + dbCredsCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results") + dbCredsCmd.Flags().BoolVarP(&exactMatchDBQuery, "exact", "x", false, "Use exact matching instead of partial matching") + dbCredsCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)") + dbCredsCmd.Flags().StringVar(&nonEmptyFieldsDBQuery, "non-empty", "", "Filter for non-empty fields (comma-separated list, e.g., 'password,email')") + dbCredsCmd.Flags().StringVar(&displayFieldsDBQuery, "display", "", "Fields to display in output (comma-separated list, e.g., 'username,email,password')") +} + +// DB export command +var dbExportCmd = &cobra.Command{ + Use: "export", + Short: "Export database to file", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Exporting database...") + // Create DBOptions with the provided parameters + options := &sqlite.DBOptions{ + Username: usernameDBQuery, + Email: emailDBQuery, + IPAddress: ipDBQuery, + Password: passwordDBQuery, + HashedPassword: hashDBQuery, + Name: nameDBQuery, + Limit: limitResultsDB, + ExactMatch: exactMatchDBQuery, + } + + // Parse non-empty fields if provided + if nonEmptyFieldsDBQuery != "" { + options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",") + } + + // Parse display fields if provided + if displayFieldsDBQuery != "" { + options.DisplayFields = strings.Split(displayFieldsDBQuery, ",") + } + + // Check if at least one search parameter is provided + if options.Username == "" && options.Email == "" && options.IPAddress == "" && + options.Password == "" && options.HashedPassword == "" && options.Name == "" && + len(options.NonEmptyFields) == 0 { + fmt.Println("Error: At least one search parameter is required.") + cmd.Help() + return + } + + // Get the count of matching results + count, err := sqlite.GetResultsCount(options) + if err != nil { + fmt.Printf("Error counting results: %v\n", err) + return + } + + // Query the database + results, err := sqlite.QueryResults(options) + if err != nil { + fmt.Printf("Error querying database: %v\n", err) + return + } + dhResults := sqlite.DehashedResults{Results: results} + + fmt.Printf("Found %d results (showing %d):\n", count, len(results)) + + // Output results based on format + ft := files.GetFileType(outputFormatDB) + err = export.WriteToFile(dhResults, "dehasher_export", ft) + if err != nil { + zap.L().Error("write_to_file", + zap.String("message", "failed to write to file"), + zap.Error(err), + ) + fmt.Printf("Error writing to file: %v\n", err) + return + } + fmt.Printf("Exported successfully to file: dehasher_export%s\n", ft.Extension()) + }, +} + +// DB query command +var dbQueryCmd = &cobra.Command{ + Use: "query", + Short: "Query local database", + Long: `Query the local database for previously run dehasher queries based on various parameters.`, + Run: func(cmd *cobra.Command, args []string) { + // Determine which table to query based on the tableTypeDBQuery parameter + switch tableTypeDBQuery { + case "results": + queryResultsTable(cmd) + case "runs": + queryRunsTable() + case "creds": + queryCredsTable(cmd) + default: + fmt.Printf("Error: Unknown table type '%s'. Valid options are: results, runs, creds\n", tableTypeDBQuery) + cmd.Help() + return + } + }, +} + +func queryRunsTable() { + // Parse date strings to time.Time + var startDate, endDate time.Time + var err error + + if startDateDBRuns != "" { + startDate, err = time.Parse("2006-01-02", startDateDBRuns) + if err != nil { + fmt.Printf("Error parsing start date: %v\n", err) + return + } + } + + if endDateDBRuns != "" { + endDate, err = time.Parse("2006-01-02", endDateDBRuns) + if err != nil { + fmt.Printf("Error parsing end date: %v\n", err) + return + } + // Set end date to end of day + endDate = endDate.Add(24*time.Hour - time.Second) + } + + // Get the count of matching runs + count, err := sqlite.GetRunsCount(lastXRunsDBRuns, startDate, endDate, containsQueryDBRuns) + if err != nil { + fmt.Printf("Error counting runs: %v\n", err) + return + } + + // Query the database + runs, err := sqlite.QueryRuns(limitResultsDB, lastXRunsDBRuns, startDate, endDate, containsQueryDBRuns) + if err != nil { + fmt.Printf("Error querying runs: %v\n", err) + return + } + + displayRunsResults(count, runs) +} + +func displayRunsResults(count int64, runs []sqlite.QueryOptions) { + // Display the results + fmt.Printf("Found %d runs (showing %d):\n", count, len(runs)) + + if len(runs) == 0 { + fmt.Println("No runs found.") + return + } + + // Output results based on format + switch outputFormatDB { + case "json": + data, err := json.MarshalIndent(runs, "", " ") + if err != nil { + fmt.Printf("Error formatting results: %v\n", err) + return + } + fmt.Println(string(data)) + case "table": + // Define headers and rows for the table + headers := []string{"ID", "Created At", "Max Records", "Username Query", "Email Query", "IP Query", "Password Query", "Hash Query", "Name Query", "Domain Query"} + rows := make([][]string, len(runs)) + + for i, run := range runs { + rows[i] = []string{ + fmt.Sprintf("%d", run.ID), + run.CreatedAt.Format("2006-01-02 15:04:05"), + fmt.Sprintf("%d", run.MaxRecords), + truncate(run.UsernameQuery, 20), + truncate(run.EmailQuery, 20), + truncate(run.IpQuery, 20), + truncate(run.PassQuery, 20), + truncate(run.HashQuery, 20), + truncate(run.NameQuery, 20), + truncate(run.DomainQuery, 20), + } + } + + pretty.Table(headers, rows) + default: + // Simple output + for _, run := range runs { + fmt.Printf("Run ID: %d\n", run.ID) + fmt.Printf(" Created At: %s\n", run.CreatedAt.Format("2006-01-02 15:04:05")) + fmt.Printf(" Max Records: %d\n", run.MaxRecords) + fmt.Printf(" Max Requests: %d\n", run.MaxRequests) + fmt.Printf(" Starting Page: %d\n", run.StartingPage) + fmt.Printf(" Output Format: %s\n", run.OutputFormat.String()) + fmt.Printf(" Output File: %s\n", run.OutputFile) + fmt.Printf(" Regex Match: %t\n", run.RegexMatch) + fmt.Printf(" Wildcard Match: %t\n", run.WildcardMatch) + fmt.Printf(" Username Query: %s\n", run.UsernameQuery) + fmt.Printf(" Email Query: %s\n", run.EmailQuery) + fmt.Printf(" IP Query: %s\n", run.IpQuery) + fmt.Printf(" Password Query: %s\n", run.PassQuery) + fmt.Printf(" Hash Query: %s\n", run.HashQuery) + fmt.Printf(" Name Query: %s\n", run.NameQuery) + fmt.Printf(" Domain Query: %s\n", run.DomainQuery) + fmt.Printf(" VIN Query: %s\n", run.VinQuery) + fmt.Printf(" License Plate Query: %s\n", run.LicensePlateQuery) + fmt.Printf(" Address Query: %s\n", run.AddressQuery) + fmt.Printf(" Phone Query: %s\n", run.PhoneQuery) + fmt.Printf(" Social Query: %s\n", run.SocialQuery) + fmt.Printf(" Crypto Address Query: %s\n", run.CryptoAddressQuery) + fmt.Printf(" Print Balance: %t\n", run.PrintBalance) + fmt.Printf(" Creds Only: %t\n", run.CredsOnly) + fmt.Println() + } + } +} + +// queryResultsTable queries the results table +func queryResultsTable(cmd *cobra.Command) { + // Create DBOptions with the provided parameters + options := &sqlite.DBOptions{ + Username: usernameDBQuery, + Email: emailDBQuery, + IPAddress: ipDBQuery, + Password: passwordDBQuery, + HashedPassword: hashDBQuery, + Name: nameDBQuery, + Vin: vinDBQuery, + LicensePlate: licensePlateDBQuery, + Address: addressDBQuery, + Phone: phoneDBQuery, + Social: socialDBQuery, + CryptoCurrencyAddress: cryptoCurrencyAddressDBQuery, + Domain: domainDBQuery, + Limit: limitResultsDB, + ExactMatch: exactMatchDBQuery, + } + + // Parse non-empty fields if provided + if nonEmptyFieldsDBQuery != "" { + options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",") + } + + // Parse display fields if provided + if displayFieldsDBQuery != "" { + options.DisplayFields = strings.Split(displayFieldsDBQuery, ",") + } + + // Check if at least one search parameter is provided + if options.Empty() { + fmt.Println("Error: At least one search parameter is required.") + cmd.Help() + return + } + + // Get the count of matching results + count, err := sqlite.GetResultsCount(options) + if err != nil { + fmt.Printf("Error counting results: %v\n", err) + return + } + + // Query the database + results, err := sqlite.QueryResults(options) + if err != nil { + fmt.Printf("Error querying database: %v\n", err) + return + } + + // Display the results + displayResultsTable(count, results, options) +} + +// displayResultsTable displays the results from the results table +func displayResultsTable(count int64, results []sqlite.Result, options *sqlite.DBOptions) { + // Display the results + fmt.Printf("Found %d results (showing %d):\n", count, len(results)) + + if len(results) == 0 { + fmt.Println("No results found.") + return + } + + // Output results based on format + switch outputFormatDB { + case "json": + data, err := json.MarshalIndent(results, "", " ") + if err != nil { + fmt.Printf("Error formatting results: %v\n", err) + return + } + fmt.Println(string(data)) + case "table": + // Determine which fields to display + type FieldInfo struct { + Name string + Width int + Getter func(result sqlite.Result) string + } + + // Define all available fields + allFields := []FieldInfo{ + {"Username", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Username), 20) }}, + {"Email", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Email), 30) }}, + {"IP Address", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.IpAddress), 15) }}, + {"Password", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Password), 20) }}, + {"Hashed Password", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.HashedPassword), 20) }}, + {"Name", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Name), 20) }}, + {"VIN", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Vin), 20) }}, + {"License Plate", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.LicensePlate), 15) }}, + {"Address", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Address), 30) }}, + {"Phone", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.Phone), 15) }}, + {"Social", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Social), 20) }}, + {"Crypto Address", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.CryptoCurrencyAddress), 20) }}, + {"Domain/URL", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Url), 30) }}, + } + + // Select fields to display + var fieldsToDisplay []FieldInfo + var headers []string + if len(options.DisplayFields) > 0 { + // Use specified fields + for _, fieldName := range options.DisplayFields { + fieldName = strings.ToLower(strings.TrimSpace(fieldName)) + for _, field := range allFields { + if strings.ToLower(field.Name) == fieldName || + (fieldName == "ip" && strings.ToLower(field.Name) == "ip address") || + (fieldName == "hash" && strings.ToLower(field.Name) == "hashed password") || + (fieldName == "license" && strings.ToLower(field.Name) == "license plate") || + (fieldName == "crypto" && strings.ToLower(field.Name) == "crypto address") || + (fieldName == "url" && strings.ToLower(field.Name) == "domain/url") { + fieldsToDisplay = append(fieldsToDisplay, field) + headers = append(headers, field.Name) + break + } + } + } + } else { + // Default fields (first 6) + fieldsToDisplay = allFields[:6] + } + + var rows [][]string + for _, result := range results { + rowValues := []string{} + for _, field := range fieldsToDisplay { + rowValues = append(rowValues, field.Getter(result)) + } + rows = append(rows, rowValues) + } + + pretty.Table(headers, rows) + default: + // Simple output + for i, result := range results { + fmt.Printf("Result %d:\n", i+1) + + // Determine which fields to display + if len(options.DisplayFields) > 0 { + // Display only specified fields + for _, field := range options.DisplayFields { + field = strings.ToLower(strings.TrimSpace(field)) + switch field { + case "username": + fmt.Printf(" Username: %s\n", result.Username) + case "email": + fmt.Printf(" Email: %s\n", result.Email) + case "ip", "ipaddress", "ip_address": + fmt.Printf(" IP Address: %s\n", result.IpAddress) + case "password": + fmt.Printf(" Password: %s\n", result.Password) + case "hash", "hashed_password": + fmt.Printf(" Hashed Password: %s\n", result.HashedPassword) + case "name": + fmt.Printf(" Name: %s\n", result.Name) + case "vin": + fmt.Printf(" VIN: %s\n", result.Vin) + case "license", "license_plate": + fmt.Printf(" License Plate: %s\n", result.LicensePlate) + case "address": + fmt.Printf(" Address: %s\n", result.Address) + case "phone": + fmt.Printf(" Phone: %s\n", result.Phone) + case "social": + fmt.Printf(" Social: %s\n", result.Social) + case "crypto", "cryptocurrency_address": + fmt.Printf(" Crypto Address: %s\n", result.CryptoCurrencyAddress) + case "domain", "url": + fmt.Printf(" Domain/URL: %s\n", result.Url) + } + } + } else { + // Display default fields + fmt.Printf(" Username: %s\n", result.Username) + fmt.Printf(" Email: %s\n", result.Email) + fmt.Printf(" IP Address: %s\n", result.IpAddress) + fmt.Printf(" Password: %s\n", result.Password) + fmt.Printf(" Hashed Password: %s\n", result.HashedPassword) + fmt.Printf(" Name: %s\n", result.Name) + } + fmt.Println() + } + } +} + +// truncate truncates a string to the specified length and adds ellipsis if needed +func truncate(s string, length int) string { + if len(s) <= length { + return s + } + return s[:length-3] + "..." +} + +func arrayToString(a []string) string { + return strings.Join(a, ", ") +} + +// DB runs command +var dbRunsCmd = &cobra.Command{ + Use: "runs", + Short: "Query previous query runs", + Long: `Query the database for previous query runs (QueryOptions) based on date range and query content.`, + Run: func(cmd *cobra.Command, args []string) { + // Call queryRunsTable directly + queryRunsTable() + }, +} + +// DB creds command +var dbCredsCmd = &cobra.Command{ + Use: "creds", + Short: "Query credentials", + Long: `Query the database for credentials based on username, email, and password.`, + Run: func(cmd *cobra.Command, args []string) { + // Call queryCredsTable directly + queryCredsTable(cmd) + }, +} + +// queryCredsTable queries the credentials table +func queryCredsTable(cmd *cobra.Command) { + // Create DBOptions with the provided parameters + options := &sqlite.DBOptions{ + Username: usernameDBQuery, + Email: emailDBQuery, + Password: passwordDBQuery, + Limit: limitResultsDB, + ExactMatch: exactMatchDBQuery, + } + + // Parse non-empty fields if provided + if nonEmptyFieldsDBQuery != "" { + options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",") + } + + // Parse display fields if provided + if displayFieldsDBQuery != "" { + options.DisplayFields = strings.Split(displayFieldsDBQuery, ",") + } + + // Check if at least one search parameter is provided + if options.Username == "" && options.Email == "" && options.Password == "" && len(options.NonEmptyFields) == 0 { + fmt.Println("Error: At least one search parameter is required.") + cmd.Help() + return + } + + // Get the count of matching credentials + count, err := sqlite.GetCredsCount(options) + if err != nil { + fmt.Printf("Error counting credentials: %v\n", err) + return + } + + // Query the database + creds, err := sqlite.QueryCreds(options) + if err != nil { + fmt.Printf("Error querying credentials: %v\n", err) + return + } + + // Display the results + displayCredsResults(count, creds) +} + +// displayCredsResults displays the results from the creds table +func displayCredsResults(count int64, creds []sqlite.Creds) { + // Display the results + fmt.Printf("Found %d credentials (showing %d):\n", count, len(creds)) + + if len(creds) == 0 { + fmt.Println("No credentials found.") + return + } + + // Output results based on format + switch outputFormatDB { + case "json": + data, err := json.MarshalIndent(creds, "", " ") + if err != nil { + fmt.Printf("Error formatting results: %v\n", err) + return + } + fmt.Println(string(data)) + case "table": + // Define all available fields + type FieldInfo struct { + Name string + Getter func(cred sqlite.Creds) string + } + + allFields := []FieldInfo{ + {"ID", func(c sqlite.Creds) string { return fmt.Sprintf("%d", c.ID) }}, + {"Created At", func(c sqlite.Creds) string { return c.CreatedAt.Format("2006-01-02 15:04:05") }}, + {"Email", func(c sqlite.Creds) string { return c.Email }}, + {"Username", func(c sqlite.Creds) string { return c.Username }}, + {"Password", func(c sqlite.Creds) string { return c.Password }}, + } + + // Select fields to display + var fieldsToDisplay []FieldInfo + var headers []string + + if len(displayFieldsDBQuery) > 0 { + // Use specified display fields + displayFields := strings.Split(displayFieldsDBQuery, ",") + for _, fieldName := range displayFields { + fieldName = strings.ToLower(strings.TrimSpace(fieldName)) + for _, field := range allFields { + if strings.ToLower(field.Name) == fieldName { + fieldsToDisplay = append(fieldsToDisplay, field) + headers = append(headers, field.Name) + break + } + } + } + } else { + // Default fields + fieldsToDisplay = allFields + for _, field := range fieldsToDisplay { + headers = append(headers, field.Name) + } + } + + // Create rows + rows := make([][]string, len(creds)) + for i, cred := range creds { + rowValues := []string{} + for _, field := range fieldsToDisplay { + rowValues = append(rowValues, field.Getter(cred)) + } + rows[i] = rowValues + } + + pretty.Table(headers, rows) + default: + // Simple output + for _, cred := range creds { + fmt.Printf("Credential ID: %d\n", cred.ID) + fmt.Printf(" Created At: %s\n", cred.CreatedAt.Format("2006-01-02 15:04:05")) + fmt.Printf(" Email: %s\n", cred.Email) + fmt.Printf(" Username: %s\n", cred.Username) + fmt.Printf(" Password: %s\n", cred.Password) + fmt.Println() + } + } +} diff --git a/cmd/query.go b/cmd/query.go new file mode 100644 index 0000000..cd9860e --- /dev/null +++ b/cmd/query.go @@ -0,0 +1,137 @@ +package cmd + +import ( + "dehasher/internal/badger" + "dehasher/internal/query" + "dehasher/internal/sqlite" + "fmt" + "github.com/spf13/cobra" +) + +var ( + // Query command flags + maxRecords int + maxRequests int + startingPage int + credsOnly bool + printBalance bool + regexMatch bool + wildcardMatch bool + outputFormat string + outputFile string + usernameQuery string + emailQuery string + ipQuery string + passwordQuery string + hashQuery string + nameQuery string + domainQuery string + vinQuery string + licensePlateQuery string + addressQuery string + phoneQuery string + socialQuery string + cryptoCurrencyAddressQuery string + + // Query command + queryCmd = &cobra.Command{ + Use: "query", + Short: "Query the Dehashed API", + Long: `Query the Dehashed API for emails, usernames, passwords, hashes, IP addresses, and names.`, + Run: func(cmd *cobra.Command, args []string) { + // Check if API key and email are provided + key := apiKey + email := apiEmail + + // If not provided as flags, try to get from stored values + if key == "" { + key = getStoredApiKey() + } + if email == "" { + email = getStoredApiEmail() + } + + // Validate credentials + if key == "" || email == "" { + fmt.Println("API key and email are required. Use --key and --email flags or set them with set-key and set-email commands.") + return + } + + // Create new QueryOptions + queryOptions := sqlite.NewQueryOptions( + maxRecords, + maxRequests, + startingPage, + outputFormat, + outputFile, + usernameQuery, + emailQuery, + ipQuery, + passwordQuery, + hashQuery, + nameQuery, + domainQuery, + vinQuery, + licensePlateQuery, + addressQuery, + phoneQuery, + socialQuery, + cryptoCurrencyAddressQuery, + regexMatch, + wildcardMatch, + printBalance, + credsOnly, + ) + + // Create new Dehasher + dehasher := query.NewDehasher(queryOptions) + dehasher.SetClientCredentials( + key, + ) + + // Start querying + dehasher.Start() + fmt.Println("\n[*] Completing Process") + + sqlite.StoreQueryOptions(queryOptions) + }, + } +) + +func init() { + // Add flags specific to query command + queryCmd.Flags().IntVarP(&maxRecords, "max-records", "m", 30000, "Maximum amount of records to return") + queryCmd.Flags().IntVarP(&maxRequests, "max-requests", "r", -1, "Maximum number of requests to make") + queryCmd.Flags().IntVarP(&startingPage, "starting-page", "s", 1, "Starting page for requests") + queryCmd.Flags().BoolVarP(&printBalance, "print-balance", "b", false, "Print remaining balance after requests") + queryCmd.Flags().BoolVarP(®exMatch, "regex-match", "R", false, "Use regex matching on query fields") + queryCmd.Flags().BoolVarP(&wildcardMatch, "wildcard-match", "W", false, "Use wildcard matching on query fields (Use ? to replace a single character, and * for multiple characters)") + queryCmd.Flags().BoolVarP(&credsOnly, "creds-only", "C", false, "Return credentials only") + queryCmd.Flags().StringVarP(&outputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt)") + queryCmd.Flags().StringVarP(&outputFile, "output", "o", "query", "File to output results to including extension") + queryCmd.Flags().StringVarP(&usernameQuery, "username", "U", "", "Username query") + queryCmd.Flags().StringVarP(&emailQuery, "email-query", "E", "", "Email query") + queryCmd.Flags().StringVarP(&ipQuery, "ip", "I", "", "IP address query") + queryCmd.Flags().StringVarP(&domainQuery, "domain", "D", "", "Domain query") + queryCmd.Flags().StringVarP(&passwordQuery, "password", "P", "", "Password query") + queryCmd.Flags().StringVarP(&vinQuery, "vin", "V", "", "VIN query") + queryCmd.Flags().StringVarP(&licensePlateQuery, "license", "L", "", "License plate query") + queryCmd.Flags().StringVarP(&addressQuery, "address", "A", "", "Address query") + queryCmd.Flags().StringVarP(&phoneQuery, "phone", "M", "", "Phone query") + queryCmd.Flags().StringVarP(&socialQuery, "social", "S", "", "Social query") + queryCmd.Flags().StringVarP(&cryptoCurrencyAddressQuery, "crypto", "B", "", "Crypto currency address query") + queryCmd.Flags().StringVarP(&hashQuery, "hash", "Q", "", "Hashed password query") + queryCmd.Flags().StringVarP(&nameQuery, "name", "N", "", "Name query") + + // Add mutually exclusive flags to exact match and regex match + queryCmd.MarkFlagsMutuallyExclusive("regex-match", "wildcard-match") +} + +// Helper functions to get stored API credentials +func getStoredApiKey() string { + return badger.GetKey() +} + +func getStoredApiEmail() string { + return badger.GetEmail() +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..e651346 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,126 @@ +package cmd + +import ( + "dehasher/internal/badger" + "fmt" + "github.com/spf13/cobra" + "go.uber.org/zap" + "os" +) + +var ( + // Global Flags + apiKey string + apiEmail string + + // rootCmd is the base command for the CLI. + rootCmd = &cobra.Command{ + Use: "dehasher", + Short: `Dehasher is a cli tool for querying query.`, + Long: fmt.Sprintf( + "%s\n%s", + ` + ______ _______ _______ _______ _______ _______ +( __ \ ( ____ \|\ /|( ___ )( ____ \|\ /|( ____ \( ____ ) +| ( \ )| ( \/| ) ( || ( ) || ( \/| ) ( || ( \/| ( )| +| | ) || (__ | (___) || (___) || (_____ | (___) || (__ | (____)| +| | | || __) | ___ || ___ |(_____ )| ___ || __) | __) +| | ) || ( | ( ) || ( ) | ) || ( ) || ( | (\ ( +| (__/ )| (____/\| ) ( || ) ( |/\____) || ) ( || (____/\| ) \ \__ +(______/ (_______/|/ \||/ \|\_______)|/ \|(_______/|/ \__/ + An Ar1ste1a Project +`, + `––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•–– + Dehasher can query the query API for: + - Emails - Usernames - Password + - Hashes - IP Addresses - Names + - VINs - License Plates - Addresses + - Phones - Social Media - Crypto Currency Addresses + Dehasher supports: + - Regex Matching + - Exact Matching +––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•–– +`, + ), + Version: "v1.0", + } +) + +// Execute adds all child commands to the root command and sets flags appropriately. +func Execute() { + if err := rootCmd.Execute(); err != nil { + zap.L().Fatal("execute_root_command", + zap.String("message", "failed to execute root command"), + zap.Error(err), + ) + fmt.Printf("[!] %v", err) + os.Exit(1) + } +} + +func init() { + // Hide the default help command + rootCmd.CompletionOptions.HiddenDefaultCmd = true + + // Add global flags for API key and email + rootCmd.PersistentFlags().StringVarP(&apiKey, "key", "k", "", "API Key for authentication") + + // Add subcommands + rootCmd.AddCommand(dbCmd) + rootCmd.AddCommand(queryCmd) + rootCmd.AddCommand(setKeyCmd) + rootCmd.AddCommand(setEmailCmd) +} + +// Command to set API key +var setKeyCmd = &cobra.Command{ + Use: "set-key [key]", + Short: "Set and store API key", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + key := args[0] + // Store key in badger DB + err := storeApiKey(key) + if err != nil { + fmt.Printf("Error storing API key: %v\n", err) + return + } + fmt.Println("API key stored successfully") + }, +} + +// Command to set API email +var setEmailCmd = &cobra.Command{ + Use: "set-email [email]", + Short: "Set and store API email", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + email := args[0] + // Store email in badger DB + err := storeApiEmail(email) + if err != nil { + fmt.Printf("Error storing API email: %v\n", err) + return + } + fmt.Println("API email stored successfully") + }, +} + +// Helper functions to store API credentials +func storeApiKey(key string) error { + err := badger.StoreKey(key) + if err != nil { + fmt.Printf("Error storing API key: %v\n", err) + return err + } + return nil +} + +func storeApiEmail(email string) error { + err := badger.StoreEmail(email) + if err != nil { + fmt.Printf("Error storing API email: %v\n", err) + return err + } + return nil +} diff --git a/cmd/whois.go b/cmd/whois.go new file mode 100644 index 0000000..ef58098 --- /dev/null +++ b/cmd/whois.go @@ -0,0 +1,303 @@ +package cmd + +import ( + "dehasher/internal/sqlite" + "dehasher/internal/whois" + "fmt" + "github.com/spf13/cobra" + "go.uber.org/zap" + "strings" +) + +var ( + // WHOIS command flags + whoisDomain string + whoisIPAddress string + whoisMXAddress string + whoisNSAddress string + whoisInclude string + whoisExclude string + whoisReverseType string + whoisOutputFormat string + whoisShowCredits bool + whoisHistory bool + whoisSubdomainScan bool + + // WHOIS command + whoisCmd = &cobra.Command{ + Use: "whois", + Short: "Dehashed WHOIS lookups and reverse WHOIS searches", + Long: `Perform WHOIS lookups, history searches, reverse WHOIS searches, IP lookups, MX lookups, NS lookups, and subdomain scans.`, + Run: func(cmd *cobra.Command, args []string) { + // Check if API key is provided + key := apiKey + + // If not provided as flag, try to get from stored value + if key == "" { + key = getStoredApiKey() + } + + // Validate credentials + if key == "" { + fmt.Println("API key is required. Use --key flag or set it with set-key command.") + return + } + + // Show credits if requested + if whoisShowCredits { + fmt.Println("[*] Getting WHOIS credits...") + credits, err := whois.GetWHOISCredits(key) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to get whois credits"), + zap.Error(err), + ) + fmt.Printf("Error getting WHOIS credits: %v\n", err) + return + } + fmt.Printf("WHOIS Credits: %d\n", credits.WhoisCredits) + return + } + + // Check if domain is provided for history and subdomain scan + if whoisHistory || whoisSubdomainScan { + if whoisDomain == "" { + fmt.Println("Domain is required for history and subdomain scan.") + return + } + } + + // Determine which operation to perform based on flags + if whoisDomain != "" { + fmt.Println("[*] Performing WHOIS lookup...") + // Domain lookup + result, err := whois.WhoisSearch(whoisDomain, key) + if err != nil { + zap.L().Error("whois_search", + zap.String("message", "failed to perform whois search"), + zap.Error(err), + ) + fmt.Printf("Error performing WHOIS lookup: %v\n", err) + return + } + + // Fix the output format to use proper formatting + fmt.Printf("WHOIS Lookup Result:\n%+v\n", result.Data.WhoisRecord) + + // Store the record + err = sqlite.StoreWhoisRecord(result.Data.WhoisRecord) + if err != nil { + zap.L().Error("store_whois_record", + zap.String("message", "failed to store whois record"), + zap.Error(err), + ) + fmt.Printf("Error storing WHOIS record: %v\n", err) + // Continue execution even if storage fails + } + + if whoisHistory { + fmt.Println("[*] Performing WHOIS history search...") + // Perform history search + history, err := whois.WhoisHistory(whoisDomain, key) + if err != nil { + zap.L().Error("whois_history", + zap.String("message", "failed to perform whois history lookup"), + zap.Error(err), + ) + fmt.Printf("Error performing WHOIS history lookup: %v\n", err) + } else { + fmt.Println("\nWHOIS History:") + fmt.Println(history) + } + + err = sqlite.StoreHistoryRecord(history.Data.Records) + if err != nil { + zap.L().Error("store_history_record", + zap.String("message", "failed to store history record"), + zap.Error(err), + ) + fmt.Printf("Error storing WHOIS history record: %v\n", err) + } + } + + // Perform subdomain scan + if whoisSubdomainScan { + fmt.Println("[*] Performing WHOIS subdomain scan...") + subdomains, err := whois.WhoisSubdomainScan(whoisDomain, key) + if err != nil { + zap.L().Error("whois_subdomain_scan", + zap.String("message", "failed to perform subdomain scan"), + zap.Error(err), + ) + fmt.Printf("Error performing subdomain scan: %v\n", err) + } else { + fmt.Println("\nSubdomain Scan:") + fmt.Println(subdomains) + } + + err = sqlite.StoreSubdomainRecord(subdomains.Data.Result.Records) + if err != nil { + zap.L().Error("store_subdomain_record", + zap.String("message", "failed to store subdomain record"), + zap.Error(err), + ) + fmt.Printf("Error storing WHOIS subdomain record: %v\n", err) + } + } + + // Get credits + credits, err := whois.GetWHOISCredits(key) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to get whois credits"), + zap.Error(err), + ) + fmt.Printf("Error getting WHOIS credits: %v\n", err) + return + } + fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits) + return + } + + if whoisIPAddress != "" { + fmt.Println("[*] Performing reverse IP lookup...") + // IP lookup + result, err := whois.WhoisIP(whoisIPAddress, key) + if err != nil { + zap.L().Error("whois_ip", + zap.String("message", "failed to perform ip lookup"), + zap.Error(err), + ) + fmt.Printf("Error performing IP lookup: %v\n", err) + return + } + fmt.Println("IP Lookup Result:") + fmt.Println(string(result)) + + // Get credits + credits, err := whois.GetWHOISCredits(key) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to get whois credits"), + zap.Error(err), + ) + fmt.Printf("Error getting WHOIS credits: %v\n", err) + return + } + fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits) + return + } + + if whoisMXAddress != "" { + fmt.Println("[*] Performing reverse MX lookup...") + // MX lookup + result, err := whois.WhoisMX(whoisMXAddress, key) + if err != nil { + zap.L().Error("whois_mx", + zap.String("message", "failed to perform mx lookup"), + zap.Error(err), + ) + fmt.Printf("Error performing MX lookup: %v\n", err) + return + } + + // todo unmarshal mx lookup + fmt.Println("MX Lookup Result:") + fmt.Println(result) + + // Get credits + credits, err := whois.GetWHOISCredits(key) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to get whois credits"), + zap.Error(err), + ) + fmt.Printf("Error getting WHOIS credits: %v\n", err) + return + } + fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits) + return + } + + if whoisNSAddress != "" { + fmt.Println("[*] Performing reverse NS lookup...") + // NS lookup + result, err := whois.WhoisNS(whoisNSAddress, key) + if err != nil { + zap.L().Error("whois_ns", + zap.String("message", "failed to perform ns lookup"), + zap.Error(err), + ) + fmt.Printf("Error performing NS lookup: %v\n", err) + return + } + fmt.Println("NS Lookup Result:") + fmt.Println(result) + + // Get credits + credits, err := whois.GetWHOISCredits(key) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to get whois credits"), + zap.Error(err), + ) + fmt.Printf("Error getting WHOIS credits: %v\n", err) + return + } + fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits) + return + } + + if whoisInclude != "" || whoisExclude != "" { + // Reverse WHOIS + includeTerms := []string{} + if whoisInclude != "" { + includeTerms = strings.Split(whoisInclude, ",") + } + + excludeTerms := []string{} + if whoisExclude != "" { + excludeTerms = strings.Split(whoisExclude, ",") + } + + if whoisReverseType == "" { + whoisReverseType = "registrant" + } + + fmt.Println("[*] Performing reverse WHOIS lookup...") + result, err := whois.ReverseWHOIS(includeTerms, excludeTerms, whoisReverseType, key) + if err != nil { + fmt.Printf("Error performing reverse WHOIS: %v\n", err) + return + } + fmt.Println("Reverse WHOIS Result:") + fmt.Println(result) + return + } + + // If no specific operation was requested + cmd.Help() + }, + } +) + +func init() { + // Add whois command to root command + rootCmd.AddCommand(whoisCmd) + + // Add flags specific to whois command + whoisCmd.Flags().StringVarP(&whoisDomain, "domain", "d", "", "Domain for WHOIS lookup, history search, and subdomain scan") + whoisCmd.Flags().StringVarP(&whoisIPAddress, "ip", "i", "", "IP address for reverse IP lookup") + whoisCmd.Flags().StringVarP(&whoisMXAddress, "mx", "m", "", "MX address for reverse MX lookup") + whoisCmd.Flags().StringVarP(&whoisNSAddress, "ns", "n", "", "NS address for reverse NS lookup") + whoisCmd.Flags().StringVarP(&whoisInclude, "include", "I", "", "Terms to include in reverse WHOIS search (comma-separated)") + whoisCmd.Flags().StringVarP(&whoisExclude, "exclude", "E", "", "Terms to exclude in reverse WHOIS search (comma-separated)") + whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "registrant", "Type of reverse WHOIS search (registrant, email, organization, address, phone)") + whoisCmd.Flags().StringVarP(&whoisOutputFormat, "format", "f", "text", "Output format (text, json)") + whoisCmd.Flags().BoolVarP(&whoisShowCredits, "credits", "c", false, "Show remaining WHOIS credits") + whoisCmd.Flags().BoolVarP(&whoisHistory, "history", "H", false, "Perform WHOIS history search [25 Credits]") + whoisCmd.Flags().BoolVarP(&whoisSubdomainScan, "subdomains", "s", false, "Perform WHOIS subdomain scan") + + // Add API key flag + whoisCmd.Flags().StringVarP(&apiKey, "key", "k", "", "Dehashed API key") +} diff --git a/counseltrust.txt b/counseltrust.txt new file mode 100644 index 0000000..6430328 --- /dev/null +++ b/counseltrust.txt @@ -0,0 +1,235 @@ +[*] Performing WHOIS lookup... +WHOIS Lookup Result: %v + {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} {2025-03-10 15:29:09 UTC 2025-03-10 15:29:09 UTC} abuse@godaddy.com 2001-09-23T21:20:52Z 2001-09-23 21:20:52 UTC counseltrust.com .com 8633 2025-09-23T21:20:52Z 2025-09-23 21:20:52 UTC {[NS51.DOMAINCONTROL.COM NS52.DOMAINCONTROL.COM] [] NS51.DOMAINCONTROL.COM +NS52.DOMAINCONTROL.COM +} 3259 Domain Name: counseltrust.com +Registry Domain ID: 77675771_DOMAIN_COM-VRSN +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: https://www.godaddy.com +Updated Date: 2024-09-24T12:17:56Z +Creation Date: 2001-09-23T21:20:52Z +Registrar Registration Expiration Date: 2025-09-23T21:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: +1.4806242505 +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Registry Registrant ID: Not Available From Registry +Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Phone Ext: +Registrant Fax: +Registrant Fax Ext: +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Registry Tech ID: Not Available From Registry +Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Phone Ext: +Tech Fax: +Tech Fax Ext: +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM +DNSSEC: unsigned +URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/ +>>> Last update of WHOIS database: 2025-03-10T15:29:08Z <<< +For more information on Whois status codes, please visit https://icann.org/epp + +TERMS OF USE: The data contained in this registrar's Whois database, while believed by the +registrar to be reliable, is provided "as is" with no guarantee or warranties regarding its +accuracy. This information is provided for the sole purpose of assisting you in obtaining +information about domain name registration records. Any use of this data for any other purpose +is expressly forbidden without the prior written permission of this registrar. By submitting +an inquiry, you agree to these terms and limitations of warranty. In particular, you agree not +to use this data to allow, enable, or otherwise support the dissemination or collection of this +data, in part or in its entirety, for any purpose, such as transmission by e-mail, telephone, +postal mail, facsimile or other means of mass unsolicited, commercial advertising or solicitations +of any kind, including spam. You further agree not to use this data to enable high volume, automated +or robotic electronic processes designed to collect or compile this data for any purpose, including +mining this data for your own personal or commercial purposes. Failure to comply with these terms +may result in termination of access to the Whois database. These terms may be subject to modification +at any time without notice. + +**NOTICE** This WHOIS server is being retired. Please use our RDAP service instead. {Tempe UNITED STATES US Registration Private Domains By Proxy, LLC 85281 Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com Arizona DomainsByProxy.com +100 S. Mill Ave, Suite 1600 14806242599} 146 GoDaddy.com, LLC {{2025-03-10 15:29:04 UTC 2025-03-10 15:29:04 UTC} 2001-09-24T02:20:52Z 2001-09-24 02:20:52 UTC counseltrust.com 2025-09-24T02:20:52Z 2025-09-24 02:20:52 UTC {[NS51.DOMAINCONTROL.COM NS52.DOMAINCONTROL.COM] [] NS51.DOMAINCONTROL.COM +NS52.DOMAINCONTROL.COM +} 251 Domain Name: COUNSELTRUST.COM + Registry Domain ID: 77675771_DOMAIN_COM-VRSN + Registrar WHOIS Server: whois.godaddy.com + Registrar URL: http://www.godaddy.com + Updated Date: 2024-09-24T17:17:57Z + Creation Date: 2001-09-24T02:20:52Z + Registry Expiry Date: 2025-09-24T02:20:52Z + Registrar: GoDaddy.com, LLC + Registrar IANA ID: 146 + Registrar Abuse Contact Email: abuse@godaddy.com + Registrar Abuse Contact Phone: 480-624-2505 + Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited + Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited + Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited + Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited + Name Server: NS51.DOMAINCONTROL.COM + Name Server: NS52.DOMAINCONTROL.COM + DNSSEC: unsigned + URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/ +>>> Last update of whois database: 2025-03-10T15:28:57Z <<< + +For more information on Whois status codes, please visit https://icann.org/epp + +NOTICE: The expiration date displayed in this record is the date the +registrar's sponsorship of the domain name registration in the registry is +currently set to expire. This date does not necessarily reflect the expiration +date of the domain name registrant's agreement with the sponsoring +registrar. Users may consult the sponsoring registrar's Whois database to +view the registrar's reported date of expiration for this registration. + +TERMS OF USE: You are not authorized to access or query our Whois +database through the use of electronic processes that are high-volume and +automated except as reasonably necessary to register domain names or +modify existing registrations; the Data in VeriSign Global Registry +Services' ("VeriSign") Whois database is provided by VeriSign for +information purposes only, and to assist persons in obtaining information +about or related to a domain name registration record. VeriSign does not +guarantee its accuracy. By submitting a Whois query, you agree to abide +by the following terms of use: You agree that you may use this Data only +for lawful purposes and that under no circumstances will you use this Data +to: (1) allow, enable, or otherwise support the transmission of mass +unsolicited, commercial advertising or solicitations via e-mail, telephone, +or facsimile; or (2) enable high volume, automated, electronic processes +that apply to VeriSign (or its computer systems). The compilation, +repackaging, dissemination or other use of this Data is expressly +prohibited without the prior written consent of VeriSign. You agree not to +use electronic processes that are automated and high-volume to access or +query the Whois database except as reasonably necessary to register +domain names or modify existing registrations. VeriSign reserves the right +to restrict your access to the Whois database in its sole discretion to ensure +operational stability. VeriSign may restrict or terminate your access to the +Whois database for failure to abide by these terms of use. VeriSign +reserves the right to modify these terms at any time. + +The Registry database contains ONLY .COM, .NET, .EDU domains and +Registrars. 146 GoDaddy.com, LLC clientDeleteProhibited clientRenewProhibited clientTransferProhibited clientUpdateProhibited Domain Name: COUNSELTRUST.COM +Registry Domain ID: 77675771_DOMAIN_COM-VRSN +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: http://www.godaddy.com +Updated Date: 2024-09-24T17:17:57Z +Creation Date: 2001-09-24T02:20:52Z +Registry Expiry Date: 2025-09-24T02:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: 480-624-2505 +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM +DNSSEC: unsigned +URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/ +>>> Last update of whois database: 2025-03-10T15:28:57Z <<< +For more information on Whois status codes, please visit https://icann.org/epp +NOTICE: The expiration date displayed in this record is the date the +registrar's sponsorship of the domain name registration in the registry is +currently set to expire. This date does not necessarily reflect the expiration +date of the domain name registrant's agreement with the sponsoring +registrar. Users may consult the sponsoring registrar's Whois database to +view the registrar's reported date of expiration for this registration. +TERMS OF USE: You are not authorized to access or query our Whois +database through the use of electronic processes that are high-volume and +automated except as reasonably necessary to register domain names or +modify existing registrations; the Data in VeriSign Global Registry +Services' ("VeriSign") Whois database is provided by VeriSign for +information purposes only, and to assist persons in obtaining information +about or related to a domain name registration record. VeriSign does not +guarantee its accuracy. By submitting a Whois query, you agree to abide +by the following terms of use: You agree that you may use this Data only +for lawful purposes and that under no circumstances will you use this Data +to: (1) allow, enable, or otherwise support the transmission of mass +unsolicited, commercial advertising or solicitations via e-mail, telephone, +or facsimile; or (2) enable high volume, automated, electronic processes +that apply to VeriSign (or its computer systems). The compilation, +repackaging, dissemination or other use of this Data is expressly +prohibited without the prior written consent of VeriSign. You agree not to +use electronic processes that are automated and high-volume to access or +query the Whois database except as reasonably necessary to register +domain names or modify existing registrations. VeriSign reserves the right +to restrict your access to the Whois database in its sole discretion to ensure +operational stability. VeriSign may restrict or terminate your access to the +Whois database for failure to abide by these terms of use. VeriSign +reserves the right to modify these terms at any time. +The Registry database contains ONLY .COM, .NET, .EDU domains and +Registrars. + 2024-09-24T17:17:57Z 2024-09-24 17:17:57 UTC whois.godaddy.com} clientTransferProhibited clientUpdateProhibited clientRenewProhibited clientDeleteProhibited Domain Name: counseltrust.com +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: https://www.godaddy.com +Updated Date: 2024-09-24T12:17:56Z +Creation Date: 2001-09-23T21:20:52Z +Registrar Registration Expiration Date: 2025-09-23T21:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: +1.4806242505 +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM + {Tempe UNITED STATES US Registration Private Domains By Proxy, LLC 85281 Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com Arizona DomainsByProxy.com +100 S. Mill Ave, Suite 1600 14806242599} 2024-09-24T12:17:56Z 2024-09-24 12:17:56 UTC} diff --git a/counseltrust2.txt b/counseltrust2.txt new file mode 100644 index 0000000..4efe188 --- /dev/null +++ b/counseltrust2.txt @@ -0,0 +1,241 @@ +[*] Performing WHOIS lookup... +WHOIS Lookup Result: +{Model:{ID:0 CreatedAt:0001-01-01 00:00:00 +0000 UTC UpdatedAt:0001-01-01 00:00:00 +0000 UTC DeletedAt:{Time:0001-01-01 00:00:00 +0000 UTC Valid:false}} Audit:{CreatedDate:2025-03-10 15:29:09 UTC UpdatedDate:2025-03-10 15:29:09 UTC} ContactEmail:abuse@godaddy.com CreatedDate:2001-09-23T21:20:52Z CreatedDateNormalized:2001-09-23 21:20:52 UTC DomainName:counseltrust.com DomainNameExt:.com EstimatedDomainAge:8633 ExpiresDate:2025-09-23T21:20:52Z ExpiresDateNormalized:2025-09-23 21:20:52 UTC Footer: Header: NameServers:{HostNames:[NS51.DOMAINCONTROL.COM NS52.DOMAINCONTROL.COM] IPs:[] RawText:NS51.DOMAINCONTROL.COM +NS52.DOMAINCONTROL.COM +} ParseCode:3259 RawText:Domain Name: counseltrust.com +Registry Domain ID: 77675771_DOMAIN_COM-VRSN +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: https://www.godaddy.com +Updated Date: 2024-09-24T12:17:56Z +Creation Date: 2001-09-23T21:20:52Z +Registrar Registration Expiration Date: 2025-09-23T21:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: +1.4806242505 +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Registry Registrant ID: Not Available From Registry +Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Phone Ext: +Registrant Fax: +Registrant Fax Ext: +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Registry Tech ID: Not Available From Registry +Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Phone Ext: +Tech Fax: +Tech Fax Ext: +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM +DNSSEC: unsigned +URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/ +>>> Last update of WHOIS database: 2025-03-10T15:29:08Z <<< +For more information on Whois status codes, please visit https://icann.org/epp + +TERMS OF USE: The data contained in this registrar's Whois database, while believed by the +registrar to be reliable, is provided "as is" with no guarantee or warranties regarding its +accuracy. This information is provided for the sole purpose of assisting you in obtaining +information about domain name registration records. Any use of this data for any other purpose +is expressly forbidden without the prior written permission of this registrar. By submitting +an inquiry, you agree to these terms and limitations of warranty. In particular, you agree not +to use this data to allow, enable, or otherwise support the dissemination or collection of this +data, in part or in its entirety, for any purpose, such as transmission by e-mail, telephone, +postal mail, facsimile or other means of mass unsolicited, commercial advertising or solicitations +of any kind, including spam. You further agree not to use this data to enable high volume, automated +or robotic electronic processes designed to collect or compile this data for any purpose, including +mining this data for your own personal or commercial purposes. Failure to comply with these terms +may result in termination of access to the Whois database. These terms may be subject to modification +at any time without notice. + +**NOTICE** This WHOIS server is being retired. Please use our RDAP service instead. Registrant:{City:Tempe Country:UNITED STATES CountryCode:US Name:Registration Private Organization:Domains By Proxy, LLC PostalCode:85281 RawText:Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com State:Arizona Street1:DomainsByProxy.com +100 S. Mill Ave, Suite 1600 Telephone:14806242599} RegistrarIANAID:146 RegistrarName:GoDaddy.com, LLC RegistryData:{Audit:{CreatedDate:2025-03-10 15:29:04 UTC UpdatedDate:2025-03-10 15:29:04 UTC} CreatedDate:2001-09-24T02:20:52Z CreatedDateNormalized:2001-09-24 02:20:52 UTC DomainName:counseltrust.com ExpiresDate:2025-09-24T02:20:52Z ExpiresDateNormalized:2025-09-24 02:20:52 UTC Footer: Header: NameServers:{HostNames:[NS51.DOMAINCONTROL.COM NS52.DOMAINCONTROL.COM] IPs:[] RawText:NS51.DOMAINCONTROL.COM +NS52.DOMAINCONTROL.COM +} ParseCode:251 RawText:Domain Name: COUNSELTRUST.COM + Registry Domain ID: 77675771_DOMAIN_COM-VRSN + Registrar WHOIS Server: whois.godaddy.com + Registrar URL: http://www.godaddy.com + Updated Date: 2024-09-24T17:17:57Z + Creation Date: 2001-09-24T02:20:52Z + Registry Expiry Date: 2025-09-24T02:20:52Z + Registrar: GoDaddy.com, LLC + Registrar IANA ID: 146 + Registrar Abuse Contact Email: abuse@godaddy.com + Registrar Abuse Contact Phone: 480-624-2505 + Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited + Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited + Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited + Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited + Name Server: NS51.DOMAINCONTROL.COM + Name Server: NS52.DOMAINCONTROL.COM + DNSSEC: unsigned + URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/ +>>> Last update of whois database: 2025-03-10T15:28:57Z <<< + +For more information on Whois status codes, please visit https://icann.org/epp + +NOTICE: The expiration date displayed in this record is the date the +registrar's sponsorship of the domain name registration in the registry is +currently set to expire. This date does not necessarily reflect the expiration +date of the domain name registrant's agreement with the sponsoring +registrar. Users may consult the sponsoring registrar's Whois database to +view the registrar's reported date of expiration for this registration. + +TERMS OF USE: You are not authorized to access or query our Whois +database through the use of electronic processes that are high-volume and +automated except as reasonably necessary to register domain names or +modify existing registrations; the Data in VeriSign Global Registry +Services' ("VeriSign") Whois database is provided by VeriSign for +information purposes only, and to assist persons in obtaining information +about or related to a domain name registration record. VeriSign does not +guarantee its accuracy. By submitting a Whois query, you agree to abide +by the following terms of use: You agree that you may use this Data only +for lawful purposes and that under no circumstances will you use this Data +to: (1) allow, enable, or otherwise support the transmission of mass +unsolicited, commercial advertising or solicitations via e-mail, telephone, +or facsimile; or (2) enable high volume, automated, electronic processes +that apply to VeriSign (or its computer systems). The compilation, +repackaging, dissemination or other use of this Data is expressly +prohibited without the prior written consent of VeriSign. You agree not to +use electronic processes that are automated and high-volume to access or +query the Whois database except as reasonably necessary to register +domain names or modify existing registrations. VeriSign reserves the right +to restrict your access to the Whois database in its sole discretion to ensure +operational stability. VeriSign may restrict or terminate your access to the +Whois database for failure to abide by these terms of use. VeriSign +reserves the right to modify these terms at any time. + +The Registry database contains ONLY .COM, .NET, .EDU domains and +Registrars. RegistrarIANAID:146 RegistrarName:GoDaddy.com, LLC Status:clientDeleteProhibited clientRenewProhibited clientTransferProhibited clientUpdateProhibited StrippedText:Domain Name: COUNSELTRUST.COM +Registry Domain ID: 77675771_DOMAIN_COM-VRSN +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: http://www.godaddy.com +Updated Date: 2024-09-24T17:17:57Z +Creation Date: 2001-09-24T02:20:52Z +Registry Expiry Date: 2025-09-24T02:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: 480-624-2505 +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM +DNSSEC: unsigned +URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/ +>>> Last update of whois database: 2025-03-10T15:28:57Z <<< +For more information on Whois status codes, please visit https://icann.org/epp +NOTICE: The expiration date displayed in this record is the date the +registrar's sponsorship of the domain name registration in the registry is +currently set to expire. This date does not necessarily reflect the expiration +date of the domain name registrant's agreement with the sponsoring +registrar. Users may consult the sponsoring registrar's Whois database to +view the registrar's reported date of expiration for this registration. +TERMS OF USE: You are not authorized to access or query our Whois +database through the use of electronic processes that are high-volume and +automated except as reasonably necessary to register domain names or +modify existing registrations; the Data in VeriSign Global Registry +Services' ("VeriSign") Whois database is provided by VeriSign for +information purposes only, and to assist persons in obtaining information +about or related to a domain name registration record. VeriSign does not +guarantee its accuracy. By submitting a Whois query, you agree to abide +by the following terms of use: You agree that you may use this Data only +for lawful purposes and that under no circumstances will you use this Data +to: (1) allow, enable, or otherwise support the transmission of mass +unsolicited, commercial advertising or solicitations via e-mail, telephone, +or facsimile; or (2) enable high volume, automated, electronic processes +that apply to VeriSign (or its computer systems). The compilation, +repackaging, dissemination or other use of this Data is expressly +prohibited without the prior written consent of VeriSign. You agree not to +use electronic processes that are automated and high-volume to access or +query the Whois database except as reasonably necessary to register +domain names or modify existing registrations. VeriSign reserves the right +to restrict your access to the Whois database in its sole discretion to ensure +operational stability. VeriSign may restrict or terminate your access to the +Whois database for failure to abide by these terms of use. VeriSign +reserves the right to modify these terms at any time. +The Registry database contains ONLY .COM, .NET, .EDU domains and +Registrars. + UpdatedDate:2024-09-24T17:17:57Z UpdatedDateNormalized:2024-09-24 17:17:57 UTC WhoisServer:whois.godaddy.com} Status:clientTransferProhibited clientUpdateProhibited clientRenewProhibited clientDeleteProhibited StrippedText:Domain Name: counseltrust.com +Registrar WHOIS Server: whois.godaddy.com +Registrar URL: https://www.godaddy.com +Updated Date: 2024-09-24T12:17:56Z +Creation Date: 2001-09-23T21:20:52Z +Registrar Registration Expiration Date: 2025-09-23T21:20:52Z +Registrar: GoDaddy.com, LLC +Registrar IANA ID: 146 +Registrar Abuse Contact Email: abuse@godaddy.com +Registrar Abuse Contact Phone: +1.4806242505 +Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited +Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited +Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited +Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited +Registrant Name: Registration Private +Registrant Organization: Domains By Proxy, LLC +Registrant Street: DomainsByProxy.com +Registrant Street: 100 S. Mill Ave, Suite 1600 +Registrant City: Tempe +Registrant State/Province: Arizona +Registrant Postal Code: 85281 +Registrant Country: US +Registrant Phone: +1.4806242599 +Registrant Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com +Name Server: NS51.DOMAINCONTROL.COM +Name Server: NS52.DOMAINCONTROL.COM + TechnicalContact:{City:Tempe Country:UNITED STATES CountryCode:US Name:Registration Private Organization:Domains By Proxy, LLC PostalCode:85281 RawText:Tech Name: Registration Private +Tech Organization: Domains By Proxy, LLC +Tech Street: DomainsByProxy.com +Tech Street: 100 S. Mill Ave, Suite 1600 +Tech City: Tempe +Tech State/Province: Arizona +Tech Postal Code: 85281 +Tech Country: US +Tech Phone: +1.4806242599 +Tech Email: Select Contact Domain Holder link at https://www.godaddy.com/whois/results.aspx?domain=counseltrust.com State:Arizona Street1:DomainsByProxy.com +100 S. Mill Ave, Suite 1600 Telephone:14806242599} UpdatedDate:2024-09-24T12:17:56Z UpdatedDateNormalized:2024-09-24 12:17:56 UTC} +[*] Performing WHOIS subdomain scan... + +Subdomain Scan: +{220 {{13 [{{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} cpanel.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} cpcalendars.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} sslvpn.counseltrust.com 1546654106 1546654106} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} stocks.counseltrust.com 1546654702 1546654702} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} cpcontacts.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} ftp.counseltrust.com 1737657659 1737657659} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} mail.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} webmail.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} webdisk.counseltrust.com 1621908519 1621908519} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} autodiscover.counseltrust.com 1743219849 1745735288} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} www.laserfiche.counseltrust.com 1649336639 1712556962} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} laserfiche.counseltrust.com 1565348884 1712556962} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} www.counseltrust.com 1517184000 1745798400}]} counseltrust.com}} + +WHOIS Credits Remaining: 220 diff --git a/dehasher b/dehasher new file mode 100755 index 0000000..4490642 Binary files /dev/null and b/dehasher differ diff --git a/dehasher.go b/dehasher.go new file mode 100644 index 0000000..d9900fb --- /dev/null +++ b/dehasher.go @@ -0,0 +1,94 @@ +package main + +import ( + "dehasher/cmd" + "dehasher/internal/badger" + "dehasher/internal/sqlite" + "fmt" + "github.com/winking324/rzap" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" + "os" + "path/filepath" +) + +var ( + basePath string + logPath string + storePath string + dbPath string +) + +func init() { + basePath = filepath.Join(os.Getenv("HOME"), ".local", "share", "Dehasher") + logPath = filepath.Join(basePath, "logs") + storePath = filepath.Join(basePath, "keystore") + dbPath = filepath.Join(basePath, "db") +} + +func createDirectories() { + var err error + + if _, err = os.Stat(basePath); os.IsNotExist(err) { + err = os.MkdirAll(basePath, 0755) + if err != nil { + zap.L().Error("Error creating directory", zap.Error(err)) + fmt.Printf("[!] Error creating base directory: %v", err) + os.Exit(-1) + } + } + + for _, dir := range []string{"logs", "keystore", "db"} { + if _, err := os.Stat(filepath.Join(basePath, dir)); os.IsNotExist(err) { + err = os.MkdirAll(filepath.Join(basePath, dir), 0755) + if err != nil { + zap.L().Error("Error creating directory", zap.Error(err), zap.String("directory", dir)) + fmt.Printf("[!] Error creating directory: %v", err) + os.Exit(-1) + } + } + } +} + +func initializeLogger() { + rzap.NewGlobalLogger([]zapcore.Core{ + rzap.NewCore(&lumberjack.Logger{ + Filename: filepath.Join(logPath, "info.log"), + }, zap.LevelEnablerFunc(func(level zapcore.Level) bool { + return level <= zap.InfoLevel + })), + rzap.NewCore(&lumberjack.Logger{ + Filename: filepath.Join(logPath, "error.log"), + }, zap.LevelEnablerFunc(func(level zapcore.Level) bool { + return level > zap.InfoLevel + })), + }) + + zap.L().Info("some message", zap.Int("status", 0)) +} + +func main() { + initializeLogger() + + zap.L().Info("creating_directories") + createDirectories() + + zap.L().Info("initializing_database") + _, err := sqlite.InitDB(dbPath) + if err != nil { + zap.L().Error("init_db", + zap.String("message", "failed to initialize database"), + zap.Error(err), + ) + fmt.Printf("[!] Error initializing database: %v", err) + os.Exit(1) + } + + zap.L().Info("starting_badger") + db := badger.Start(storePath) + defer db.Close() + + zap.L().Info("executing_command") + cmd.Execute() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fa96d9c --- /dev/null +++ b/go.mod @@ -0,0 +1,54 @@ +module dehasher + +go 1.23.0 + +toolchain go1.24.3 + +require ( + github.com/charmbracelet/lipgloss v1.1.0 + github.com/dgraph-io/badger/v4 v4.7.0 + github.com/spf13/cobra v1.9.1 + github.com/winking324/rzap v0.1.0 + go.uber.org/zap v1.20.0 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 + gopkg.in/yaml.v3 v3.0.1 + gorm.io/driver/sqlite v1.5.7 + gorm.io/gorm v1.26.1 +) + +require ( + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d1c0bf1 --- /dev/null +++ b/go.sum @@ -0,0 +1,160 @@ +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y= +github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/winking324/rzap v0.1.0 h1:otHb5JwO2l9Alr1MPeww8FAzF+YcmiZMR0EvBZnmlqo= +github.com/winking324/rzap v0.1.0/go.mod h1:C7Ui70QKYWiN5h4Qk2U0qaTN5yC3ujpKsRgHcXsFWhI= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc= +go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= +gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= +gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/internal/badger/badger.go b/internal/badger/badger.go new file mode 100644 index 0000000..8452fef --- /dev/null +++ b/internal/badger/badger.go @@ -0,0 +1,174 @@ +package badger + +import ( + "crypto/sha256" + "github.com/dgraph-io/badger/v4" + "go.uber.org/zap" + "log" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "sync" +) + +var ( + encryptionKey []byte // must be 32 bytes + db *badger.DB + rootDir string + once sync.Once +) + +func GetHardwareEntropy() []byte { + // Get hostname + hostname, err := os.Hostname() + if err != nil { + hostname = "unknown-host" + log.Printf("Error getting hostname: %v", err) + } + + // Get username + currentUser, err := user.Current() + username := "unknown-user" + if err == nil && currentUser != nil { + username = currentUser.Username + } + + // Get OS and architecture info + osInfo := runtime.GOOS + "-" + runtime.GOARCH + + // Combine all information for a unique but consistent fingerprint + fingerprint := strings.Join([]string{ + hostname, + username, + osInfo, + // You could add a static salt here for additional security + "Dehasher-static-salt-value", + }, ":") + + // Hash the fingerprint to get a 32-byte key + sum := sha256.Sum256([]byte(fingerprint)) + return sum[:] +} + +func Start(dirPath string) *badger.DB { + var err error + + zap.L().Info("Starting Badger DB", zap.String("directory", dirPath)) + zap.L().Info("Badger DB Directory Path", zap.String("directory", dirPath)) + + once.Do(func() { + if !strings.HasSuffix(dirPath, "db") { + dirPath = filepath.Join(dirPath, "db") + } + rootDir = dirPath + + encryptionKey = GetHardwareEntropy() + if err != nil { + zap.L().Fatal("get_encryption_key", + zap.String("message", "failed to get encryption key"), + zap.Error(err), + ) + } + + badgerDB := filepath.Join(rootDir, "badger.db") + opts := badger.DefaultOptions(badgerDB). + WithEncryptionKey(encryptionKey). + WithIndexCacheSize(10 << 20). // 10MB + WithLoggingLevel(badger.ERROR) + db, err = badger.Open(opts) + if err != nil { + zap.L().Fatal("new_badger_db", + zap.String("message", "failed to open badger database"), + zap.Error(err), + ) + } + }) + + return db +} + +func Close() { + err := db.Close() + if err != nil { + zap.L().Fatal("new_badger_db", + zap.String("message", "failed to close badger database"), + zap.Error(err), + ) + } +} + +func GetKey() string { + var apiKey string + + err := db.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("cfg:api_key")) + if err != nil { + return err // could be ErrKeyNotFound + } + return item.Value(func(val []byte) error { + apiKey = string(val) + return nil + }) + }) + + if err != nil { + zap.L().Error("get_api_key", + zap.String("message", "failed to get api_key"), + zap.Error(err), + ) + } + + return apiKey +} + +func GetEmail() string { + var email string + + err := db.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("cfg:email")) + if err != nil { + return err // could be ErrKeyNotFound + } + return item.Value(func(val []byte) error { + email = string(val) + return nil + }) + }) + + if err != nil { + zap.L().Error("get_email", + zap.String("message", "failed to get email"), + zap.Error(err), + ) + } + + return email +} + +func StoreKey(apiKey string) error { + err := db.Update(func(txn *badger.Txn) error { + return txn.Set([]byte("cfg:api_key"), []byte(apiKey)) + }) + if err != nil { + zap.L().Error("set_api_key", + zap.String("message", "failed to set api_key"), + zap.Error(err), + ) + } + return err +} + +func StoreEmail(email string) error { + err := db.Update(func(txn *badger.Txn) error { + return txn.Set([]byte("cfg:email"), []byte(email)) + }) + if err != nil { + zap.L().Error("set_email", + zap.String("message", "failed to set email"), + zap.Error(err), + ) + } + return err +} diff --git a/internal/export/export.go b/internal/export/export.go new file mode 100644 index 0000000..17fb675 --- /dev/null +++ b/internal/export/export.go @@ -0,0 +1,77 @@ +package export + +import ( + "dehasher/internal/files" + "dehasher/internal/sqlite" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "gopkg.in/yaml.v3" + "io/ioutil" + "os" + "strings" +) + +func WriteCredsToFile(creds []sqlite.Creds, outputFile string, fileType files.FileType) error { + var data []byte + var err error + + switch fileType { + case files.JSON: + data, err = json.MarshalIndent(creds, "", " ") + case files.XML: + data, err = xml.MarshalIndent(creds, "", " ") + case files.YAML: + data, err = yaml.Marshal(creds) + case files.TEXT: + var outStrings []string + for _, c := range creds { + outStrings = append(outStrings, c.ToString()+"\n") + } + data = []byte(strings.Join(outStrings, "")) + default: + return errors.New("unsupported file type") + } + + if err != nil { + return err + } + + filePath := fmt.Sprintf("%s.%s", outputFile, fileType.String()) + return os.WriteFile(filePath, data, 0644) +} + +func WriteToFile(results sqlite.DehashedResults, outputFile string, fileType files.FileType) error { + var data []byte + var err error + + result := results.Results + + switch fileType { + case files.JSON: + data, err = json.MarshalIndent(result, "", " ") + case files.XML: + data, err = xml.MarshalIndent(result, "", " ") + case files.YAML: + data, err = yaml.Marshal(result) + case files.TEXT: + var outStrings []string + for _, r := range result { + out := fmt.Sprintf( + "Id: %s\nEmail: %s\nIpAddress: %s\nUsername: %s\nPassword: %s\nHashedPassword: %s\nHashType: %s\nName: %s\nVin: %s\nAddress: %s\nPhone: %s\nDatabaseName: %s\n\n", + r.DehashedId, r.Email, r.IpAddress, r.Username, r.Password, r.HashedPassword, r.HashType, r.Name, r.Vin, r.Address, r.Phone, r.DatabaseName) + outStrings = append(outStrings, out) + } + data = []byte(strings.Join(outStrings, "")) + default: + return errors.New("unsupported file type") + } + + if err != nil { + return err + } + + filePath := fmt.Sprintf("%s.%s", outputFile, fileType) + return ioutil.WriteFile(filePath, data, 0644) +} diff --git a/internal/files/filetype.go b/internal/files/filetype.go new file mode 100644 index 0000000..11cecae --- /dev/null +++ b/internal/files/filetype.go @@ -0,0 +1,44 @@ +package files + +type FileType int32 + +const ( + JSON FileType = iota + XML + YAML + TEXT +) + +func GetFileType(filetype string) FileType { + switch filetype { + case "json": + return JSON + case "xml": + return XML + case "yaml": + return YAML + case "txt": + return TEXT + default: + return JSON + } +} + +func (ft FileType) String() string { + switch ft { + case JSON: + return "json" + case XML: + return "xml" + case YAML: + return "yaml" + case TEXT: + return "txt" + default: + return "json" + } +} + +func (ft FileType) Extension() string { + return "." + ft.String() +} diff --git a/internal/pretty/tables.go b/internal/pretty/tables.go new file mode 100644 index 0000000..8ff571f --- /dev/null +++ b/internal/pretty/tables.go @@ -0,0 +1,48 @@ +package pretty + +import ( + "fmt" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" +) + +var ( + rows = [][]string{ + {"Chinese", "您好", "你好"}, + {"Japanese", "こんにちは", "やあ"}, + {"Arabic", "أهلين", "أهلا"}, + {"Russian", "Здравствуйте", "Привет"}, + {"Spanish", "Hola", "¿Qué tal?"}, + } + + purple = lipgloss.Color("99") + gray = lipgloss.Color("245") + lightGray = lipgloss.Color("241") + + headerStyle = lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center) + cellStyle = lipgloss.NewStyle().Padding(0, 1) + oddRowStyle = cellStyle.Foreground(gray) + evenRowStyle = cellStyle.Foreground(lightGray) +) + +func Table(headers []string, rows [][]string) { + t := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(lipgloss.NewStyle().Foreground(purple)). + StyleFunc(func(row, col int) lipgloss.Style { + switch { + case row == table.HeaderRow: + return headerStyle + case row%2 == 0: + return evenRowStyle + default: + return oddRowStyle + } + }). + Headers(headers...). + Rows(rows...) + + // You can also add tables row-by-row + //t.Row("English", "You look absolutely fabulous.", "How's it going?") + fmt.Println(t) +} diff --git a/internal/query/client.go b/internal/query/client.go new file mode 100644 index 0000000..d31e940 --- /dev/null +++ b/internal/query/client.go @@ -0,0 +1,111 @@ +package query + +import ( + "dehasher/internal/sqlite" + "fmt" + "log" + "net/http" + "net/url" + "os" + "strings" +) + +type DehashedClient struct { + key string + email string + results []sqlite.Result + client *http.Client + query string + params string + printBal bool + total int + balance int +} + +var baseUrl = "https://api.dehashed.com/v2/search" + +func NewDehashedClient(key, email string, printBal bool) *DehashedClient { + return &DehashedClient{key: key, email: email, results: make([]sqlite.Result, 0), client: &http.Client{}, printBal: printBal} +} + +func (dc *DehashedClient) getKey() string { + return dc.key +} + +func (dc *DehashedClient) getEmail() string { + return dc.email +} + +func (dc *DehashedClient) GetResults() sqlite.DehashedResults { + return sqlite.DehashedResults{Results: dc.results} +} + +func (dc *DehashedClient) buildQuery(params map[string]string) { + urlParams := url.Values{} + urlString := baseUrl + + if len(params) > 0 { + urlString += "?query=" + + for k, v := range params { + if len(v) > 0 { + urlParams.Add(k, v) + } + } + } + + tmp, _ := url.QueryUnescape(urlParams.Encode()) + tmp2 := strings.Replace(tmp, "=", ":", -1) + dc.params = tmp2 + urlString += dc.params + dc.query = urlString +} + +func (dc *DehashedClient) setResults(results int) { + dc.query = fmt.Sprintf("%s?query=%s&size=%d", baseUrl, dc.params, results) +} + +func (dc *DehashedClient) setPage(page int) { + dc.query = fmt.Sprintf("%s&nextPage=%d", dc.query, page) +} + +func (dc *DehashedClient) Do() int { + fmt.Printf("\n\t[*] Performing Request...") + req, err := http.NewRequest("GET", dc.query, nil) + if err != nil { + fmt.Printf("[!] Error constructing request: %v", err) + os.Exit(-1) + } + + dc.setAuth(req) + req.Header.Add("Dehashed-Api-Key", dc.getKey()) + req.Header.Add("Accept", "application/json") + resp, err := dc.client.Do(req) + if err != nil { + fmt.Printf("[!] Error performing request: %s\n%v", dc.query, err) + os.Exit(-1) + } + + if resp.StatusCode != 200 { + dhErr := GetDehashedError(resp.StatusCode) + fmt.Println() + log.Fatal(dhErr.Error()) + } + + entries, balance, total := sqlite.NewDehashedResults(resp.Body) + dc.results = append(dc.results, entries...) + dc.balance = balance + dc.total += total + if dc.printBal { + fmt.Printf("\n\t\t[*] Balance Remaining: %d", balance) + } + return total +} + +func (dc *DehashedClient) setAuth(r *http.Request) { + r.SetBasicAuth(dc.email, dc.key) +} + +func (dc *DehashedClient) GetDomains() int { + return dc.balance +} diff --git a/internal/query/clientv2.go b/internal/query/clientv2.go new file mode 100644 index 0000000..3412cba --- /dev/null +++ b/internal/query/clientv2.go @@ -0,0 +1,193 @@ +package query + +import ( + "bytes" + "crypto/sha256" + "dehasher/internal/sqlite" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "go.uber.org/zap" + "io" + "net/http" + "strings" +) + +type DehashedParameter string + +const ( + Username DehashedParameter = "username" + Email DehashedParameter = "email" + Password DehashedParameter = "password" + HashedPassword DehashedParameter = "hashed_password" + Name DehashedParameter = "name" + IpAddress DehashedParameter = "ip_address" + Domain DehashedParameter = "domain" + Vin DehashedParameter = "vin" + LicensePlate DehashedParameter = "license_plate" + Address DehashedParameter = "address" + Phone DehashedParameter = "phone" + Social DehashedParameter = "social" + CryptoAddress DehashedParameter = "cryptocurrency_address" +) + +func (dp DehashedParameter) GetArgumentString(arg string) string { + return fmt.Sprintf("%s:%s", string(dp), arg) +} + +type DehashedSearchRequest struct { + ForcePlaintext bool `json:"-"` + Page int `json:"page"` + Query string `json:"query"` + Size int `json:"size"` + Wildcard bool `json:"wildcard"` + Regex bool `json:"regex"` + DeDupe bool `json:"de_dupe"` +} + +func NewDehashedSearchRequest(page, size int, wildcard, regex, forcePlaintext bool) *DehashedSearchRequest { + return &DehashedSearchRequest{Page: page, Query: "", Size: size, Wildcard: wildcard, Regex: regex, DeDupe: true, ForcePlaintext: forcePlaintext} +} + +func (dsr *DehashedSearchRequest) buildQuery(query string, param DehashedParameter) { + if len(dsr.Query) > 0 { + dsr.Query = fmt.Sprintf("%s&%s", strings.TrimSpace(dsr.Query), strings.TrimSpace(query)) + } else { + dsr.Query = query + } +} + +func (dsr *DehashedSearchRequest) AddUsernameQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Username.GetArgumentString(query), Username) +} + +func (dsr *DehashedSearchRequest) AddEmailQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Email.GetArgumentString(query), Email) +} + +func (dsr *DehashedSearchRequest) AddIpAddressQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(IpAddress.GetArgumentString(query), IpAddress) +} + +func (dsr *DehashedSearchRequest) AddDomainQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Domain.GetArgumentString(query), Domain) +} + +func (dsr *DehashedSearchRequest) AddPasswordQuery(query string) { + if dsr.ForcePlaintext { + dsr.buildQuery(Password.GetArgumentString(query), Password) + return + } + hash := sha256.Sum256([]byte(query)) + query = hex.EncodeToString(hash[:]) + dsr.AddHashedPasswordQuery(query) +} + +func (dsr *DehashedSearchRequest) AddVinQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Vin.GetArgumentString(query), Vin) +} + +func (dsr *DehashedSearchRequest) AddLicensePlateQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(LicensePlate.GetArgumentString(query), LicensePlate) +} + +func (dsr *DehashedSearchRequest) AddAddressQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Address.GetArgumentString(query), Address) +} + +func (dsr *DehashedSearchRequest) AddPhoneQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Phone.GetArgumentString(query), Phone) +} + +func (dsr *DehashedSearchRequest) AddSocialQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Social.GetArgumentString(query), Social) +} + +func (dsr *DehashedSearchRequest) AddCryptoAddressQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(CryptoAddress.GetArgumentString(query), CryptoAddress) +} + +func (dsr *DehashedSearchRequest) AddHashedPasswordQuery(query string) { + dsr.buildQuery(HashedPassword.GetArgumentString(query), HashedPassword) +} + +func (dsr *DehashedSearchRequest) AddNameQuery(query string) { + query = strings.TrimSpace(query) + dsr.buildQuery(Name.GetArgumentString(query), Name) +} + +type DehashedClientV2 struct { + apiKey string + results []sqlite.Result +} + +func NewDehashedClientV2(apiKey string) *DehashedClientV2 { + return &DehashedClientV2{apiKey: apiKey} +} + +func (dcv2 *DehashedClientV2) Search(searchRequest DehashedSearchRequest) (int, error) { + reqBody, _ := json.Marshal(searchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/search", bytes.NewReader(reqBody)) + if err != nil { + return -1, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", dcv2.apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + zap.L().Error("v2_search", + zap.String("message", "failed to perform request"), + zap.Error(err), + ) + return -1, err + } + if res == nil { + zap.L().Error("v2_search", + zap.String("message", "response was nil"), + ) + return -1, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + zap.L().Error("v2_search", + zap.String("message", "failed to read response body"), + zap.Error(err), + ) + return -1, err + } + + var responseResults sqlite.DehashedResponse + err = json.Unmarshal(b, &responseResults) + if err != nil { + zap.L().Error("v2_search", + zap.String("message", "failed to unmarshal response body"), + zap.Error(err), + ) + return -1, err + } + + dcv2.results = append(dcv2.results, responseResults.Entries...) + return responseResults.TotalResults, nil +} + +func (dcv2 *DehashedClientV2) GetResults() sqlite.DehashedResults { + return sqlite.DehashedResults{Results: dcv2.results} +} + +func (dcv2 *DehashedClientV2) GetTotalResults() int { + return len(dcv2.results) +} diff --git a/internal/query/dehashed.go b/internal/query/dehashed.go new file mode 100644 index 0000000..a14949f --- /dev/null +++ b/internal/query/dehashed.go @@ -0,0 +1,206 @@ +package query + +import ( + "dehasher/internal/export" + "dehasher/internal/sqlite" + "encoding/json" + "fmt" + "go.uber.org/zap" + "os" +) + +// Dehasher is a struct for querying the Dehashed API +type Dehasher struct { + options sqlite.QueryOptions + nextPage int + request *DehashedSearchRequest + client *DehashedClientV2 +} + +// NewDehasher creates a new Dehasher +func NewDehasher(options *sqlite.QueryOptions) *Dehasher { + dh := &Dehasher{ + options: *options, + nextPage: options.StartingPage + 1, + } + dh.setQueries() + dh.request = NewDehashedSearchRequest(dh.options.StartingPage, dh.options.MaxRecords, dh.options.WildcardMatch, dh.options.RegexMatch, false) + dh.buildRequest() + return dh +} + +// SetClientCredentials sets the client credentials for the dehasher +func (dh *Dehasher) SetClientCredentials(key string) { + dh.client = NewDehashedClientV2(key) +} + +func (dh *Dehasher) getNextPage() int { + 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() { + var numQueries int + + switch { + case dh.options.MaxRequests == 0: + zap.L().Error("max requests cannot be zero") + fmt.Println("[!] Max Requests cannot be zero") + os.Exit(1) + case dh.options.MaxRecords <= 10000 || dh.options.MaxRequests == 1: + numQueries = 1 + if dh.options.MaxRecords > 10000 { + dh.options.MaxRecords = 10000 + } + zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords)) + case dh.options.MaxRequests < 0 && dh.options.MaxRecords > 20000: + numQueries = 3 + dh.options.MaxRecords = 10000 + zap.L().Info("max requests set to 3", zap.Int("max_records", dh.options.MaxRecords)) + case dh.options.MaxRequests < 0 && dh.options.MaxRecords > 10000: + numQueries = 2 + dh.options.MaxRecords = 10000 + zap.L().Info("max requests set to 2", zap.Int("max_records", dh.options.MaxRecords)) + case dh.options.MaxRecords < 0 && dh.options.MaxRecords < 10000: + numQueries = 1 + zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords)) + case dh.options.MaxRequests == 2 && dh.options.MaxRecords > 20000: + numQueries = 2 + dh.options.MaxRecords = 10000 + zap.L().Info("max requests set to 2", zap.Int("max_records", dh.options.MaxRecords)) + case dh.options.MaxRequests == 2 && dh.options.MaxRecords <= 10000: + numQueries = 1 + zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords)) + default: + numQueries = 3 + dh.options.MaxRecords = 10000 + zap.L().Info("max requests set to 3", zap.Int("max_records", dh.options.MaxRecords)) + } + + dh.options.MaxRequests = numQueries + fmt.Printf("Making %d Requests for %d Records (%d Total)\n", dh.options.MaxRequests, dh.options.MaxRecords, dh.options.MaxRequests*dh.options.MaxRecords) +} + +// Start starts the querying process +func (dh *Dehasher) Start() { + fmt.Println("[*] Querying Dehashed API...") + for i := 0; i < dh.options.MaxRequests; i++ { + fmt.Printf("\n\t[*] Performing Request...") + count, err := dh.client.Search(*dh.request) + if err != nil { + fmt.Printf("[!] Error performing request: %v", err) + os.Exit(-1) + } + + if count < dh.options.MaxRecords { + fmt.Printf("\n\t\t[+] Retrieved %d Records", count) + fmt.Printf("\n[-] Not Enough Entries, ending queries") + break + } else { + fmt.Printf("\n\t\t[+] Retrieved %d Records", dh.options.MaxRecords) + } + + 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() { + var data []byte + + zap.L().Info("extracting_credentials") + results := dh.client.GetResults() + creds := results.ExtractCredentials() + fmt.Printf("\n\t[*] Discovered %d Credentials", len(creds)) + err := sqlite.StoreCreds(creds) + 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") + err = sqlite.StoreResults(results) + 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 { + fmt.Printf("\n\t[*] Writing entries to file: %s.%s", dh.options.OutputFile, dh.options.OutputFormat.String()) + if !dh.options.CredsOnly { + err := export.WriteToFile(results, dh.options.OutputFile, dh.options.OutputFormat) + if err != nil { + fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err) + data, err = json.MarshalIndent(results, "", " ") + fmt.Println(string(data)) + os.Exit(0) + } else { + fmt.Println("\n\t\t[*] Success\n") + } + } else { + creds := results.ExtractCredentials() + err := export.WriteCredsToFile(creds, dh.options.OutputFile, dh.options.OutputFormat) + if err != nil { + fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err) + data, err = json.MarshalIndent(creds, "", " ") + fmt.Println(string(data)) + os.Exit(0) + } else { + fmt.Println("\n\t\t[*] Success\n") + } + } + } +} diff --git a/internal/query/errors.go b/internal/query/errors.go new file mode 100644 index 0000000..12027e1 --- /dev/null +++ b/internal/query/errors.go @@ -0,0 +1,33 @@ +package query + +type DehashError struct { + Message string + Code int +} + +type DehashResponseError struct { + HttpResponse int `json:"HTTP Response Code"` +} + +func (de *DehashError) Error() string { + return de.Message +} + +func GetDehashedError(c int) DehashError { + switch c { + case 400: + return DehashError{Code: 400, Message: "There is an issue with authentication. Please check your API key and email. If you haven't, refresh your API Key "} + case 401: + return DehashError{Code: 401, Message: "You need a search subscription and API credits to use the API, please purchase a search subscription and add credits to your account."} + case 403: + return DehashError{Code: 403, Message: "Insufficient Credits"} + case 404: + return DehashError{Code: 404, Message: "Method not permitted"} + case 429: + return DehashError{Code: 420, Message: "Rate Limited"} + case 302: + return DehashError{Code: 302, Message: "Invalid/Missing Query"} + default: + return DehashError{Code: -1, Message: "An unknown error has occurred"} + } +} diff --git a/internal/sqlite/db.go b/internal/sqlite/db.go new file mode 100644 index 0000000..bcc6d71 --- /dev/null +++ b/internal/sqlite/db.go @@ -0,0 +1,365 @@ +package sqlite + +import ( + "fmt" + "go.uber.org/zap" + "gorm.io/gorm" + "time" +) + +// QueryResults queries the database for results based on the provided options +func QueryResults(options *DBOptions) ([]Result, error) { + db := GetDB() + var results []Result + query := db.Model(&Result{}) + + // Apply filters based on the provided options + query = applyFilters(query, options) + + // Apply limit + if options.Limit > 0 { + query = query.Limit(options.Limit) + } + + // Execute the query + if err := query.Find(&results).Error; err != nil { + zap.L().Error("query_results", + zap.String("message", "failed to query results"), + zap.Error(err), + ) + return nil, fmt.Errorf("failed to query results: %w", err) + } + + return results, nil +} + +// applyFilters applies filters to the query based on the provided options +func applyFilters(query *gorm.DB, options *DBOptions) *gorm.DB { + // Helper function to apply filter based on exact match setting + applyFilter := func(field, value string) *gorm.DB { + if value == "" { + return query + } + + if options.ExactMatch { + return query.Where(field+" = ?", value) + } else { + return query.Where(field+" LIKE ?", "%"+value+"%") + } + } + + // Apply filters for each field if provided + if options.Email != "" { + query = applyFilter("email", options.Email) + } + + if options.Username != "" { + query = applyFilter("username", options.Username) + } + + if options.IPAddress != "" { + query = applyFilter("ip_address", options.IPAddress) + } + + if options.Password != "" { + query = applyFilter("password", options.Password) + } + + if options.HashedPassword != "" { + query = applyFilter("hashed_password", options.HashedPassword) + } + + if options.Name != "" { + query = applyFilter("name", options.Name) + } + + if options.Vin != "" { + query = applyFilter("vin", options.Vin) + } + + if options.LicensePlate != "" { + query = applyFilter("license_plate", options.LicensePlate) + } + + if options.Address != "" { + query = applyFilter("address", options.Address) + } + + if options.Phone != "" { + query = applyFilter("phone", options.Phone) + } + + if options.Social != "" { + query = applyFilter("social", options.Social) + } + + if options.CryptoCurrencyAddress != "" { + query = applyFilter("cryptocurrency_address", options.CryptoCurrencyAddress) + } + + if options.Domain != "" { + query = applyFilter("url", options.Domain) + } + + // Apply non-empty field filters + for _, field := range options.NonEmptyFields { + switch field { + case "username": + query = query.Where("JSON_ARRAY_LENGTH(username) > 0") + case "email": + query = query.Where("JSON_ARRAY_LENGTH(email) > 0") + case "ip_address", "ipaddress", "ip": + query = query.Where("JSON_ARRAY_LENGTH(ip_address) > 0") + case "password": + query = query.Where("JSON_ARRAY_LENGTH(password) > 0") + case "hashed_password", "hash": + query = query.Where("JSON_ARRAY_LENGTH(hashed_password) > 0") + case "name": + query = query.Where("JSON_ARRAY_LENGTH(name) > 0") + case "vin": + query = query.Where("JSON_ARRAY_LENGTH(vin) > 0") + case "license_plate", "license": + query = query.Where("JSON_ARRAY_LENGTH(license_plate) > 0") + case "address": + query = query.Where("JSON_ARRAY_LENGTH(address) > 0") + case "phone": + query = query.Where("JSON_ARRAY_LENGTH(phone) > 0") + case "social": + query = query.Where("JSON_ARRAY_LENGTH(social) > 0") + case "cryptocurrency_address", "crypto": + query = query.Where("JSON_ARRAY_LENGTH(cryptocurrency_address) > 0") + case "url", "domain": + query = query.Where("JSON_ARRAY_LENGTH(url) > 0") + } + } + + return query +} + +// GetResultsCount returns the count of results matching the provided options +func GetResultsCount(options *DBOptions) (int64, error) { + db := GetDB() + var count int64 + query := db.Model(&Result{}) + + // Apply filters based on the provided options + query = applyFilters(query, options) + + // Count the results + if err := query.Count(&count).Error; err != nil { + zap.L().Error("get_results_count", + zap.String("message", "failed to count results"), + zap.Error(err), + ) + return 0, fmt.Errorf("failed to count results: %w", err) + } + + return count, nil +} + +// QueryRuns queries the database for previous query runs (QueryOptions) based on the provided filters +func QueryRuns(limit, lastXRuns int, startDate, endDate time.Time, containsQuery string) ([]QueryOptions, error) { + db := GetDB() + var runs []QueryOptions + query := db.Model(&QueryOptions{}) + + // Apply date range filter if provided + if lastXRuns > 0 { + query = query.Order("created_at DESC").Limit(lastXRuns) + } else if !startDate.IsZero() && !endDate.IsZero() { + query = query.Where("created_at BETWEEN ? AND ?", startDate, endDate) + } else if !startDate.IsZero() { + query = query.Where("created_at >= ?", startDate) + } else if !endDate.IsZero() { + query = query.Where("created_at <= ?", endDate) + } + + // Apply query filter if provided + if containsQuery != "" { + // Search in all query fields + query = query.Where( + "username_query LIKE ? OR "+ + "email_query LIKE ? OR "+ + "ip_query LIKE ? OR "+ + "pass_query LIKE ? OR "+ + "hash_query LIKE ? OR "+ + "name_query LIKE ? OR "+ + "domain_query LIKE ? OR "+ + "vin_query LIKE ? OR "+ + "license_plate_query LIKE ? OR "+ + "address_query LIKE ? OR "+ + "phone_query LIKE ? OR "+ + "social_query LIKE ? OR "+ + "crypto_address_query LIKE ?", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", + ) + } + + // Apply limit + if limit > 0 { + query = query.Limit(limit) + } + + // Order by most recent first + query = query.Order("created_at DESC") + + // Execute the query + if err := query.Find(&runs).Error; err != nil { + zap.L().Error("query_runs", + zap.String("message", "failed to query runs"), + zap.Error(err), + ) + return nil, fmt.Errorf("failed to query runs: %w", err) + } + + return runs, nil +} + +// GetRunsCount returns the count of runs matching the provided filters +func GetRunsCount(lastXRuns int, startDate, endDate time.Time, containsQuery string) (int64, error) { + db := GetDB() + var count int64 + query := db.Model(&QueryOptions{}) + + // Apply date range filter if provided + if lastXRuns > 0 { + query = query.Order("created_at DESC").Limit(lastXRuns) + } else if !startDate.IsZero() && !endDate.IsZero() { + query = query.Where("created_at BETWEEN ? AND ?", startDate, endDate) + } else if !startDate.IsZero() { + query = query.Where("created_at >= ?", startDate) + } else if !endDate.IsZero() { + query = query.Where("created_at <= ?", endDate) + } + + // Apply query filter if provided + if containsQuery != "" { + // Search in all query fields + query = query.Where( + "username_query LIKE ? OR "+ + "email_query LIKE ? OR "+ + "ip_query LIKE ? OR "+ + "pass_query LIKE ? OR "+ + "hash_query LIKE ? OR "+ + "name_query LIKE ? OR "+ + "domain_query LIKE ? OR "+ + "vin_query LIKE ? OR "+ + "license_plate_query LIKE ? OR "+ + "address_query LIKE ? OR "+ + "phone_query LIKE ? OR "+ + "social_query LIKE ? OR "+ + "crypto_address_query LIKE ?", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", "%"+containsQuery+"%", "%"+containsQuery+"%", + "%"+containsQuery+"%", + ) + } + + // Count the results + if err := query.Count(&count).Error; err != nil { + zap.L().Error("get_runs_count", + zap.String("message", "failed to count runs"), + zap.Error(err), + ) + return 0, fmt.Errorf("failed to count runs: %w", err) + } + + return count, nil +} + +// QueryCreds queries the database for credentials based on the provided filters +func QueryCreds(options *DBOptions) ([]Creds, error) { + db := GetDB() + var creds []Creds + query := db.Model(&Creds{}) + + // Apply filters based on the provided options + if options.Username != "" { + if options.ExactMatch { + query = query.Where("username = ?", options.Username) + } else { + query = query.Where("username LIKE ?", "%"+options.Username+"%") + } + } + + if options.Email != "" { + if options.ExactMatch { + query = query.Where("email = ?", options.Email) + } else { + query = query.Where("email LIKE ?", "%"+options.Email+"%") + } + } + + if options.Password != "" { + if options.ExactMatch { + query = query.Where("password = ?", options.Password) + } else { + query = query.Where("password LIKE ?", "%"+options.Password+"%") + } + } + + // Apply limit + if options.Limit > 0 { + query = query.Limit(options.Limit) + } + + // Execute the query + if err := query.Find(&creds).Error; err != nil { + zap.L().Error("query_creds", + zap.String("message", "failed to query credentials"), + zap.Error(err), + ) + return nil, fmt.Errorf("failed to query credentials: %w", err) + } + + return creds, nil +} + +// GetCredsCount returns the count of credentials matching the provided filters +func GetCredsCount(options *DBOptions) (int64, error) { + db := GetDB() + var count int64 + query := db.Model(&Creds{}) + + // Apply filters based on the provided options + if options.Username != "" { + if options.ExactMatch { + query = query.Where("username = ?", options.Username) + } else { + query = query.Where("username LIKE ?", "%"+options.Username+"%") + } + } + + if options.Email != "" { + if options.ExactMatch { + query = query.Where("email = ?", options.Email) + } else { + query = query.Where("email LIKE ?", "%"+options.Email+"%") + } + } + + if options.Password != "" { + if options.ExactMatch { + query = query.Where("password = ?", options.Password) + } else { + query = query.Where("password LIKE ?", "%"+options.Password+"%") + } + } + + // Count the results + if err := query.Count(&count).Error; err != nil { + zap.L().Error("get_creds_count", + zap.String("message", "failed to count credentials"), + zap.Error(err), + ) + return 0, fmt.Errorf("failed to count credentials: %w", err) + } + + return count, nil +} diff --git a/internal/sqlite/gorm.go b/internal/sqlite/gorm.go new file mode 100644 index 0000000..c8a6240 --- /dev/null +++ b/internal/sqlite/gorm.go @@ -0,0 +1,209 @@ +package sqlite + +import ( + "fmt" + "go.uber.org/zap" + "gorm.io/gorm/clause" + "os" + "path/filepath" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +var DB *gorm.DB + +// InitDB initializes the database connection +func InitDB(dbDir string) (*gorm.DB, error) { + zap.L().Info("Initializing database") + + // Create directory if it doesn't exist + if err := os.MkdirAll(dbDir, 0755); err != nil { + zap.L().Error("Failed to create database directory", zap.Error(err)) + return nil, fmt.Errorf("failed to create database directory: %w", err) + } + + dbPath := filepath.Join(dbDir, "dehashed.sqlite") + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + zap.L().Error("Failed to connect to database", zap.Error(err)) + return nil, fmt.Errorf("failed to connect to database: %w", err) + } + + // Auto migrate your models + err = db.AutoMigrate(&Result{}, &Creds{}, QueryOptions{}, Creds{}, WhoisRecord{}, SubdomainRecord{}, HistoryRecord{}) + if err != nil { + zap.L().Error("Failed to migrate database", zap.Error(err)) + return nil, fmt.Errorf("failed to migrate database: %w", err) + } + + DB = db + return db, nil +} + +// GetDB returns the database connection +func GetDB() *gorm.DB { + if DB == nil { + zap.L().Error("database not initialized") + fmt.Println("sqlite database not initialized") + os.Exit(1) + } + return DB +} + +func StoreResults(results DehashedResults) error { + if len(results.Results) == 0 { + return nil + } + + zap.L().Info("Storing results", zap.Int("count", len(results.Results))) + db := GetDB() + + // Use batch insert with conflict handling + const batchSize = 100 + var lastErr error + + // Extract the slice of results + resultSlice := results.Results + + for i := 0; i < len(resultSlice); i += batchSize { + end := i + batchSize + if end > len(resultSlice) { + end = len(resultSlice) + } + + batch := resultSlice[i:end] + // Use Clauses with OnConflict DoNothing to skip conflicts + err := db.Clauses(clause.OnConflict{DoNothing: true}).CreateInBatches(&batch, batchSize).Error + if err != nil { + zap.L().Warn("Error storing some results", zap.Error(err)) + lastErr = err + // Continue with next batch despite error + } + } + + return lastErr +} + +func StoreCreds(creds []Creds) error { + if len(creds) == 0 { + return nil + } + + zap.L().Info("Storing credentials", zap.Int("count", len(creds))) + db := GetDB() + + // Use batch insert with conflict handling + // This will insert records in batches and continue even if some fail + const batchSize = 100 + var lastErr error + + for i := 0; i < len(creds); i += batchSize { + end := i + batchSize + if end > len(creds) { + end = len(creds) + } + + batch := creds[i:end] + // Use Clauses with OnConflict DoNothing to skip conflicts + err := db.Clauses(clause.OnConflict{DoNothing: true}).CreateInBatches(&batch, batchSize).Error + if err != nil { + zap.L().Warn("Error storing some credentials", zap.Error(err)) + lastErr = err + // Continue with next batch despite error + } + } + + return lastErr +} + +func StoreQueryOptions(queryOptions *QueryOptions) error { + db := GetDB() + return db.Create(queryOptions).Error +} + +func StoreWhoisRecord(whoisRecord WhoisRecord) error { + // Create a pointer to the record to make it addressable + recordPtr := &whoisRecord + + zap.L().Info("Storing WHOIS record", + zap.String("domain", whoisRecord.DomainName)) + + db := GetDB() + + // Use OnConflict clause to handle duplicates + err := db.Clauses(clause.OnConflict{DoNothing: true}).Create(recordPtr).Error + if err != nil { + zap.L().Error("store_whois_record", + zap.String("message", "failed to store whois record"), + zap.Error(err)) + return err + } + + return nil +} + +func StoreSubdomainRecord(subdomainRecords []SubdomainRecord) error { + if len(subdomainRecords) == 0 { + return nil + } + + zap.L().Info("Storing subdomain records", zap.Int("count", len(subdomainRecords))) + db := GetDB() + + // Use batch insert with conflict handling + const batchSize = 100 + var lastErr error + + for i := 0; i < len(subdomainRecords); i += batchSize { + end := i + batchSize + if end > len(subdomainRecords) { + end = len(subdomainRecords) + } + + batch := subdomainRecords[i:end] + // Use Clauses with OnConflict DoNothing to skip conflicts + err := db.Clauses(clause.OnConflict{DoNothing: true}).CreateInBatches(&batch, batchSize).Error + if err != nil { + zap.L().Warn("Error storing some subdomain records", zap.Error(err)) + lastErr = err + // Continue with next batch despite error + } + } + + return lastErr +} + +func StoreHistoryRecord(historyRecords []HistoryRecord) error { + if len(historyRecords) == 0 { + return nil + } + + zap.L().Info("Storing history records", zap.Int("count", len(historyRecords))) + db := GetDB() + + // Use batch insert with conflict handling + const batchSize = 100 + var lastErr error + + for i := 0; i < len(historyRecords); i += batchSize { + end := i + batchSize + if end > len(historyRecords) { + end = len(historyRecords) + } + + batch := historyRecords[i:end] + // Use Clauses with OnConflict DoNothing to skip conflicts + err := db.Clauses(clause.OnConflict{DoNothing: true}).CreateInBatches(&batch, batchSize).Error + if err != nil { + zap.L().Warn("Error storing some history records", zap.Error(err)) + lastErr = err + // Continue with next batch despite error + } + } + + return lastErr +} diff --git a/internal/sqlite/query.go b/internal/sqlite/query.go new file mode 100644 index 0000000..1b6382c --- /dev/null +++ b/internal/sqlite/query.go @@ -0,0 +1,20 @@ +package sqlite + +type DehashedSearchRequest struct { + Page int `json:"page"` + Query string `json:"query"` + Size int `json:"size"` + Wildcard bool `json:"wildcard"` + Regex bool `json:"regex"` + DeDupe bool `json:"de_dupe"` +} + +func NewDehashedSearchRequest(size int, wildcard, regex bool) *DehashedSearchRequest { + return &DehashedSearchRequest{ + Page: 0, + Size: size, + Wildcard: false, + Regex: false, + DeDupe: true, + } +} diff --git a/internal/sqlite/result.go b/internal/sqlite/result.go new file mode 100644 index 0000000..d31817b --- /dev/null +++ b/internal/sqlite/result.go @@ -0,0 +1,90 @@ +package sqlite + +import ( + "encoding/json" + "fmt" + "go.uber.org/zap" + "gorm.io/gorm" + "io" + "os" +) + +type DehashedResponse struct { + Balance int `json:"balance"` + Entries []Result `json:"entries"` + Success bool `json:"success"` + Took string `json:"took"` + TotalResults int `json:"total"` +} + +type Result struct { + gorm.Model + DehashedId string `json:"id" xml:"id" yaml:"id" gorm:"uniqueIndex"` + Email []string `json:"email,omitempty" xml:"email,omitempty" yaml:"email,omitempty" gorm:"serializer:json"` + IpAddress []string `json:"ip_address,omitempty" xml:"ip_address,omitempty" yaml:"ip_address,omitempty" gorm:"serializer:json"` + Username []string `json:"username,omitempty" xml:"username,omitempty" yaml:"username,omitempty" gorm:"serializer:json"` + Password []string `json:"password,omitempty" xml:"password,omitempty" yaml:"password,omitempty" gorm:"serializer:json"` + HashedPassword []string `json:"hashed_password,omitempty" xml:"hashed_password,omitempty" yaml:"hashed_password,omitempty" gorm:"serializer:json"` + HashType string `json:"hash_type,omitempty" xml:"hash_type,omitempty" yaml:"hash_type,omitempty"` + Name []string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" gorm:"serializer:json"` + Vin []string `json:"vin,omitempty" xml:"vin,omitempty" yaml:"vin,omitempty" gorm:"serializer:json"` + LicensePlate []string `json:"license_plate,omitempty" xml:"license_plate,omitempty" yaml:"license_plate,omitempty" gorm:"serializer:json"` + Url []string `json:"url,omitempty" xml:"url,omitempty" yaml:"url,omitempty" gorm:"serializer:json"` + Social []string `json:"social,omitempty" xml:"social,omitempty" yaml:"social,omitempty" gorm:"serializer:json"` + CryptoCurrencyAddress []string `json:"cryptocurrency_address,omitempty" xml:"cryptocurrency_address,omitempty" yaml:"cryptocurrency_address,omitempty" gorm:"serializer:json"` + Address []string `json:"address,omitempty" xml:"address,omitempty" yaml:"address,omitempty" gorm:"serializer:json"` + Phone []string `json:"phone,omitempty" xml:"phone,omitempty" yaml:"phone,omitempty" gorm:"serializer:json"` + Company []string `json:"company,omitempty" xml:"company,omitempty" yaml:"company,omitempty" gorm:"serializer:json"` + DatabaseName string `json:"database_name,omitempty" xml:"database_name,omitempty" yaml:"database_name,omitempty"` +} + +type DehashedResults struct { + Results []Result `json:"results"` +} + +func (dr *DehashedResults) ExtractCredentials() []Creds { + var creds []Creds + + results := dr.Results + + for _, r := range results { + if len(r.Password) > 0 { + // Get first email if available + email := "" + if len(r.Email) > 0 { + email = r.Email[0] + } + + // Get first password + password := r.Password[0] + + cred := Creds{Email: email, Password: password} + creds = append(creds, cred) + } + } + + go func() { + err := StoreCreds(creds) + if err != nil { + zap.L().Error("store_creds", + zap.String("message", "failed to store creds"), + zap.Error(err), + ) + fmt.Printf("Error Storing Results: %v", err) + } + }() + + return creds +} + +func NewDehashedResults(body io.Reader) ([]Result, int, int) { + var response DehashedResponse + + err := json.NewDecoder(body).Decode(&response) + if err != nil { + fmt.Printf("Error Parsing Response Body: %v", err) + os.Exit(-1) + } + + return response.Entries, response.Balance, response.TotalResults +} diff --git a/internal/sqlite/structs.go b/internal/sqlite/structs.go new file mode 100644 index 0000000..4650da2 --- /dev/null +++ b/internal/sqlite/structs.go @@ -0,0 +1,108 @@ +package sqlite + +import ( + "dehasher/internal/files" + "fmt" + "gorm.io/gorm" +) + +type DBOptions struct { + Username string + Email string + IPAddress string + Password string + HashedPassword string + Name string + Vin string + LicensePlate string + Address string + Phone string + Social string + CryptoCurrencyAddress string + Domain string + Limit int + ExactMatch bool + NonEmptyFields []string // Fields that should not be empty + DisplayFields []string // Fields to display in output +} + +func NewDBOptions() *DBOptions { + return &DBOptions{ + Limit: 100, // Default limit + ExactMatch: false, + NonEmptyFields: []string{}, + DisplayFields: []string{}, + } +} + +func (o *DBOptions) Empty() bool { + return o.Username == "" && o.Email == "" && o.IPAddress == "" && + o.Password == "" && o.HashedPassword == "" && o.Name == "" && + o.Vin == "" && o.LicensePlate == "" && o.Address == "" && + o.Phone == "" && o.Social == "" && o.CryptoCurrencyAddress == "" && o.Domain == "" && + len(o.NonEmptyFields) == 0 +} + +type QueryOptions struct { + gorm.Model + MaxRecords int `json:"max_records"` + MaxRequests int `json:"max_requests"` + StartingPage int `json:"starting_page"` + OutputFormat files.FileType `json:"output_format"` + OutputFile string `json:"output_file"` + RegexMatch bool `json:"regex_match"` + WildcardMatch bool `json:"wildcard_match"` + UsernameQuery string `json:"username_query"` + EmailQuery string `json:"email_query"` + IpQuery string `json:"ip_query"` + PassQuery string `json:"pass_query"` + HashQuery string `json:"hash_query"` + NameQuery string `json:"name_query"` + DomainQuery string `json:"domain_query"` + VinQuery string `json:"vin_query"` + LicensePlateQuery string `json:"license_plate_query"` + AddressQuery string `json:"address_query"` + PhoneQuery string `json:"phone_query"` + SocialQuery string `json:"social_query"` + CryptoAddressQuery string `json:"crypto_address_query"` + PrintBalance bool `json:"print_balance"` + CredsOnly bool `json:"creds_only"` +} + +func NewQueryOptions(maxRecords, maxRequests, startingPage int, outputFormat, outputFile, usernameQuery, emailQuery, ipQuery, passQuery, hashQuery, nameQuery, domainQuery, vinQuery, licensePlateQuery, addressQuery, phoneQuery, socialQuery, cryptoAddressQuery string, regexMatch, wildcardMatch, printBalance, credsOnly bool) *QueryOptions { + return &QueryOptions{ + MaxRecords: maxRecords, + MaxRequests: maxRequests, + StartingPage: startingPage, + OutputFormat: files.GetFileType(outputFormat), + OutputFile: outputFile, + PrintBalance: printBalance, + CredsOnly: credsOnly, + RegexMatch: regexMatch, + WildcardMatch: wildcardMatch, + UsernameQuery: usernameQuery, + EmailQuery: emailQuery, + IpQuery: ipQuery, + PassQuery: passQuery, + HashQuery: hashQuery, + NameQuery: nameQuery, + DomainQuery: domainQuery, + VinQuery: vinQuery, + LicensePlateQuery: licensePlateQuery, + AddressQuery: addressQuery, + PhoneQuery: phoneQuery, + SocialQuery: socialQuery, + CryptoAddressQuery: cryptoAddressQuery, + } +} + +type Creds struct { + gorm.Model + Email string `json:"email" yaml:"email" xml:"email"` + Username string `json:"username" yaml:"username" xml:"username"` + Password string `json:"password" yaml:"password" xml:"password"` +} + +func (c Creds) ToString() string { + return fmt.Sprintf("%s%s%s", c.Username, "%", c.Password) +} diff --git a/internal/sqlite/whois.go b/internal/sqlite/whois.go new file mode 100644 index 0000000..84d6afa --- /dev/null +++ b/internal/sqlite/whois.go @@ -0,0 +1,160 @@ +package sqlite + +import "gorm.io/gorm" + +type WhoIsLookupResult struct { + RemainingCredits int `json:"remaining_credits"` + Data Data `json:"data"` +} + +type Data struct { + WhoisRecord WhoisRecord `json:"WhoisRecord"` +} + +type WhoisRecord struct { + gorm.Model + Audit Audit `json:"audit" gorm:"serializer:json"` + ContactEmail string `json:"contactEmail"` + CreatedDate string `json:"createdDate"` + CreatedDateNormalized string `json:"createdDateNormalized"` + DomainName string `json:"domainName"` + DomainNameExt string `json:"domainNameExt"` + EstimatedDomainAge int `json:"estimatedDomainAge"` + ExpiresDate string `json:"expiresDate"` + ExpiresDateNormalized string `json:"expiresDateNormalized"` + Footer string `json:"footer"` + Header string `json:"header"` + NameServers NameServers `json:"nameServers" gorm:"serializer:json"` + ParseCode int `json:"parseCode"` + RawText string `json:"rawText"` + Registrant Contact `json:"registrant" gorm:"serializer:json"` + RegistrarIANAID string `json:"registrarIANAID"` + RegistrarName string `json:"registrarName"` + RegistryData RegistryData `json:"registryData" gorm:"serializer:json"` + Status string `json:"status"` + StrippedText string `json:"strippedText"` + TechnicalContact Contact `json:"technicalContact" gorm:"serializer:json"` + UpdatedDate string `json:"updatedDate"` + UpdatedDateNormalized string `json:"updatedDateNormalized"` +} + +type Audit struct { + CreatedDate string `json:"createdDate"` + UpdatedDate string `json:"updatedDate"` +} + +type NameServers struct { + HostNames []string `json:"hostNames"` + IPs []string `json:"ips"` + RawText string `json:"rawText"` +} + +type Contact struct { + City string `json:"city"` + Country string `json:"country"` + CountryCode string `json:"countryCode"` + Name string `json:"name"` + Organization string `json:"organization"` + PostalCode string `json:"postalCode"` + RawText string `json:"rawText"` + State string `json:"state"` + Street1 string `json:"street1"` + Telephone string `json:"telephone"` +} + +type RegistryData struct { + Audit Audit `json:"audit"` + CreatedDate string `json:"createdDate"` + CreatedDateNormalized string `json:"createdDateNormalized"` + DomainName string `json:"domainName"` + ExpiresDate string `json:"expiresDate"` + ExpiresDateNormalized string `json:"expiresDateNormalized"` + Footer string `json:"footer"` + Header string `json:"header"` + NameServers NameServers `json:"nameServers"` + ParseCode int `json:"parseCode"` + RawText string `json:"rawText"` + RegistrarIANAID string `json:"registrarIANAID"` + RegistrarName string `json:"registrarName"` + Status string `json:"status"` + StrippedText string `json:"strippedText"` + UpdatedDate string `json:"updatedDate"` + UpdatedDateNormalized string `json:"updatedDateNormalized"` + WhoisServer string `json:"whoisServer"` +} + +type WhoIsSubdomainScan struct { + RemainingCredits int `json:"remaining_credits"` + Data ScanData `json:"data"` +} + +type ScanData struct { + Result ScanResult `json:"result"` + Search string `json:"search"` +} + +type ScanResult struct { + Count int `json:"count"` + Records []SubdomainRecord `json:"records"` +} + +type SubdomainRecord struct { + gorm.Model + Domain string `json:"domain"` + FirstSeen int64 `json:"firstSeen"` + LastSeen int64 `json:"lastSeen"` +} + +type WhoIsHistory struct { + RemainingCredits int `json:"remaining_credits"` + Data HistoryData `json:"data"` +} + +type HistoryData struct { + Records []HistoryRecord `json:"records"` + RecordsCount int `json:"recordsCount"` +} + +type HistoryRecord struct { + gorm.Model + AdministrativeContact ContactInfo `json:"administrativeContact" gorm:"serializer:json"` + Audit Audit `json:"audit" gorm:"serializer:json"` + BillingContact ContactInfo `json:"billingContact" gorm:"serializer:json"` + CleanText string `json:"cleanText"` + CreatedDateISO8601 string `json:"createdDateISO8601"` + CreatedDateRaw string `json:"createdDateRaw"` + DomainName string `json:"domainName"` + DomainType string `json:"domainType"` + ExpiresDateISO8601 string `json:"expiresDateISO8601"` + ExpiresDateRaw string `json:"expiresDateRaw"` + NameServers []string `json:"nameServers" gorm:"serializer:json"` + RawText string `json:"rawText"` + RegistrantContact ContactInfo `json:"registrantContact" gorm:"serializer:json"` + RegistrarName string `json:"registrarName"` + Status []string `json:"status" gorm:"serializer:json"` + TechnicalContact ContactInfo `json:"technicalContact" gorm:"serializer:json"` + UpdatedDateISO8601 string `json:"updatedDateISO8601"` + UpdatedDateRaw string `json:"updatedDateRaw"` + WhoisServer string `json:"whoisServer"` + ZoneContact ContactInfo `json:"zoneContact" gorm:"serializer:json"` +} + +type ContactInfo struct { + City string `json:"city"` + Country string `json:"country"` + Email string `json:"email"` + Fax string `json:"fax"` + FaxExt string `json:"faxExt"` + Name string `json:"name"` + Organization string `json:"organization"` + PostalCode string `json:"postalCode"` + RawText string `json:"rawText"` + State string `json:"state"` + Street string `json:"street"` + Telephone string `json:"telephone"` + TelephoneExt string `json:"telephoneExt"` +} + +type WhoIsCredits struct { + WhoisCredits int `json:"whois_credits"` +} diff --git a/internal/whois/whois.go b/internal/whois/whois.go new file mode 100644 index 0000000..93dfef5 --- /dev/null +++ b/internal/whois/whois.go @@ -0,0 +1,319 @@ +package whois + +import ( + "bytes" + "dehasher/internal/sqlite" + "encoding/json" + "errors" + "fmt" + "go.uber.org/zap" + "io" + "net/http" +) + +type DehashedWHOISSearchRequest struct { + Include []string `json:"include,omitempty"` + Exclude []string `json:"exclude,omitempty"` + IPAddress string `json:"ip_address,omitempty"` + ReverseType string `json:"reverse_type,omitempty"` + Domain string `json:"domain,omitempty"` + MXAddress string `json:"mx_address,omitempty"` + NSAddress string `json:"ns_address,omitempty"` + SearchType string `json:"search_type,omitempty"` +} + +func WhoisSearch(domain, apiKey string) (sqlite.WhoIsLookupResult, error) { + var whois sqlite.WhoIsLookupResult + whoisSearchRequest := DehashedWHOISSearchRequest{ + Domain: domain, + SearchType: "whois", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return whois, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return whois, err + } + if res == nil { + return whois, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return whois, err + } + + err = json.Unmarshal(b, &whois) + if err != nil { + zap.L().Error("whois_search", + zap.String("message", "failed to unmarshal response body"), + zap.Error(err), + ) + fmt.Println("Error unmarshalling response body:", err) + fmt.Println("Response body:", string(b)) + return whois, err + } + + return whois, nil +} + +func WhoisHistory(domain, apiKey string) (sqlite.WhoIsHistory, error) { + var whois sqlite.WhoIsHistory + whoisSearchRequest := DehashedWHOISSearchRequest{ + Domain: domain, + SearchType: "whois-history", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return whois, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + zap.L().Info("whois_history", + zap.String("message", "response was not nil"), + ) + defer res.Body.Close() + } + if err != nil { + zap.L().Error("whois_history", + zap.String("message", "failed to perform request"), + zap.Error(err), + ) + return whois, err + } + if res == nil { + zap.L().Error("whois_history", + zap.String("message", "response was nil"), + ) + return whois, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + zap.L().Error("whois_history", + zap.String("message", "failed to read response body"), + zap.Error(err), + ) + return whois, err + } + + err = json.Unmarshal(b, &whois) + if err != nil { + zap.L().Error("whois_history", + zap.String("message", "failed to unmarshal response body"), + zap.Error(err), + ) + fmt.Println("Error unmarshalling response body:", err) + fmt.Println("Response body:", string(b)) + return whois, err + } + + return whois, nil +} + +func ReverseWHOIS(include []string, exclude []string, reverseType, apiKey string) (string, error) { + whoisSearchRequest := DehashedWHOISSearchRequest{ + Include: include, + Exclude: exclude, + ReverseType: reverseType, + SearchType: "reverse-whois", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return "", err + } + if res == nil { + return "", errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(b), nil +} + +func WhoisIP(ipAddress, apiKey string) ([]byte, error) { + whoisSearchRequest := DehashedWHOISSearchRequest{ + IPAddress: ipAddress, + SearchType: "reverse-ip", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return nil, err + } + if res == nil { + return nil, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + return b, nil +} + +func WhoisMX(mxAddress, apiKey string) (string, error) { + whoisSearchRequest := DehashedWHOISSearchRequest{ + MXAddress: mxAddress, + SearchType: "reverse-mx", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return "", err + } + if res == nil { + return "", errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(b), nil +} + +func WhoisNS(nsAddress, apiKey string) (string, error) { + whoisSearchRequest := DehashedWHOISSearchRequest{ + NSAddress: nsAddress, + SearchType: "reverse-ns", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return "", err + } + if res == nil { + return "", errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(b), nil +} + +func WhoisSubdomainScan(domain, apiKey string) (sqlite.WhoIsSubdomainScan, error) { + var whois sqlite.WhoIsSubdomainScan + whoisSearchRequest := DehashedWHOISSearchRequest{ + Domain: domain, + SearchType: "subdomain-scan", + } + reqBody, _ := json.Marshal(whoisSearchRequest) + req, err := http.NewRequest("POST", "https://api.dehashed.com/v2/whois/search", bytes.NewReader(reqBody)) + if err != nil { + return whois, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return whois, err + } + if res == nil { + return whois, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return whois, err + } + + err = json.Unmarshal(b, &whois) + if err != nil { + zap.L().Error("whois_subdomain_scan", + zap.String("message", "failed to unmarshal response body"), + zap.Error(err), + ) + fmt.Println("Error unmarshalling response body:", err) + fmt.Println("Response body:", string(b)) + return whois, err + } + + return whois, nil +} + +func GetWHOISCredits(apiKey string) (sqlite.WhoIsCredits, error) { + var whoisCredits sqlite.WhoIsCredits + + req, err := http.NewRequest("GET", "https://api.dehashed.com/v2/whois/credits", nil) + if err != nil { + return whoisCredits, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Dehashed-Api-Key", apiKey) + res, err := http.DefaultClient.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return whoisCredits, err + } + if res == nil { + return whoisCredits, errors.New("response was nil") + } + b, err := io.ReadAll(res.Body) + if err != nil { + return whoisCredits, err + } + + err = json.Unmarshal(b, &whoisCredits) + if err != nil { + zap.L().Error("get_whois_credits", + zap.String("message", "failed to unmarshal response body"), + zap.Error(err), + ) + fmt.Println("Error unmarshalling response body:", err) + fmt.Println("Response body:", string(b)) + return whoisCredits, err + } + + return whoisCredits, nil +} diff --git a/ondefend.txt b/ondefend.txt new file mode 100644 index 0000000..b065cfe --- /dev/null +++ b/ondefend.txt @@ -0,0 +1,455 @@ +Id: Uhs6wdMRv-jq3VAHW_Wc +Email: [selby@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [BRIAN SELBY] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402, US] +Phone: [7177181611] +DatabaseName: DemandScience by Pure Incubation + +Id: nWaSwdMRbgUelnkGA_uI +Email: [kriddle@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [KATHY RIDDLE] +Vin: [] +Address: [224 ST CHARLES WAY STE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: kRypwYMRZd5-FWZi9grp +Email: [david@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [DAVID DOLAN] +Vin: [] +Address: [224 ST CHARLES WAY SUITE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: AqCvwYMRs35batudGTqf +Email: [jpaff@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: IxLfwdMRkvXBjMmbeqpV +Email: [gplatt@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: Evite + +Id: zBbozYMRs35batudWm6j +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [df34221016bc31f1b86c14519] +Password: [] +HashedPassword: [] +HashType: +Name: [stiles@counseltrustcom] +Vin: [] +Address: [] +Phone: [] +DatabaseName: ShareThis.com + +Id: 37u0wdMRZd5-FWZiMghr +Email: [jyonkovitch@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [JASON YONKOVITCH] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402, US] +Phone: [7177181611] +DatabaseName: DemandScience by Pure Incubation + +Id: N0VExYMRkvXBjMmbnULI +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: Adobe + +Id: 1_I0wNMRv-jq3VAHQBKZ +Email: [dwells@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [David Wells] +Vin: [] +Address: [PO BOX 993, MADISON, AL] +Phone: [7174656685] +DatabaseName: Luxottica + +Id: TF69z5MRkvXBjMmbM3Mt +Email: [WCHILCOAT@COUNSELTRUST.COM] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [*16CpSaOtR9Ew=:None||UNKNOWN] +HashType: +Name: [WANDA CHILCOAT] +Vin: [] +Address: [2740 CLEARVIEW WAY, YORK, PA, 17403 2740 CLEARVIEW WAY YORK, PA, 17403] +Phone: [7177414611] +DatabaseName: AT&T + +Id: uxOYwdMRZd5-FWZiD3PA +Email: [paul.stevenson@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [PAUL STEVENSON] +Vin: [] +Address: [224 ST CHARLES WAY SUITE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: SGGjwNMRbgUelnkGHZSP +Email: [david.dolan@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [DAVID DOLAN] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402-4665, US 224 St. Charles Way Suite 100, York, PA, 17402, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: o5ixwYMRv-jq3VAHfzI5 +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [http://www.linkedin.com/in/bonnie-stiles-6a75b69] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5)] +HashType: +Name: [Bonnie M. Stiles] +Vin: [] +Address: [224 Saint Charles Way, York, PA, 17402, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: S37Qz5MRkvXBjMmbhwiO +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [] +Password: [stryker] +HashedPassword: [ebe73849058b47ae5eb163ecb134a4c4:||MD5 2cbf1f833918150abc54fa50c53dc8dbd0266a7e:||SHA1 09792abb14956e93ecf2a709affde76344ca3ed396e57ffcbf65436cce83fe3c:||SHA256 49301fa88e4fa3d5547c0074fdfd8f56c7e4468222fcfe181882a0c97970b9e79b8598ec13d7437791b5dfb201a1f9188a25dffeaf3ccb7342364cdc29f9317d:||SHA512 c3RyeWtlcg==:||BASE64] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: trustupdates.com (Cit0day) + +Id: Z8kqQdURkHaJUned-bBv +Email: [pstevenson@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [PAUL STEVENSON] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402-4665, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: YrsEwdMRs35batudzJVH +Email: [lkauffman@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: Evite + +Id: QRFKzdMRs35batud9GZ5 +Email: [DDOLAN@COUNSELTRUST.COM] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [*0x7gMMV53xScLt+SqJEA1hw==:None||UNKNOWN *0+HSfRH0LZIc=:None||UNKNOWN] +HashType: +Name: [DAVID DOLAN] +Vin: [] +Address: [3023 S ATLANTIC AV, DAYB SHR, FL, 32118 1330 HICKORY RUN DR, elizabethtown pa 17022-9202 ELIZABETHTOWN, PA, 17022] +Phone: [7178584702] +DatabaseName: AT&T + +Id: AapPzdMRbgUelnkGzAZi +Email: [ecrooks@counseltrust.com] +IpAddress: [] +Username: [edcrooks] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: MyFitnessPal + +Id: iPykdNMRZd5-FWZi6NTb +Email: [ddolan@counseltrust.com] +IpAddress: [] +Username: [] +Password: [5c480f68b] +HashedPassword: [35452b0e9763678ce20606aff52e399c:||MD5 ff7f60425e953d03e14024a7ed0a9f90bf2ea90e:||SHA1 0ba12cc8988083b4b1db7cecdb319bb6e6a01f19c383cfc01194ac79356e96ee:||SHA256 001ec68783cb044cc83a8d5f396916ff94a4550c37c056dbb0ee9dceede14736fccd39102575cf2f92d69c23822164b5644df77939b5364bae2e0f303c0ca5ce:||SHA512 NWM0ODBmNjhi:||BASE64] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: Collections + +Id: y6tqwdMRZd5-FWZiayk2 +Email: [crooks@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [Brandon Crooks] +Vin: [] +Address: [224 Saint Charles Way Ste 100, York, PA, 17402, US] +Phone: [7177181611] +DatabaseName: DemandScience by Pure Incubation + +Id: iLTwwYMRbgUelnkGiiW6 +Email: [paul@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [PAUL STEVENSON] +Vin: [] +Address: [224 ST CHARLES WAY SUITE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: mqOPwYMRv-jq3VAHGN4o +Email: [crooks@counseltrust.com] +IpAddress: [] +Username: [https://www.linkedin.com/in/brandon-crooks-733a315/] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5)] +HashType: +Name: [Brandon Crooks] +Vin: [] +Address: [224 Saint Charles Way Ste 100, York, PA, 17402, US] +Phone: [7177181611] +DatabaseName: DemandScience by Pure Incubation + +Id: lZ0LwdMRbgUelnkGVn_G +Email: [ecrooks@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5)] +HashType: +Name: [EDWARD CROOKS] +Vin: [] +Address: [224 SAINT CHARLES WAY, YORK, PA, 17402-4664, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: 5t5mwYMRiA0xW47jCGZa +Email: [ddolan@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [David Dolan] +Vin: [] +Address: [224 Saint Charles Way, York, PA, 17402-4644, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: 877zwNMRbgUelnkG9SWf +Email: [pstevenson@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [$2y$10$/UdtWJ1fbpOa0yJMAyDzkeuBHktRBahxlNkwZ2zW11DQiawcc3O8G:None||Blowfish(OpenBSD)] +HashType: +Name: [PAUL STEVENSON] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402-4665, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: PF7owNMRs35batud-AC5 +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [counseltrust.com:None||Cisco-PIX(MD5) COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [Bonnie M. Stiles] +Vin: [] +Address: [224 Saint Charles Way, York, PA, 17402, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: g1K4wdMRbgUelnkGiwtt +Email: [dolan@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [DAVID DOLAN] +Vin: [] +Address: [224 SCHARLES WY, YORK, PA, 17402, US] +Phone: [7177181601] +DatabaseName: DemandScience by Pure Incubation + +Id: 5uGAzYMRiA0xW47jZ0SG +Email: [gplatt@counseltrust.com] +IpAddress: [] +Username: [871fc67187061029d70c29640] +Password: [] +HashedPassword: [] +HashType: +Name: [gplatt@counseltrustcom] +Vin: [] +Address: [] +Phone: [] +DatabaseName: ShareThis.com + +Id: vfJvwYMRiA0xW47jrHLh +Email: [onnie@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [BONNIE STILES] +Vin: [] +Address: [224 ST CHARLES WAY SUITE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: GHKcwdMRbgUelnkGrEIP +Email: [onnie.stiles@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [COUNSELTRUST.COM:None||Cisco-PIX(MD5)] +HashType: +Name: [BONNIE STILES] +Vin: [] +Address: [224 ST CHARLES WAY SUITE 100, YORK, PA, 17402, US] +Phone: [7177181600] +DatabaseName: DemandScience by Pure Incubation + +Id: W56-x5MRv-jq3VAHVOq9 +Email: [stiles@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: LinkedIn + +Id: DuS0wYMRbgUelnkG2EyB +Email: [gplatt@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [$2a$05$b8axmc6a1T7pXoZXGq/KR.OGSSM.swfjqCMNRbGsIjL5is6stNX5S:None||Blowfish(OpenBSD)] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: ParkMobile + +Id: 11M_wYMRs35batud_mIn +Email: [pstevenson@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [] +Vin: [] +Address: [] +Phone: [] +DatabaseName: LinkedIn + +Id: QooWwdMRv-jq3VAH6-uY +Email: [gplatt@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [] +HashType: +Name: [GEOFFREY PLATT] +Vin: [] +Address: [224 SAINT CHARLES WAY STE 100, YORK, PA, 17402, US] +Phone: [7177181611] +DatabaseName: DemandScience by Pure Incubation + +Id: AItfnYMRv-jq3VAHEtYB +Email: [dwells@counseltrust.com] +IpAddress: [] +Username: [] +Password: [] +HashedPassword: [19a673bd64ef80608f70626c1441d578:None||MD5] +HashType: +Name: [] +Vin: [] +Address: [NU] +Phone: [] +DatabaseName: Chegg +