Press enter or click to view image in full size
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.
Join Medium for free to get updates from this writer.
This guide walks you through the entire process on Kali Linux, from calculating the favicon hash to extracting live, validated subdomains.
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 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.
Press enter or click to view image in full size
# 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/pip3 install shodanshodan --helpYou need a Shodan API key. Get yours at: https://account.shodan.io
shodan init YOUR_SHODAN_API_KEYReplace YOUR_SHODAN_API_KEY with your actual key.
shodan infoExpected output:
Query credits available: 100
Scan credits available: 0Most sites host their favicon at /favicon.ico. Download it with:
curl -s https://example.com/favicon.ico -o favicon.icoIf the standard location doesn’t work, inspect the HTML source:
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.icoCreate 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])
Make it executable:
chmod +x favicon_hash.pyInstall the required library:
pip3 install mmh3 requestspython3 favicon_hash.py https://example.com/favicon.icoExample Output:
[+] URL: https://example.com/favicon.ico
[+] Favicon Hash: 123456789
[+] Use in Shodan: http.favicon.hash:123456789If 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)}')"Now the real magic begins. Use the hash to find every server Shodan has indexed that shares the same favicon.
shodan search --fields ip_str,port,hostnames "http.favicon.hash:123456789" > shodan_results.txtRefine 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
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.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.comawk '{print $3}' shodan_results.txt | sort -u > subdomains.txtcat subdomains.txtExpected Output:
www.example.com
sub1.example.com
sub2.example.com
staging.example.com
admin.internal.example.comFor later reference, keep the IP-to-hostname mapping:
awk '{print $1 "\t" $3}' shodan_results.txt | sort -u > ip_hostname_map.txtNot all extracted hostnames will resolve. We need to validate them.
cat subdomains.txt | dnsx -silent -o resolved_subdomains.txtcat resolved_subdomains.txt | httpx -silent -o live_subdomains.txtbash
cat subdomains.txt | dnsx -silent | httpx -silent -o live_subdomains.txtbash
cat live_subdomains.txtExpected Output:
https://www.example.com
https://sub1.example.com
https://sub2.example.com
https://staging.example.comcat 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]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"
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
Use WhatWeb or Wappalyzer to verify if discovered subdomains share the same tech stack:
whatweb -l subdomains_raw.txtOnce 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
Press enter or click to view image in full size
# Quick search for all WordPress sites (hash: 116323821)
shodan search --fields ip_str,port,hostnames "http.favicon.hash:116323821"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
# 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
# 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 ""
doneSome favicons are served via redirect. Follow them:
curl -sL https://example.com/favicon.ico -o favicon.icoThe favicon hash technique works because of three key factors:
http.favicon.hash as a searchable field for every HTTP response that contains a favicon.# 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"
# 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
pip3 install mmh3
# If that fails, try:
pip3 install mmh3cffi# Find where pip installed it
python3 -m shodan --help# Add to PATH
export PATH=$PATH:~/.local/bin
echo 'export PATH=$PATH:~/.local/bin' >> ~/.bashrc
# 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/
IMPORTANT: This technique discovers servers and subdomains that may include staging, internal, or development environments. Only test targets you are explicitly authorized to assess.
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.
Press enter or click to view image in full size
# 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 -titleOnce you have your live subdomains, consider:
naabu or nmapffuf or gobusterwappalyzer or whatwebsubjs or jsluicegau or katanaSave as setup_favicon_recon.sh:
#!/bin/bashecho "[+] 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>\""