first commit
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# Dehasher
|
||||
## A cli tool built for interaction with the Dehash API
|
||||
|
||||
<div align="center">
|
||||
<img src="https://img.wanman.io/fUSu0/SaCUyEMe87.png/raw" style="width: 350px; height: auto" alt="Ar1ste1a" title="Ar1ste1a Offensive Security">
|
||||
</div>
|
||||
|
||||
# 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<redacted> -a ar1ste1a@<redacted> -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<redacted> -a ar1ste1a@domain.tld -E @target.com
|
||||
```
|
||||
|
||||
## Simple Credentials Query
|
||||
``` go
|
||||
# Provide credentials for emails matching @target.com
|
||||
dehasher -k ddq<redacted> -a ar1ste1a@domain.tld -E @target.com -C
|
||||
```
|
||||
|
||||
## Simple Query Returning Balance
|
||||
``` go
|
||||
# Provide credentials for emails matching @target.com
|
||||
dehasher -k ddq<redacted> -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<redacted> -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<redacted> -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<redacted> -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<redacted> -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<redacted> -a ar1ste1a@domain.tld -C -B -U admin -X u -x -o admins_file
|
||||
```
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+137
@@ -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()
|
||||
}
|
||||
+126
@@ -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
|
||||
}
|
||||
+303
@@ -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")
|
||||
}
|
||||
@@ -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}
|
||||
@@ -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
|
||||
+94
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+455
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user