14 Commits

Author SHA1 Message Date
KrakenTech a0f216508d Merge pull request #7 from Kraken-OffSec/sqlite-pure-go-driver
Sqlite pure go driver
2025-05-20 10:42:54 -04:00
Evan Hosinski 7d1b7a2225 Updated the makefile to reflect CGO_ENABLED=0 2025-05-20 10:41:09 -04:00
Evan Hosinski acf6336516 Added the kraken k to the .img folder 2025-05-20 10:39:08 -04:00
Evan Hosinski ce8079f72e Added the kraken k to the .img folder 2025-05-19 12:01:48 -04:00
KrakenTech 54ee9a192f Update README.md 2025-05-19 11:55:03 -04:00
KrakenTech 95791312d0 Update README.md 2025-05-17 14:45:01 -04:00
KrakenTech d4e626d574 Update README.md 2025-05-17 13:32:51 -04:00
KrakenTech dd2050ce64 Update README.md 2025-05-17 13:31:46 -04:00
KrakenTech 075f826816 Add files via upload 2025-05-17 13:19:15 -04:00
Evan Hosinski de34de60d9 Updated Readme to reflect new branding 2025-05-17 13:14:19 -04:00
Evan Hosinski 67c4e0394e Updated Readme to reflect new branding 2025-05-17 13:13:29 -04:00
Evan Hosinski 49424c1603 Merge remote-tracking branch 'origin/main' 2025-05-17 13:13:19 -04:00
Evan Hosinski d4db32c8b9 Updated Readme to reflect new branding 2025-05-17 12:58:37 -04:00
KrakenTech d2cc0d1022 Update Makefile 2025-05-17 11:11:33 -04:00
48 changed files with 335 additions and 158 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

+2 -2
View File
@@ -30,14 +30,14 @@ clean:
# Build for current platform # Build for current platform
build: build:
$(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) -ldflags "-X main.version=$(VERSION)" crowsnest.go CGO_ENABLED=0 $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) -ldflags "-X main.version=$(VERSION)" crowsnest.go
# Build for all platforms # Build for all platforms
build-all: clean build-all: clean
@for platform in $(PLATFORMS); do \ @for platform in $(PLATFORMS); do \
for arch in $(ARCHS); do \ for arch in $(ARCHS); do \
echo "Building for $$platform/$$arch..."; \ echo "Building for $$platform/$$arch..."; \
GOOS=$$platform GOARCH=$$arch $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch -ldflags "-X main.version=$(VERSION)" crowsnest.go; \ GOOS=$$platform GOARCH=$$arch CGO_ENABLED=0 $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch -ldflags "-X main.version=$(VERSION)" crowsnest.go; \
if [ "$$platform" = "windows" ]; then \ if [ "$$platform" = "windows" ]; then \
mv $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch.exe; \ mv $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch.exe; \
fi; \ fi; \
+75 -69
View File
@@ -1,5 +1,5 @@
<div align="center"> <div align="center">
<img src=.img/crowsnest.png style="width: 500px; height: auto" alt="Ar1ste1a" title="CrowsNest Logo"> <img src=.img/crowsnest.png style="width: 500px; height: auto" alt="CrowsNest Logo" title="CrowsNest Logo">
</div> </div>
### A CLI tool for seamless interaction with the Dehashed and Hunter.io APIs. ### A CLI tool for seamless interaction with the Dehashed and Hunter.io APIs.
@@ -28,7 +28,7 @@
To begin, clone the repository To begin, clone the repository
``` bash-session ``` bash-session
git clone https://github.com/Ar1ste1a/CrowsNest.git git clone https://github.com/Kraken-OffSec/CrowsNest.git
cd crowsnest cd crowsnest
go build crowsnest.go go build crowsnest.go
``` ```
@@ -58,33 +58,28 @@ To configure the database location:
## 🌐 Dehashed ## 🌐 Dehashed
### Initial Setup ### Initial Setup
CrowsNest requires an API key from Dehashed. Set it up with: CrowsNest requires an API key from Dehashed. Set it up with:
![Alt text](.img/set-dehashed.png "Set Dehashed Key")
```bash ```bash
ar1ste1a@kali:~$ crowsnest set-dehashed <redacted> ar1ste1a@kali:~$ crowsnest set-dehashed <redacted>
``` ```
### Simple Query ### Simple Query
CrowsNest can be used simply for example to query for credentials matching a given email domain. CrowsNest can be used simply for example to query for credentials matching a given email domain.
![Alt text](.img/simple_query.png "Simple Query")
``` go ``` go
# Provide credentials for domains matching target.com # Provide credentials for domains matching target.com
crowsnest api -D target.com -C crowsnest dehashed -D target.com
``` ```
### Simple Credentials Query ### Simple Credentials Query
CrowsNest can also be used to return only credentials for a given query. CrowsNest can also be used to return only credentials for a given query.
![Alt text](.img/simple_creds_query.png "Creds Only Query")
``` go ``` go
# Provide credentials for emails matching @target.com # Provide credentials for emails matching @target.com
crowsnest api -E @target.com -C crowsnest dehashed -D @target.com -C
```
### Multiple Match Query
CrowsNest is capable of handling multiple queries for the same field.
This is useful for when you want to search for multiple domains, or multiple usernames.
``` go
# Provide credentials for domains matching target.com and target2.com, retrieving only credentials
crowsnest api -D target.com,target2.com -C
``` ```
### Wildcard Query ### Wildcard Query
@@ -92,34 +87,47 @@ CrowsNest is capable of handling wildcard queries.
A wildcard query cannot begin with a wildcard. A wildcard query cannot begin with a wildcard.
This is a limitation of the Dehashed API. This is a limitation of the Dehashed API.
An asterisk can be used to denote multiple characters, and a question mark can be used to denote a single character. An asterisk can be used to denote multiple characters, and a question mark can be used to denote a single character.
![Alt text](.img/wildcard_sample.png "Wildcard Query") <br>
![Alt text](.img/wildcard_query.png "Wildcard Query")
``` go ``` go
# Provide credentials for emails matching @target.com and @target2.com # Provide credentials for emails matching @target.com and @target2.com
crowsnest api -E @target?.com -C -W crowsnest dehashed -E @target?.com -C -W
``` ```
### Email Query ### Email Query
Dehashed has dictated that emails should be searched in the following format: Dehashed has dictated that emails should be searched in the following format:
`email:target.name&domain:target.com`. `email:target.name&domain:target.com`.
As such, to query an email, please use the following format (note, wildcard is not required but can be useful): As such, to query an email, please use the following format (note, wildcard is not required but can be useful):
<br>
*see photo above in Wildcard Query*
``` go ``` go
# Provide credentials for emails matching target.*@target.com # Provide credentials for emails matching target.*@target.com
crowsnest api -W -E 'target*' -D target.com crowsnest dehashed -W -E 'target*' -D target.com
``` ```
You may also query the domain and find emails as well You may also query the domain and find emails as well
``` go ``` go
# Provide credentials for emails matching target.com # Provide credentials for emails matching target.com
crowsnest api -D target.com -C crowsnest dehashed -D target.com -C
``` ```
### Combining Queries
CrowsNest is capable of combining queries.
This is useful for when you want to query for credentials matching a given email or domain, but only for a specific username.
![Alt text](.img/combining_queries.png "Combined Query")
``` go
# Provide credentials for emails matching @target.com and username containing 'admin'
crowsnest dehashed -D target.com -U admin
```
### Regex Query ### Regex Query
CrowsNest is capable of handling regex queries. CrowsNest is capable of handling regex queries.
Simply denote regex queries with the `-R` flag. Simply denote regex queries with the `-R` flag.
Place all regex queries in quotes with the corresponding query flag in single quotes. Place all regex queries in quotes with the corresponding query flag in single quotes.
<br>
!!!! *Currently, the Regex Operators appear to be broken. I am waiting on a response from Dehashed* !!!!
``` go ``` go
# Return matches for emails matching this given regex query # Return matches for emails matching this given regex query
crowsnest api -R -E '[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)?@target.com' crowsnest dehashed -R -E 'joh?n(ath[oa]n)' -D hotmail.com'
``` ```
### Output Text (default JSON) ### Output Text (default JSON)
@@ -129,7 +137,7 @@ To change the output format, use the `-f` flag.
CrowsNest currently supports JSON, YAML, XML, and TEXT output formats. CrowsNest currently supports JSON, YAML, XML, and TEXT output formats.
``` go ``` go
# Return matches for usernames exactly matching "admin" and write to text file 'admins_file.txt' # Return matches for usernames exactly matching "admin" and write to text file 'admins_file.txt'
crowsnest api -U admin -o admins_file -f txt crowsnest dehashed -U admin -o admins_file -f txt
``` ```
--- ---
@@ -141,7 +149,7 @@ The WhoIs Lookups require a separate API Credit from the Dehashed API.
### Domain Lookup ### Domain Lookup
CrowsNest can perform a domain lookup for a given domain. CrowsNest can perform a domain lookup for a given domain.
This provides a tree view of the domain's WHOIS information. This provides a tree view of the domain's WHOIS information.
![Alt text](.img/tree_whois_lookup.png "WhoIs Tree View") ![Alt text](.img/whois_domain.png "WhoIs Tree View")
```bash ```bash
# Perform a WHOIS lookup for example.com # Perform a WHOIS lookup for example.com
crowsnest whois -d example.com crowsnest whois -d example.com
@@ -156,10 +164,20 @@ The history lookup is immediately written to file and not displayed in the termi
crowsnest whois -d example.com -H crowsnest whois -d example.com -H
``` ```
### Subdomain Scan
CrowsNest can perform a subdomain scan for a given domain.
This provides a list of all subdomains that match the given query.
![Alt text](.img/whois_subdomain.png "WhoIs Tree View")
```bash
# Perform a WHOIS subdomain scan for google.com
crowsnest whois -d google.com -s
```
### Reverse WHOIS Lookup ### Reverse WHOIS Lookup
CrowsNest can perform a reverse WHOIS lookup for given criteria. CrowsNest can perform a reverse WHOIS lookup for given criteria.
This provides a list of all domains that match the given query. This provides a list of all domains that match the given query.
The reverse WHOIS lookup is immediately written to file and not displayed in the terminal or stored in the database. The reverse WHOIS lookup is immediately written to file and not stored in the database.
![Alt text](.img/whois_reverse.png "WhoIs Tree View")
```bash ```bash
# Perform a reverse WHOIS lookup for example.com # Perform a reverse WHOIS lookup for example.com
crowsnest whois -I example.com crowsnest whois -I example.com
@@ -168,7 +186,7 @@ crowsnest whois -I example.com
### IP Lookup ### IP Lookup
CrowsNest can perform a reverse IP lookup for a given IP address. CrowsNest can perform a reverse IP lookup for a given IP address.
This provides a list of all domains that match the given query. This provides a list of all domains that match the given query.
![Alt text](.img/reverse_ip_lookup.png "WhoIs Tree View") ![Alt text](.img/whois_ip.png "WhoIs View")
```bash ```bash
# Perform a reverse IP lookup for 8.8.8.8 # Perform a reverse IP lookup for 8.8.8.8
crowsnest whois -i 8.8.8.8 crowsnest whois -i 8.8.8.8
@@ -177,28 +195,21 @@ crowsnest whois -i 8.8.8.8
### MX Lookup ### MX Lookup
CrowsNest can perform an MX lookup for a given MX hostname. CrowsNest can perform an MX lookup for a given MX hostname.
This provides a list of all domains that match the given query. This provides a list of all domains that match the given query.
![Alt text](.img/mx_lookup.png "WhoIs Tree View") ![Alt text](.img/whois_mx.png "WhoIs Tree View")
```bash ```bash
# Perform a reverse MX lookup for google.com # Perform a reverse MX lookup for google.com
crowsnest whois -m google.com crowsnest whois -m stmp.google.com
``` ```
### NS Lookup ### NS Lookup
CrowsNest can perform an NS lookup for a given NS hostname. CrowsNest can perform an NS lookup for a given NS hostname.
This provides a list of all domains that match the given query. This provides a list of all domains that match the given query.
The picture below also includes the --debug global flag. The picture below also includes the --debug global flag.
![Alt text](.img/debug_ns_search.png "WhoIs Tree View") ![Alt text](.img/whois_ns.png "WhoIs Tree View")
```bash ```bash
# Perform a reverse NS lookup for google.com # Perform a reverse NS lookup for google.com
crowsnest whois -n google.com crowsnest whois -n google.com
``` ```
### Subdomain Scan
CrowsNest can perform a subdomain scan for a given domain.
This provides a list of all subdomains that match the given query.
![Alt text](.img/subdomains_lookup.png "WhoIs Tree View")
```bash
# Perform a WHOIS subdomain scan for google.com
crowsnest whois -d google.com -s
```
--- ---
@@ -206,6 +217,7 @@ crowsnest whois -d google.com -s
CrowsNest supports Hunter.io lookups. CrowsNest supports Hunter.io lookups.
Hunter.io lookups require a separate API Key from the Dehashed API. Hunter.io lookups require a separate API Key from the Dehashed API.
This can be set using the `set-hunter` command. This can be set using the `set-hunter` command.
![Alt text](.img/set-hunter.png "Set Dehashed Key")
```bash ```bash
# Set the Hunter.io API key # Set the Hunter.io API key
crowsnest set-hunter <redacted> crowsnest set-hunter <redacted>
@@ -213,26 +225,17 @@ crowsnest set-hunter <redacted>
### Domain Search ### Domain Search
CrowsNest can perform a domain search for a given domain. CrowsNest can perform a domain search for a given domain.
This provides information about company including a description, social media information and any technologies in use. This provides information about company including a description, social media information and any technologies in use.<br>
![Alt text](.img/hunter_domain_search.png "Hunter.io Domain Search") ![Alt text](.img/hunter_domain.png "Hunter.io Domain Search")
```bash ```bash
# Perform a Hunter.io domain search for example.com # Perform a Hunter.io domain search for example.com
crowsnest hunter -d example.com -D crowsnest hunter -d example.com -D
``` ```
### Email Finder
CrowsNest can perform an email finder search for a given domain, first name, and last name.
This provides information about a user including a confidence score, and any social media accounts linked to a first name, last name and email.
![Alt text](.img/hunter_email_finder.png "Hunter.io Email Finder")
```bash
# Perform a Hunter.io email finder search for example.com
crowsnest hunter -d example.com -F John -L Doe -E
```
### Email Verification ### Email Verification
CrowsNest can perform an email verification search for a given email. CrowsNest can perform an email verification search for a given email.
This provides a verification and score of a given email address. This provides a verification and score of a given email address.
![Alt text](.img/email_verification.png "Hunter.io Email Verification") ![Alt text](.img/hunter_emailverification.png "Hunter.io Email Verification")
```bash ```bash
# Perform a Hunter.io email verification search for example@target.com # Perform a Hunter.io email verification search for example@target.com
crowsnest hunter -e example@target.com -V crowsnest hunter -e example@target.com -V
@@ -241,16 +244,25 @@ crowsnest hunter -e example@target.com -V
### Company Enrichment ### Company Enrichment
CrowsNest can perform a company enrichment search for a given domain. CrowsNest can perform a company enrichment search for a given domain.
This provides information about a company given its domain. This provides information about a company given its domain.
![Alt text](.img/company_enrichment.png "Hunter.io Company Enrichment") ![Alt text](.img/hunter_company.png "Hunter.io Company Enrichment")
```bash ```bash
# Perform a Hunter.io company enrichment search for example.com # Perform a Hunter.io company enrichment search for example.com
crowsnest hunter -d example.com -C crowsnest hunter -d example.com -C
``` ```
### Email Finder
CrowsNest can perform an email finder search for a given domain, first name, and last name.
This provides information about a user including a confidence score, and any social media accounts linked to a first name, last name and email.
![Alt text](.img/hunter_emailfind.png "Hunter.io Email Finder")
```bash
# Perform a Hunter.io email finder search for example.com
crowsnest hunter -d example.com -F John -L Doe -E
```
### Person Enrichment ### Person Enrichment
CrowsNest can perform a person enrichment search for a given email. CrowsNest can perform a person enrichment search for a given email.
This provides information about a user given an email address.. This provides information about a user given an email address..
![Alt text](.img/person_enrichment.png "Hunter.io Person Enrichment") ![Alt text](.img/hunter_person.png "Hunter.io Person Enrichment")
```bash ```bash
# Perform a Hunter.io person enrichment search for example@target.com # Perform a Hunter.io person enrichment search for example@target.com
crowsnest hunter -e example@target.com -P crowsnest hunter -e example@target.com -P
@@ -265,7 +277,13 @@ This is a combination of the company and person enrichments given an email addre
# Perform a Hunter.io combined enrichment search for example@target.com # Perform a Hunter.io combined enrichment search for example@target.com
crowsnest hunter -e example@target.com -B crowsnest hunter -e example@target.com -B
``` ```
## Debugging
CrowsNest supports debugging. This can be enabled using the `--debug` flag in the root command.
![Alt text](.img/crowsnest_debugging_global.png "Debugging")
```bash
# Perform a Hunter.io combined enrichment search for example@target.com with debugging enabled
crowsnest --debug hunter -e example@target.com -B
```
--- ---
## 📊 Database Querying ## 📊 Database Querying
CrowsNest stores query results in a local database. CrowsNest stores query results in a local database.
@@ -276,10 +294,10 @@ This database also includes WhoIs Information and Subdomain Scan results, but do
## Simple Query ## Simple Query
#### It's possible to query the database using shorthand and without knowing any SQL at all. #### It's possible to query the database using shorthand and without knowing any SQL at all.
#### The following queries the results table where username is not null, only showing the username, email and password columns. #### The following queries the results table where username is not null, only showing the username, email and password columns.
![Alt text](.img/simple_query_db.png "Simple Query") ![Alt text](.img/query_simple.png "Simple Query")
#### You may also add in a simple query using the `-q` flag. The following displays a 'LIKE' clause on the email column. #### You may also add in a simple query using the `-q` flag. The following displays a 'LIKE' clause on the email column.
#### Note the '%\<clause\>%' is still required. #### Note the '%\<clause\>%' is still required.
![Alt text](.img/simple_where.png "Simple Query") ![Alt text](.img/query_where.png "Simple Query")
```bash ```bash
# Query the database for all results containing the word 'admin' in the username # Query the database for all results containing the word 'admin' in the username
@@ -288,7 +306,7 @@ crowsnest query -t results -q "username LIKE '%admin%'"
## Raw SQL Queries ## Raw SQL Queries
![Alt text](.img/raw_query_db.png "Raw Query") ![Alt text](.img/query_raw.png "Raw Query")
CrowsNest also supports raw SQL queries. This is useful for when you want to query for specific information. CrowsNest also supports raw SQL queries. This is useful for when you want to query for specific information.
```bash ```bash
@@ -306,6 +324,7 @@ crowsnest query -t results -q "username LIKE '%admin%'" -n username,email,passwo
## Listing Tables and Columns ## Listing Tables and Columns
CrowsNest supports listing all available tables and columns. CrowsNest supports listing all available tables and columns.
This is useful for when you want to query for specific information. This is useful for when you want to query for specific information.
![Alt text](.img/query_alltables.png "List All Tables")
```bash ```bash
# List all available tables and columns # List all available tables and columns
crowsnest query -a crowsnest query -a
@@ -336,6 +355,7 @@ CrowsNest supports exporting results to a file.
This is useful for when you want to requery for specific information without touching the Dehashed API. This is useful for when you want to requery for specific information without touching the Dehashed API.
The export subcommand supports all the same options as the query subcommand. The export subcommand supports all the same options as the query subcommand.
The export subcommand also supports file naming and output format control. The export subcommand also supports file naming and output format control.
![Alt text](.img/export_raw.png "Export Results")
```bash ```bash
# Export all results containing the word 'admin' in the username to a text file # Export all results containing the word 'admin' in the username to a text file
crowsnest export -t results -q "username LIKE '%admin%'" -o admins_file -f txt crowsnest export -t results -q "username LIKE '%admin%'" -o admins_file -f txt
@@ -346,8 +366,8 @@ crowsnest export -t results -q "username LIKE '%admin%'" -o admins_file -f txt
CrowsNest uses the `zap` logging library for logging. The logs are stored in `~/.local/share/crowsnest/logs`. CrowsNest uses the `zap` logging library for logging. The logs are stored in `~/.local/share/crowsnest/logs`.
The logs can be easily queried from the crowsnest CLI. The logs can be easily queried from the crowsnest CLI.
### Logs Dates ### Filtering by Date
#### crowsnest utilized 'easy time' to determine the appropriate time for a given query. #### CrowsNest utilizes 'easy time' to determine the appropriate time for a given query.
![Alt text](.img/easy_time_parsing.png "Easy Time") ![Alt text](.img/easy_time_parsing.png "Easy Time")
#### You may also used dates mixed with easy time to perform queries. #### You may also used dates mixed with easy time to perform queries.
![Alt text](.img/mixed_time_query.png "Mixed Time") ![Alt text](.img/mixed_time_query.png "Mixed Time")
@@ -374,27 +394,13 @@ crowsnest logs -s "last 24 hours"
crowsnest logs -s "05-01-2025" -v error,fatal crowsnest logs -s "05-01-2025" -v error,fatal
``` ```
## 🎉 Sample Run
```bash
ar1ste1a@kali:~$ crowsnest api -D <redacted>.com -o <redacted> -f json
Making 3 Requests for 10000 Records (30000 Total)
[*] Querying Dehashed API...
[*] Performing Request...
[+] Retrieved 2740 Records
[-] Not Enough Entries, ending queries
[+] Discovered 10 Credentials
[*] Writing entries to file: <redacted>.json
[*] Success
[*] Completing Process
```
## 🤝 Contributing ## 🤝 Contributing
Contributions are welcome! Submit a pull request to help improve CrowsNest. Contributions are welcome! Submit a pull request to help improve CrowsNest.
## [Buy Me A Coffee](https://buymeacoffee.com/ehosinskiz)
<div align="center"> <div align="center">
<img src="https://img.wanman.io/fUSu0/jUtovIFE52.png/raw" style="width: 350px; height: auto" alt="Ar1ste1a" title="Ar1ste1a Offensive Security"> <img src=.img/kraken_k.png style="width: 350px; height: auto" alt="Ar1ste1a" title="Ar1ste1a Offensive Security">
</div> </div>
## **Release The Kraken** ## **Release The Kraken**
+1 -2
View File
@@ -166,7 +166,7 @@ var (
fmt.Println("Email Find Result:") fmt.Println("Email Find Result:")
var ( var (
headers = []string{"Email", "Score", "Domain", "Accept All", "Position", "Twitter", "Linkedin", "Phone Number", "Company", "Sources", "Verification"} headers = []string{"Email", "Score", "Domain", "Accept All", "Position", "Twitter", "Linkedin", "Phone Number", "Company", "Verification"}
rows [][]string rows [][]string
) )
@@ -180,7 +180,6 @@ var (
result.LinkedinURL, result.LinkedinURL,
result.PhoneNumber, result.PhoneNumber,
result.Company, result.Company,
fmt.Sprintf("%v", result.Sources),
fmt.Sprintf("%v", result.Verification), fmt.Sprintf("%v", result.Verification),
}) })
+45 -10
View File
@@ -26,7 +26,7 @@ func init() {
whoisCmd.Flags().StringVarP(&whoisNSAddress, "ns", "n", "", "NS hostname for reverse NS lookup") whoisCmd.Flags().StringVarP(&whoisNSAddress, "ns", "n", "", "NS hostname for reverse NS lookup")
whoisCmd.Flags().StringVarP(&whoisInclude, "include", "I", "", "Up to 4 Terms to include in reverse WHOIS search (comma-separated)") whoisCmd.Flags().StringVarP(&whoisInclude, "include", "I", "", "Up to 4 Terms to include in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisExclude, "exclude", "E", "", "Up to 4 Terms to exclude in reverse WHOIS search (comma-separated)") whoisCmd.Flags().StringVarP(&whoisExclude, "exclude", "E", "", "Up to 4 Terms to exclude in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "registrant", "Type of reverse WHOIS search ([default] current or historic)") whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "current", "Type of reverse WHOIS search ([default] current or historic)")
whoisCmd.Flags().StringVarP(&whoisOutputFormat, "format", "f", "text", "Output format (text, json)") whoisCmd.Flags().StringVarP(&whoisOutputFormat, "format", "f", "text", "Output format (text, json)")
whoisCmd.Flags().StringVarP(&whoisOutputFile, "output", "o", "whois", "File to output results to including extension") whoisCmd.Flags().StringVarP(&whoisOutputFile, "output", "o", "whois", "File to output results to including extension")
whoisCmd.Flags().BoolVarP(&whoisShowCredits, "credits", "c", false, "Show remaining WHOIS credits") whoisCmd.Flags().BoolVarP(&whoisShowCredits, "credits", "c", false, "Show remaining WHOIS credits")
@@ -118,6 +118,7 @@ var (
if whoisDomain != "" { if whoisDomain != "" {
fmt.Println("[*] Performing WHOIS lookup...") fmt.Println("[*] Performing WHOIS lookup...")
if !whoisHistory && !whoisSubdomainScan {
// Domain lookup // Domain lookup
result, err := w.WhoisSearch(whoisDomain) result, err := w.WhoisSearch(whoisDomain)
if err != nil { if err != nil {
@@ -170,6 +171,7 @@ var (
zap.String("message", "no whois record to write to file"), zap.String("message", "no whois record to write to file"),
) )
} }
}
if whoisHistory { if whoisHistory {
filename := whoisOutputFile + "_history" filename := whoisOutputFile + "_history"
@@ -253,7 +255,6 @@ var (
) )
fmt.Printf("Error performing subdomain scan: %v\n", err) fmt.Printf("Error performing subdomain scan: %v\n", err)
} else { } else {
fmt.Println("Subdomain Scan:")
err = sqlite.StoreWhoisSubdomainRecords(subdomains) err = sqlite.StoreWhoisSubdomainRecords(subdomains)
if err != nil { if err != nil {
if debugGlobal { if debugGlobal {
@@ -290,6 +291,7 @@ var (
} }
// Store the subdomains // Store the subdomains
fmt.Println("Subdomain Scan:")
pretty.Table(headers, rows) pretty.Table(headers, rows)
} else { } else {
@@ -492,6 +494,12 @@ var (
} }
if whoisInclude != "" || whoisExclude != "" { if whoisInclude != "" || whoisExclude != "" {
if debugGlobal {
debug.PrintInfo("performing reverse whois")
debug.PrintInfo("include: " + whoisInclude)
debug.PrintInfo("exclude: " + whoisExclude)
debug.PrintInfo("reverse type: " + whoisReverseType)
}
// Reverse WHOIS // Reverse WHOIS
includeTerms := []string{} includeTerms := []string{}
if whoisInclude != "" { if whoisInclude != "" {
@@ -511,18 +519,11 @@ var (
} }
} }
if whoisReverseType == "" {
if debugGlobal {
debug.PrintInfo("reverse type not specified, using default")
}
whoisReverseType = "current"
} else {
toLower := strings.ToLower(whoisReverseType) toLower := strings.ToLower(whoisReverseType)
if toLower != "current" && toLower != "historic" { if toLower != "current" && toLower != "historic" {
fmt.Println("[!] Error: Invalid reverse type. Must be 'current' or 'historic'.") fmt.Println("[!] Error: Invalid reverse type. Must be 'current' or 'historic'.")
return return
} }
}
fmt.Println("[*] Performing reverse WHOIS lookup...") fmt.Println("[*] Performing reverse WHOIS lookup...")
result, err := w.ReverseWHOIS(includeTerms, excludeTerms, whoisReverseType) result, err := w.ReverseWHOIS(includeTerms, excludeTerms, whoisReverseType)
@@ -538,8 +539,42 @@ var (
fmt.Printf("Error performing reverse WHOIS: %v\n", err) fmt.Printf("Error performing reverse WHOIS: %v\n", err)
return return
} }
// Write to file
if len(result.DomainsList) > 0 {
fmt.Printf("[*] Writing reverse WHOIS results to file: %s%s\n", whoisOutputFile, fType.Extension())
err = export.WriteIStringToFile(result, whoisOutputFile, fType)
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to write reverse whois to file")
debug.PrintError(err)
}
zap.L().Error("write_reverse_whois",
zap.String("message", "failed to write reverse whois to file"),
zap.Error(err),
)
fmt.Printf("Error writing reverse WHOIS to file: %v\n", err)
}
fmt.Println("Reverse WHOIS Result:") fmt.Println("Reverse WHOIS Result:")
fmt.Println(result) fmt.Printf("Total Domains: %d\n", result.DomainsCount)
var (
headers = []string{"Domain"}
rows [][]string
)
for _, r := range result.DomainsList {
rows = append(rows, []string{r})
}
pretty.Table(headers, rows)
} else {
fmt.Println("[!] No results found")
zap.L().Info("reverse_whois",
zap.String("message", "no results found"),
)
}
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) checkBalance(w)
+8
View File
@@ -8,6 +8,7 @@ require (
github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/lipgloss v1.1.0
github.com/dgraph-io/badger/v4 v4.7.0 github.com/dgraph-io/badger/v4 v4.7.0
github.com/fatih/color v1.15.0 github.com/fatih/color v1.15.0
github.com/glebarez/sqlite v1.11.0
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/winking324/rzap v0.1.0 github.com/winking324/rzap v0.1.0
go.uber.org/zap v1.20.0 go.uber.org/zap v1.20.0
@@ -27,9 +28,11 @@ require (
github.com/charmbracelet/x/term v0.2.1 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
@@ -40,6 +43,7 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/muesli/termenv v0.16.0 // indirect github.com/muesli/termenv v0.16.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
@@ -53,4 +57,8 @@ require (
golang.org/x/sys v0.31.0 // indirect golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
) )
+19
View File
@@ -34,6 +34,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -43,6 +47,10 @@ github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6F
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -75,6 +83,9 @@ 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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -164,3 +175,11 @@ gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= 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 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
+5 -1
View File
@@ -166,7 +166,11 @@ func (dcv2 *DehashedClientV2) Search(searchRequest DehashedSearchRequest) (int,
zap.String("message", "preparing search request"), zap.String("message", "preparing search request"),
) )
} }
reqBody, _ := json.Marshal(searchRequest)
// Create a copy of the search request to avoid modifying the original
requestCopy := searchRequest
reqBody, _ := json.Marshal(requestCopy)
if dcv2.debug { if dcv2.debug {
j := string(reqBody) j := string(reqBody)
+79 -15
View File
@@ -3,11 +3,12 @@ package dehashed
import ( import (
"crowsnest/internal/debug" "crowsnest/internal/debug"
"crowsnest/internal/export" "crowsnest/internal/export"
"crowsnest/internal/pretty"
"crowsnest/internal/sqlite" "crowsnest/internal/sqlite"
"encoding/json"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
"os" "os"
"strings"
) )
// Dehasher is a struct for querying the Dehashed API // Dehasher is a struct for querying the Dehashed API
@@ -208,12 +209,10 @@ func (dh *Dehasher) buildRequest() {
// parseResults parses the results and writes them to a file // parseResults parses the results and writes them to a file
func (dh *Dehasher) parseResults() { func (dh *Dehasher) parseResults() {
var data []byte
zap.L().Info("extracting_credentials") zap.L().Info("extracting_credentials")
results := dh.client.GetResults() results := dh.client.GetResults()
creds := results.ExtractCredentials() creds := results.ExtractCredentials()
fmt.Printf("\n\t[+] Discovered %d Credentials", len(creds)) fmt.Printf(" [+] Discovered %d Credentials\n", len(creds))
err := sqlite.StoreDehashedCreds(creds) err := sqlite.StoreDehashedCreds(creds)
if err != nil { if err != nil {
zap.L().Error("store_creds", zap.L().Error("store_creds",
@@ -234,28 +233,93 @@ func (dh *Dehasher) parseResults() {
zap.L().Info("results_stored", zap.Int("count", len(results.Results))) zap.L().Info("results_stored", zap.Int("count", len(results.Results)))
if len(results.Results) > 0 { if len(results.Results) > 0 {
fmt.Printf("\n\t[*] Writing entries to file: %s.%s", dh.options.OutputFile, dh.options.OutputFormat.String()) var (
headers = []string{"Email", "Username", "Password"}
rows [][]string
)
fmt.Printf(" [*] Writing entries to file: %s.%s\n", dh.options.OutputFile, dh.options.OutputFormat.String())
if !dh.options.CredsOnly { if !dh.options.CredsOnly {
err := export.WriteToFile(results, dh.options.OutputFile, dh.options.OutputFormat) err := export.WriteToFile(results, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil { if err != nil {
fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err) fmt.Printf("[!] Error Writing to file: %v Outputting to terminal.\n", err)
data, err = json.MarshalIndent(results, "", " ") zap.L().Error("write_results",
fmt.Println(string(data)) zap.String("message", "failed to write results to file"),
os.Exit(0) zap.Error(err),
)
} else { } else {
fmt.Println("\n\t\t[*] Success\n") fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing results table")
}
headers = []string{"Name", "Email", "Username", "Password", "Address", "Phone", "Social", "Crypto Address", "Company"}
if len(results.Results) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
r := results.Results[i]
rows = append(rows, []string{
strings.Join(r.Name, ", "), strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "), strings.Join(r.Password, ", "),
strings.Join(r.Address, ", "), strings.Join(r.Phone, ", "),
strings.Join(r.Social, ", "), strings.Join(r.CryptoCurrencyAddress, ", "),
strings.Join(r.Company, ", ")})
} }
} else { } else {
for _, r := range results.Results {
rows = append(rows, []string{
strings.Join(r.Name, ", "), strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "), strings.Join(r.Password, ", "),
strings.Join(r.Address, ", "), strings.Join(r.Phone, ", "),
strings.Join(r.Social, ", "), strings.Join(r.CryptoCurrencyAddress, ", "),
strings.Join(r.Company, ", ")})
}
}
// Print Table
pretty.Table(headers, rows)
} else {
if dh.debug {
debug.PrintInfo("extracting credentials")
}
creds := results.ExtractCredentials() creds := results.ExtractCredentials()
if dh.debug {
debug.PrintInfo("writing credentials to file")
}
err := export.WriteCredsToFile(creds, dh.options.OutputFile, dh.options.OutputFormat) err := export.WriteCredsToFile(creds, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil { if err != nil {
fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err) fmt.Printf("[!] Error Writing to file: %v\n Outputting to terminal.", err)
data, err = json.MarshalIndent(creds, "", " ") zap.L().Error("write_creds",
fmt.Println(string(data)) zap.String("message", "failed to write creds to file"),
os.Exit(0) zap.Error(err),
)
} else { } else {
fmt.Println("\n\t\t[*] Success\n") fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing credentials table")
}
headers = []string{"Email", "Username", "Password"}
if len(creds) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
c := creds[i]
rows = append(rows, []string{c.Email, c.Username, c.Password})
}
} else {
for _, c := range creds {
rows = append(rows, []string{c.Email, c.Username, c.Password})
} }
} }
// Print Table
pretty.Table(headers, rows)
}
} else {
fmt.Println(" [-] No results found")
} }
} }
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"gorm.io/driver/sqlite" sql "github.com/glebarez/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
) )
@@ -42,7 +42,7 @@ func InitDB(dbPath string) (*gorm.DB, error) {
} }
zap.L().Info("Opening database", zap.String("finalPath", finalDbPath)) zap.L().Info("Opening database", zap.String("finalPath", finalDbPath))
db, err := gorm.Open(sqlite.Open(finalDbPath), &gorm.Config{ db, err := gorm.Open(sql.Open(finalDbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
}) })
if err != nil { if err != nil {
+17
View File
@@ -628,3 +628,20 @@ func StoreWhoisLookup(lookup []LookupResult) error {
return lastErr return lastErr
} }
// ReverseWhoisResponse represents the response from a reverse WHOIS lookup
type ReverseWhoisResponse struct {
RemainingCredits int `json:"remaining_credits"`
Data ReverseWhoisData `json:"data"`
}
// ReverseWhoisData contains the domain count and list from a reverse WHOIS lookup
type ReverseWhoisData struct {
DomainsCount int `json:"domainsCount"`
DomainsList []string `json:"domainsList"`
NextPageSearchAfter *string `json:"nextPageSearchAfter"`
}
func (rwd ReverseWhoisData) String() string {
return fmt.Sprintf("Domains Count: %d\nDomains List: %v\nNext Page Search After: %v\n", rwd.DomainsCount, rwd.DomainsList, rwd.NextPageSearchAfter)
}
+32 -7
View File
@@ -298,7 +298,9 @@ func (w *DehashedWhoIs) WhoisHistory(domain string) ([]sqlite.HistoryRecord, err
return whois.Data.Records, nil return whois.Data.Records, nil
} }
func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverseType string) (string, error) { func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverseType string) (sqlite.ReverseWhoisData, error) {
var whois sqlite.ReverseWhoisData
if w.debug { if w.debug {
debug.PrintInfo("performing reverse whois search") debug.PrintInfo("performing reverse whois search")
zap.L().Info("reverse_whois_debug", zap.L().Info("reverse_whois_debug",
@@ -329,7 +331,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to create request"), zap.String("message", "failed to create request"),
zap.Error(err), zap.Error(err),
) )
return "", err return whois, err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
@@ -356,7 +358,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to perform request"), zap.String("message", "failed to perform request"),
zap.Error(err), zap.Error(err),
) )
return "", err return whois, err
} }
if res == nil { if res == nil {
if w.debug { if w.debug {
@@ -365,7 +367,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.L().Error("reverse_whois", zap.L().Error("reverse_whois",
zap.String("message", "response was nil"), zap.String("message", "response was nil"),
) )
return "", errors.New("response was nil") return whois, errors.New("response was nil")
} }
b, err := io.ReadAll(res.Body) b, err := io.ReadAll(res.Body)
@@ -378,7 +380,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to read response body"), zap.String("message", "failed to read response body"),
zap.Error(err), zap.Error(err),
) )
return "", err return whois, err
} }
// Check for HTTP status code errors // Check for HTTP status code errors
@@ -396,7 +398,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("error", dhErr.Error()), zap.String("error", dhErr.Error()),
zap.String("body_error", string(b)), zap.String("body_error", string(b)),
) )
return "", &dhErr return whois, &dhErr
} }
if w.debug { if w.debug {
@@ -404,7 +406,30 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
debug.PrintJson(fmt.Sprintf("Body: %s\n", string(b[:]))) debug.PrintJson(fmt.Sprintf("Body: %s\n", string(b[:])))
} }
return string(b), nil var whoisResponse sqlite.ReverseWhoisResponse
err = json.Unmarshal(b, &whoisResponse)
if err != nil {
if w.debug {
debug.PrintInfo("failed to unmarshal response body")
debug.PrintError(err)
}
zap.L().Error("reverse_whois",
zap.String("message", "failed to unmarshal response body"),
zap.Error(err),
)
return whois, err
}
if w.debug {
debug.PrintInfo("unmarshalled response body")
debug.PrintJson(fmt.Sprintf("Remaining Credits: %d\n", whoisResponse.RemainingCredits))
debug.PrintJson(fmt.Sprintf("Data: %v\n", whoisResponse.Data))
}
w.balance = whoisResponse.RemainingCredits
whois = whoisResponse.Data
return whois, nil
} }
func (w *DehashedWhoIs) WhoisIP(ipAddress string) ([]sqlite.LookupResult, error) { func (w *DehashedWhoIs) WhoisIP(ipAddress string) ([]sqlite.LookupResult, error) {