May 13, 2026
How to Find Subdomains Using Shodan and the Favicon Hash Trick
Subdomain enumeration is the foundation of any serious bug bounty reconnaissance. While tools like Sublist3r, Amass, and crt.sh are…
MD Mehedi Hasan
8 min read
Subdomain enumeration is the foundation of any serious bug bounty reconnaissance. While tools like Sublist3r, Amass, and crt.sh are excellent, they rely on DNS records and certificate transparency logs — which means they only find what's publicly indexed.
There's a smarter way.
Many organizations reuse the same favicon across their entire infrastructure — main site, subdomains, staging servers, CDN nodes, and even internal tools. This means the favicon acts as a unique fingerprint. If you can hash it and search Shodan's indexed data, you can discover subdomains and servers that no DNS brute-force tool will ever find.
This guide walks you through the entire process on Kali Linux, from calculating the favicon hash to extracting live, validated subdomains.
How It Works
Target Domain (example.com)
│
▼
Download Favicon → Compute MurmurHash3 → Search Shodan
│
▼
Shodan returns all IPs/hostnames sharing that favicon hash
│
▼
Extract hostnames → DNS resolution → HTTP validation
│
▼
Live Subdomains DiscoveredTarget Domain (example.com)
│
▼
Download Favicon → Compute MurmurHash3 → Search Shodan
│
▼
Shodan returns all IPs/hostnames sharing that favicon hash
│
▼
Extract hostnames → DNS resolution → HTTP validation
│
▼
Live Subdomains DiscoveredThe key insight: Shodan indexes favicon hashes for every website it scans. The search filter http.favicon.hash:<hash> lets you query all servers worldwide that share the exact same favicon as your target.
Prerequisites
Install Required Tools
# Install dnsx and httpx (ProjectDiscovery tools)
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
sudo cp ~/go/bin/dnsx /usr/bin/
sudo cp ~/go/bin/httpx /usr/bin/# Install dnsx and httpx (ProjectDiscovery tools)
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
sudo cp ~/go/bin/dnsx /usr/bin/
sudo cp ~/go/bin/httpx /usr/bin/Step 1: Install Shodan CLI
Install via pip
pip3 install shodanpip3 install shodanVerify Installation
shodan --helpshodan --helpInitialize with Your API Key
You need a Shodan API key. Get yours at: https://account.shodan.io
shodan init YOUR_SHODAN_API_KEYshodan init YOUR_SHODAN_API_KEYReplace YOUR_SHODAN_API_KEY with your actual key.
Test Your Connection
shodan infoshodan infoExpected output:
Query credits available: 100
Scan credits available: 0Query credits available: 100
Scan credits available: 0Step 2: Extract the Favicon & Calculate Its Hash
Method A: Favicon at Standard Location
Most sites host their favicon at /favicon.ico. Download it with:
curl -s https://example.com/favicon.ico -o favicon.icocurl -s https://example.com/favicon.ico -o favicon.icoMethod B: Favicon at Custom Location
If the standard location doesn't work, inspect the HTML source:
curl -s https://example.com | grep -i "favicon\|icon" | grep -oP 'href="\K[^"]+'curl -s https://example.com | grep -i "favicon\|icon" | grep -oP 'href="\K[^"]+'Then download from the discovered path:
curl -s https://example.com/path/to/favicon.ico -o favicon.icocurl -s https://example.com/path/to/favicon.ico -o favicon.icoCalculate the Favicon Hash
Create a Python script called favicon_hash.py:
#!/usr/bin/env python3
"""
Favicon Hash Calculator for Shodan Reconnaissance
Usage: python3 favicon_hash.py <favicon_url>
"""
import mmh3
import requests
import sys
import codecs
def calculate_favicon_hash(url):
"""Download favicon and calculate MurmurHash3 hash."""
try:
response = requests.get(url, timeout=10, verify=False)
response.raise_for_status()
favicon = response.content
hash_value = mmh3.hash(favicon)
print(f"[+] URL: {url}")
print(f"[+] Favicon Hash: {hash_value}")
print(f"[+] Use in Shodan: http.favicon.hash:{hash_value}")
return hash_value
except requests.exceptions.RequestException as e:
print(f"[-] Error downloading favicon: {e}")
sys.exit(1)
except Exception as e:
print(f"[-] Error calculating hash: {e}")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 favicon_hash.py <favicon_url>")
print("Example: python3 favicon_hash.py https://example.com/favicon.ico")
sys.exit(1)
# Suppress SSL warnings for self-signed certs
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
calculate_favicon_hash(sys.argv[1])#!/usr/bin/env python3
"""
Favicon Hash Calculator for Shodan Reconnaissance
Usage: python3 favicon_hash.py <favicon_url>
"""
import mmh3
import requests
import sys
import codecs
def calculate_favicon_hash(url):
"""Download favicon and calculate MurmurHash3 hash."""
try:
response = requests.get(url, timeout=10, verify=False)
response.raise_for_status()
favicon = response.content
hash_value = mmh3.hash(favicon)
print(f"[+] URL: {url}")
print(f"[+] Favicon Hash: {hash_value}")
print(f"[+] Use in Shodan: http.favicon.hash:{hash_value}")
return hash_value
except requests.exceptions.RequestException as e:
print(f"[-] Error downloading favicon: {e}")
sys.exit(1)
except Exception as e:
print(f"[-] Error calculating hash: {e}")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 favicon_hash.py <favicon_url>")
print("Example: python3 favicon_hash.py https://example.com/favicon.ico")
sys.exit(1)
# Suppress SSL warnings for self-signed certs
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
calculate_favicon_hash(sys.argv[1])Make it executable:
chmod +x favicon_hash.pychmod +x favicon_hash.pyInstall the required library:
pip3 install mmh3 requestspip3 install mmh3 requestsRun the Script
python3 favicon_hash.py https://example.com/favicon.icopython3 favicon_hash.py https://example.com/favicon.icoExample Output:
[+] URL: https://example.com/favicon.ico
[+] Favicon Hash: 123456789
[+] Use in Shodan: http.favicon.hash:123456789[+] URL: https://example.com/favicon.ico
[+] Favicon Hash: 123456789
[+] Use in Shodan: http.favicon.hash:123456789One-Liner Alternative (No Script File)
If you prefer not to create a file, use this one-liner:
python3 -c "import mmh3, requests; print(f'Favicon Hash: {mmh3.hash(requests.get(\"https://example.com/favicon.ico\").content)}')"python3 -c "import mmh3, requests; print(f'Favicon Hash: {mmh3.hash(requests.get(\"https://example.com/favicon.ico\").content)}')"Step 3: Search Shodan for Matching Favicon Hash
Now the real magic begins. Use the hash to find every server Shodan has indexed that shares the same favicon.
Basic Search
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789" > shodan_results.txtshodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789" > shodan_results.txtWith Additional Filters
Refine your search to focus on specific ports or countries:
# HTTPS only
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 port:443" > shodan_https.txt
# Specific country (e.g., United States)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 country:US" > shodan_us.txt
# Specific organization
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 org:\"Target Inc\"" > shodan_org.txt# HTTPS only
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 port:443" > shodan_https.txt
# Specific country (e.g., United States)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 country:US" > shodan_us.txt
# Specific organization
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789 org:\"Target Inc\"" > shodan_org.txtDownload Large Result Sets
For targets with many results, use Shodan's download feature:
shodan download --limit 5000 search_results "http.favicon.hash:123456789"
shodan parse --fields ip_str,port,hostnames search_results.json.gz > shodan_results.txtshodan download --limit 5000 search_results "http.favicon.hash:123456789"
shodan parse --fields ip_str,port,hostnames search_results.json.gz > shodan_results.txtStep 4: Parse and Extract Subdomains
View Raw Results
cat shodan_results.txtcat shodan_results.txtExample Output:
93.184.216.34 80 www.example.com
93.184.216.35 443 sub1.example.com
93.184.216.36 80 sub2.example.com
192.168.1.10 8080 staging.example.com
10.0.0.5 443 admin.internal.example.com93.184.216.34 80 www.example.com
93.184.216.35 443 sub1.example.com
93.184.216.36 80 sub2.example.com
192.168.1.10 8080 staging.example.com
10.0.0.5 443 admin.internal.example.comExtract Hostnames Only
awk '{print $3}' shodan_results.txt | sort -u > subdomains.txtawk '{print $3}' shodan_results.txt | sort -u > subdomains.txtView Extracted Subdomains
cat subdomains.txtcat subdomains.txtExpected Output:
www.example.com
sub1.example.com
sub2.example.com
staging.example.com
admin.internal.example.comwww.example.com
sub1.example.com
sub2.example.com
staging.example.com
admin.internal.example.comAlternative: Extract Hostnames with IPs
For later reference, keep the IP-to-hostname mapping:
awk '{print $1 "\t" $3}' shodan_results.txt | sort -u > ip_hostname_map.txtawk '{print $1 "\t" $3}' shodan_results.txt | sort -u > ip_hostname_map.txtStep 5: Validate and Filter Live Subdomains
Not all extracted hostnames will resolve. We need to validate them.
DNS Resolution with dnsx
cat subdomains.txt | dnsx -silent -o resolved_subdomains.txtcat subdomains.txt | dnsx -silent -o resolved_subdomains.txtCheck for Live HTTP/HTTPS with httpx
cat resolved_subdomains.txt | httpx -silent -o live_subdomains.txtcat resolved_subdomains.txt | httpx -silent -o live_subdomains.txtFull Validation Pipeline (One Command)
bash
cat subdomains.txt | dnsx -silent | httpx -silent -o live_subdomains.txtcat subdomains.txt | dnsx -silent | httpx -silent -o live_subdomains.txtView Live Subdomains
bash
cat live_subdomains.txtcat live_subdomains.txtExpected Output:
https://www.example.com
https://sub1.example.com
https://sub2.example.com
https://staging.example.comhttps://www.example.com
https://sub1.example.com
https://sub2.example.com
https://staging.example.comEnrich with Status Codes and Titles
cat resolved_subdomains.txt | httpx -silent -status-code -title -o enriched.txt
cat enriched.txtcat resolved_subdomains.txt | httpx -silent -status-code -title -o enriched.txt
cat enriched.txtExample Output:
https://www.example.com [200] [Example Domain]
https://sub1.example.com [200] [Dashboard - Login]
https://staging.example.com [302] [Redirecting...]
https://admin.internal.example.com [403] [Forbidden]https://www.example.com [200] [Example Domain]
https://sub1.example.com [200] [Dashboard - Login]
https://staging.example.com [302] [Redirecting...]
https://admin.internal.example.com [403] [Forbidden]Complete One-Click Automation Script
Create favicon_subdomain_scanner.sh:
#!/bin/bash
# Favicon-Based Subdomain Discovery Tool
# Author: SecurityTalent
# Usage: ./favicon_subdomain_scanner.sh <domain>
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
if [ $# -lt 1 ]; then
echo -e "${RED}Usage: $0 <domain> [favicon_url]${NC}"
echo -e "${YELLOW}Example: $0 example.com${NC}"
echo -e "${YELLOW}Example: $0 example.com https://example.com/custom/favicon.ico${NC}"
exit 1
fi
DOMAIN=$1
FAVICON_URL=${2:-"https://$DOMAIN/favicon.ico"}
OUTPUT_DIR="favicon_recon_$DOMAIN"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo -e "${GREEN}[+] Target Domain: $DOMAIN${NC}"
echo -e "${GREEN}[+] Favicon URL: $FAVICON_URL${NC}"
echo -e "${GREEN}[+] Output Directory: $OUTPUT_DIR${NC}"
echo ""
mkdir -p "$OUTPUT_DIR"
# Step 1: Download favicon and calculate hash
echo -e "${YELLOW}[*] Step 1: Downloading favicon and calculating hash...${NC}"
curl -s "$FAVICON_URL" -o "$OUTPUT_DIR/favicon.ico"
if [ ! -f "$OUTPUT_DIR/favicon.ico" ] || [ ! -s "$OUTPUT_DIR/favicon.ico" ]; then
echo -e "${RED}[-] Failed to download favicon. Trying alternative discovery...${NC}"
# Try to find favicon from HTML
FAV_ALT=$(curl -s "https://$DOMAIN" | grep -oP 'href="\K[^"]*favicon[^"]*' | head -1)
if [ -n "$FAV_ALT" ]; then
if [[ "$FAV_ALT" == http* ]]; then
FAVICON_URL="$FAV_ALT"
else
FAVICON_URL="https://$DOMAIN$FAV_ALT"
fi
echo -e "${GREEN}[+] Discovered alternative favicon URL: $FAVICON_URL${NC}"
curl -s "$FAVICON_URL" -o "$OUTPUT_DIR/favicon.ico"
else
echo -e "${RED}[-] Could not find favicon. Exiting.${NC}"
exit 1
fi
fi
HASH=$(python3 -c "import mmh3; print(mmh3.hash(open('$OUTPUT_DIR/favicon.ico','rb').read()))")
echo -e "${GREEN}[+] Favicon Hash: $HASH${NC}"
echo "$HASH" > "$OUTPUT_DIR/favicon_hash.txt"
# Step 2: Search Shodan
echo -e "${YELLOW}[*] Step 2: Searching Shodan for matching favicon hash...${NC}"
shodan search --fields ip_str,port,hostnames "http.favicon.hash:$HASH" > "$OUTPUT_DIR/shodan_raw.txt"
echo -e "${GREEN}[+] Shodan results saved to $OUTPUT_DIR/shodan_raw.txt${NC}"
# Step 3: Extract hostnames
echo -e "${YELLOW}[*] Step 3: Extracting hostnames...${NC}"
awk '{print $3}' "$OUTPUT_DIR/shodan_raw.txt" | grep -v "^$" | sort -u > "$OUTPUT_DIR/subdomains_raw.txt"
echo -e "${GREEN}[+] $(wc -l < "$OUTPUT_DIR/subdomains_raw.txt") unique hostnames found${NC}"
# Step 4: DNS Resolution
echo -e "${YELLOW}[*] Step 4: Resolving DNS...${NC}"
cat "$OUTPUT_DIR/subdomains_raw.txt" | dnsx -silent -o "$OUTPUT_DIR/resolved.txt" 2>/dev/null || \
cat "$OUTPUT_DIR/subdomains_raw.txt" > "$OUTPUT_DIR/resolved.txt"
echo -e "${GREEN}[+] $(wc -l < "$OUTPUT_DIR/resolved.txt") resolved hostnames${NC}"
# Step 5: HTTP Validation
echo -e "${YELLOW}[*] Step 5: Checking live HTTP hosts...${NC}"
cat "$OUTPUT_DIR/resolved.txt" | httpx -silent -status-code -title -o "$OUTPUT_DIR/live_enriched.txt" 2>/dev/null || \
cat "$OUTPUT_DIR/resolved.txt" > "$OUTPUT_DIR/live_enriched.txt"
# Extract just URLs
awk '{print $1}' "$OUTPUT_DIR/live_enriched.txt" > "$OUTPUT_DIR/live_subdomains.txt"
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} RECONNAISSANCE COMPLETE${NC}"
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}[+] Target: $DOMAIN${NC}"
echo -e "${GREEN}[+] Favicon Hash: $HASH${NC}"
echo -e "${GREEN}[+] Total Subdomains Found: $(wc -l < "$OUTPUT_DIR/subdomains_raw.txt")${NC}"
echo -e "${GREEN}[+] Live Subdomains: $(wc -l < "$OUTPUT_DIR/live_subdomains.txt")${NC}"
echo ""
echo -e "${YELLOW}Results saved to: $OUTPUT_DIR/${NC}"
echo -e "${YELLOW} - favicon_hash.txt${NC}"
echo -e "${YELLOW} - shodan_raw.txt${NC}"
echo -e "${YELLOW} - subdomains_raw.txt${NC}"
echo -e "${YELLOW} - resolved.txt${NC}"
echo -e "${YELLOW} - live_subdomains.txt${NC}"
echo -e "${YELLOW} - live_enriched.txt${NC}"
echo ""
echo -e "${GREEN}Live Subdomains:${NC}"
cat "$OUTPUT_DIR/live_subdomains.txt"#!/bin/bash
# Favicon-Based Subdomain Discovery Tool
# Author: SecurityTalent
# Usage: ./favicon_subdomain_scanner.sh <domain>
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
if [ $# -lt 1 ]; then
echo -e "${RED}Usage: $0 <domain> [favicon_url]${NC}"
echo -e "${YELLOW}Example: $0 example.com${NC}"
echo -e "${YELLOW}Example: $0 example.com https://example.com/custom/favicon.ico${NC}"
exit 1
fi
DOMAIN=$1
FAVICON_URL=${2:-"https://$DOMAIN/favicon.ico"}
OUTPUT_DIR="favicon_recon_$DOMAIN"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo -e "${GREEN}[+] Target Domain: $DOMAIN${NC}"
echo -e "${GREEN}[+] Favicon URL: $FAVICON_URL${NC}"
echo -e "${GREEN}[+] Output Directory: $OUTPUT_DIR${NC}"
echo ""
mkdir -p "$OUTPUT_DIR"
# Step 1: Download favicon and calculate hash
echo -e "${YELLOW}[*] Step 1: Downloading favicon and calculating hash...${NC}"
curl -s "$FAVICON_URL" -o "$OUTPUT_DIR/favicon.ico"
if [ ! -f "$OUTPUT_DIR/favicon.ico" ] || [ ! -s "$OUTPUT_DIR/favicon.ico" ]; then
echo -e "${RED}[-] Failed to download favicon. Trying alternative discovery...${NC}"
# Try to find favicon from HTML
FAV_ALT=$(curl -s "https://$DOMAIN" | grep -oP 'href="\K[^"]*favicon[^"]*' | head -1)
if [ -n "$FAV_ALT" ]; then
if [[ "$FAV_ALT" == http* ]]; then
FAVICON_URL="$FAV_ALT"
else
FAVICON_URL="https://$DOMAIN$FAV_ALT"
fi
echo -e "${GREEN}[+] Discovered alternative favicon URL: $FAVICON_URL${NC}"
curl -s "$FAVICON_URL" -o "$OUTPUT_DIR/favicon.ico"
else
echo -e "${RED}[-] Could not find favicon. Exiting.${NC}"
exit 1
fi
fi
HASH=$(python3 -c "import mmh3; print(mmh3.hash(open('$OUTPUT_DIR/favicon.ico','rb').read()))")
echo -e "${GREEN}[+] Favicon Hash: $HASH${NC}"
echo "$HASH" > "$OUTPUT_DIR/favicon_hash.txt"
# Step 2: Search Shodan
echo -e "${YELLOW}[*] Step 2: Searching Shodan for matching favicon hash...${NC}"
shodan search --fields ip_str,port,hostnames "http.favicon.hash:$HASH" > "$OUTPUT_DIR/shodan_raw.txt"
echo -e "${GREEN}[+] Shodan results saved to $OUTPUT_DIR/shodan_raw.txt${NC}"
# Step 3: Extract hostnames
echo -e "${YELLOW}[*] Step 3: Extracting hostnames...${NC}"
awk '{print $3}' "$OUTPUT_DIR/shodan_raw.txt" | grep -v "^$" | sort -u > "$OUTPUT_DIR/subdomains_raw.txt"
echo -e "${GREEN}[+] $(wc -l < "$OUTPUT_DIR/subdomains_raw.txt") unique hostnames found${NC}"
# Step 4: DNS Resolution
echo -e "${YELLOW}[*] Step 4: Resolving DNS...${NC}"
cat "$OUTPUT_DIR/subdomains_raw.txt" | dnsx -silent -o "$OUTPUT_DIR/resolved.txt" 2>/dev/null || \
cat "$OUTPUT_DIR/subdomains_raw.txt" > "$OUTPUT_DIR/resolved.txt"
echo -e "${GREEN}[+] $(wc -l < "$OUTPUT_DIR/resolved.txt") resolved hostnames${NC}"
# Step 5: HTTP Validation
echo -e "${YELLOW}[*] Step 5: Checking live HTTP hosts...${NC}"
cat "$OUTPUT_DIR/resolved.txt" | httpx -silent -status-code -title -o "$OUTPUT_DIR/live_enriched.txt" 2>/dev/null || \
cat "$OUTPUT_DIR/resolved.txt" > "$OUTPUT_DIR/live_enriched.txt"
# Extract just URLs
awk '{print $1}' "$OUTPUT_DIR/live_enriched.txt" > "$OUTPUT_DIR/live_subdomains.txt"
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} RECONNAISSANCE COMPLETE${NC}"
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}[+] Target: $DOMAIN${NC}"
echo -e "${GREEN}[+] Favicon Hash: $HASH${NC}"
echo -e "${GREEN}[+] Total Subdomains Found: $(wc -l < "$OUTPUT_DIR/subdomains_raw.txt")${NC}"
echo -e "${GREEN}[+] Live Subdomains: $(wc -l < "$OUTPUT_DIR/live_subdomains.txt")${NC}"
echo ""
echo -e "${YELLOW}Results saved to: $OUTPUT_DIR/${NC}"
echo -e "${YELLOW} - favicon_hash.txt${NC}"
echo -e "${YELLOW} - shodan_raw.txt${NC}"
echo -e "${YELLOW} - subdomains_raw.txt${NC}"
echo -e "${YELLOW} - resolved.txt${NC}"
echo -e "${YELLOW} - live_subdomains.txt${NC}"
echo -e "${YELLOW} - live_enriched.txt${NC}"
echo ""
echo -e "${GREEN}Live Subdomains:${NC}"
cat "$OUTPUT_DIR/live_subdomains.txt"Make it executable and run:
chmod +x favicon_subdomain_scanner.sh
# Basic usage
./favicon_subdomain_scanner.sh example.com
# With custom favicon URL
./favicon_subdomain_scanner.sh example.com https://example.com/assets/custom-icon.ico
chmod +x favicon_subdomain_scanner.sh
# Basic usage
./favicon_subdomain_scanner.sh example.com
# With custom favicon URL
./favicon_subdomain_scanner.sh example.com https://example.com/assets/custom-icon.ico
Tips for Bug Bounty Hunters
1. Cross-Check Technologies
Use WhatWeb or Wappalyzer to verify if discovered subdomains share the same tech stack:
whatweb -l subdomains_raw.txtwhatweb -l subdomains_raw.txt2. Expand the Attack Surface
Once you have live subdomains, test for:
# Directory fuzzing
ffuf -u https://subdomain.com/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
# Open ports
nmap -sC -sV -p- subdomain.com
# .git exposure
gau subdomain.com | grep "\.git"
# CORS misconfigurations
corsy -u https://subdomain.com# Directory fuzzing
ffuf -u https://subdomain.com/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
# Open ports
nmap -sC -sV -p- subdomain.com
# .git exposure
gau subdomain.com | grep "\.git"
# CORS misconfigurations
corsy -u https://subdomain.com3. Known Favicon Hashes for Quick Wins
# Quick search for all WordPress sites (hash: 116323821)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:116323821"# Quick search for all WordPress sites (hash: 116323821)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:116323821"4. Automate with Shodan API
For large-scale searches with full result sets:
# Download up to 10,000 results
shodan download --limit 10000 search_results "http.favicon.hash:123456789"
# Parse the compressed results
shodan parse --fields ip_str,port,hostnames search_results.json.gz > full_results.txt# Download up to 10,000 results
shodan download --limit 10000 search_results "http.favicon.hash:123456789"
# Parse the compressed results
shodan parse --fields ip_str,port,hostnames search_results.json.gz > full_results.txt5. Combine with Other Tools
# Merge with traditional subdomain enumeration
subfinder -d example.com -o subfinder_domains.txt
cat subfinder_domains.txt subdomains_raw.txt | sort -u > all_subdomains.txt
# Check with crt.sh
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u >> all_subdomains.txt
# Validate all combined
cat all_subdomains.txt | httpx -silent -o final_live.txt# Merge with traditional subdomain enumeration
subfinder -d example.com -o subfinder_domains.txt
cat subfinder_domains.txt subdomains_raw.txt | sort -u > all_subdomains.txt
# Check with crt.sh
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u >> all_subdomains.txt
# Validate all combined
cat all_subdomains.txt | httpx -silent -o final_live.txt6. Common Favicon Locations to Check
# Standard locations many targets use
for path in /favicon.ico /favicon.png /assets/favicon.ico /static/favicon.ico /images/favicon.ico /img/favicon.ico; do
echo "Checking: https://example.com$path"
curl -s -o /dev/null -w "%{http_code}" "https://example.com$path"
echo ""
done# Standard locations many targets use
for path in /favicon.ico /favicon.png /assets/favicon.ico /static/favicon.ico /images/favicon.ico /img/favicon.ico; do
echo "Checking: https://example.com$path"
curl -s -o /dev/null -w "%{http_code}" "https://example.com$path"
echo ""
done7. Handle Redirects
Some favicons are served via redirect. Follow them:
curl -sL https://example.com/favicon.ico -o favicon.icocurl -sL https://example.com/favicon.ico -o favicon.icoWhy This Works
Technical Explanation
The favicon hash technique works because of three key factors:
- Favicon Reuse — Organizations consistently reuse their favicon across all subdomains, staging environments, CDN endpoints, and even internal applications. It's a branding artifact that gets copied everywhere.
- Shodan Indexing — Shodan continuously scans the internet and indexes
http.favicon.hashas a searchable field for every HTTP response that contains a favicon. - MurmurHash3 Consistency — The hash algorithm produces the same output for the same binary input. Any server serving the exact same favicon file will produce the identical hash, regardless of the domain name or IP address.
What You Can Discover That Other Tools Miss
Troubleshooting
Issue 1: Shodan Returns No Results
# Check if you have credits
shodan info
# Test with a known hash (Google's favicon)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:-305179312"# Check if you have credits
shodan info
# Test with a known hash (Google's favicon)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:-305179312"Issue 2: Favicon Download Fails
# Test with verbose curl
curl -v https://example.com/favicon.ico
# Try without SSL verification
curl -sk https://example.com/favicon.ico -o favicon.ico# Test with verbose curl
curl -v https://example.com/favicon.ico
# Try without SSL verification
curl -sk https://example.com/favicon.ico -o favicon.icoIssue 3: "mmh3" Module Not Found
pip3 install mmh3
# If that fails, try:
pip3 install mmh3cffipip3 install mmh3
# If that fails, try:
pip3 install mmh3cffiIssue 4: "shodan: command not found"
# Find where pip installed it
python3 -m shodan --help
# Add to PATH
export PATH=$PATH:~/.local/bin
echo 'export PATH=$PATH:~/.local/bin' >> ~/.bashrc# Find where pip installed it
python3 -m shodan --help
# Add to PATH
export PATH=$PATH:~/.local/bin
echo 'export PATH=$PATH:~/.local/bin' >> ~/.bashrcIssue 5: HTTPX/DNSX Not Found
# Install from Go
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
# Copy to PATH
sudo cp ~/go/bin/dnsx /usr/local/bin/
sudo cp ~/go/bin/httpx /usr/local/bin/# Install from Go
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
# Copy to PATH
sudo cp ~/go/bin/dnsx /usr/local/bin/
sudo cp ~/go/bin/httpx /usr/local/bin/Ethical & Legal Reminder
IMPORTANT: This technique discovers servers and subdomains that may include staging, internal, or development environments. Only test targets you are explicitly authorized to assess.
- Bug Bounty: Verify scope before testing any discovered subdomain
- Pentesting: Include all discovered assets in your Rules of Engagement
- Disclosure: Report discovered internal/development servers responsibly
Conclusion
The favicon hash trick is one of the most underutilized techniques in bug bounty reconnaissance. While everyone else is brute-forcing DNS records and scraping certificate logs, you can leverage Shodan's massive indexed dataset to find hidden assets based on a single shared favicon.
Key Takeaways
Quick Reference (One-Click)
# Complete workflow in 3 commands
HASH=$(python3 -c "import mmh3, requests; print(mmh3.hash(requests.get('https://example.com/favicon.ico').content))")
shodan search --fields ip_str,port,hostnames "http.favicon.hash:$HASH" | awk '{print $3}' | sort -u | dnsx -silent | httpx -silent -status-code -title# Complete workflow in 3 commands
HASH=$(python3 -c "import mmh3, requests; print(mmh3.hash(requests.get('https://example.com/favicon.ico').content))")
shodan search --fields ip_str,port,hostnames "http.favicon.hash:$HASH" | awk '{print $3}' | sort -u | dnsx -silent | httpx -silent -status-code -titleWhat's Next?
Once you have your live subdomains, consider:
- Port scanning with
naabuornmap - Directory fuzzing with
ffuforgobuster - Technology fingerprinting with
wappalyzerorwhatweb - JavaScript analysis with
subjsorjsluice - Endpoint discovery with
gauorkatana
Complete Setup Script
Save as setup_favicon_recon.sh:
#!/bin/bash
echo "[+] Installing required Python packages..."
pip3 install shodan mmh3 requests --quiet
echo "[+] Installing Go tools..."
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
sudo cp ~/go/bin/dnsx /usr/bin/
sudo cp ~/go/bin/httpx /usr/bin/
echo "[+] Creating favicon_hash.py..."
cat > favicon_hash.py << 'PYEOF'
#!/usr/bin/env python3
import mmh3, requests, sys, urllib3
urllib3.disable_warnings()
if len(sys.argv) != 2:
print("Usage: python3 favicon_hash.py <url>")
sys.exit(1)
r = requests.get(sys.argv[1], timeout=10, verify=False)
print(f"Favicon Hash: {mmh3.hash(r.content)}")
PYEOF
chmod +x favicon_hash.py
echo ""
echo "[+] Setup Complete!"
echo ""
echo "[+] Test with: python3 favicon_hash.py https://example.com/favicon.ico"
echo "[+] Then: shodan search --fields ip_str,port,hostnames \"http.favicon.hash:<HASH>\""#!/bin/bash
echo "[+] Installing required Python packages..."
pip3 install shodan mmh3 requests --quiet
echo "[+] Installing Go tools..."
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
sudo cp ~/go/bin/dnsx /usr/bin/
sudo cp ~/go/bin/httpx /usr/bin/
echo "[+] Creating favicon_hash.py..."
cat > favicon_hash.py << 'PYEOF'
#!/usr/bin/env python3
import mmh3, requests, sys, urllib3
urllib3.disable_warnings()
if len(sys.argv) != 2:
print("Usage: python3 favicon_hash.py <url>")
sys.exit(1)
r = requests.get(sys.argv[1], timeout=10, verify=False)
print(f"Favicon Hash: {mmh3.hash(r.content)}")
PYEOF
chmod +x favicon_hash.py
echo ""
echo "[+] Setup Complete!"
echo ""
echo "[+] Test with: python3 favicon_hash.py https://example.com/favicon.ico"
echo "[+] Then: shodan search --fields ip_str,port,hostnames \"http.favicon.hash:<HASH>\""Happy Bug Hunting!
Found this useful? Follow SecurityTalent for more advanced recon techniques, vulnerability research, and bug bounty strategies.