What is Shodan?
Shodan is a search engine that helps you discover internet-exposed assets (servers, services, devices) by indexing banners and response data. For ethical hackers, it’s very useful for reconnaissance, asset discovery, and security research — especially when you use it responsibly.
Why Shodan is useful for ethical hacking
Shodan can help you:
- Identify publicly reachable services and applications
- Narrow results using filters (country, port, org, etc.)
- Support recon workflows for bug bounty and defensive research
The key is to use it for authorized testing or responsible research, not for abuse.
Example: Finding Next.js sites with a Shodan query
Recently, the React2Shell topic has been trending in the cybersecurity community. When vulnerabilities affect specific versions of React/Next.js, one common recon approach is to identify apps that look like they’re running those frameworks, then verify versions responsibly.
Here’s an example Shodan search query that can surface websites using Next.js:
http.html:"/_next/static/"

When you search this, Shodan may return servers and websites where the HTML contains the /_next/static/ path, which is commonly associated with Next.js.
Filtering results
After you get results, you can narrow them down using Shodan filters — for example, filtering by country. This helps you focus on a specific region during recon or research.

Responsible version checking (manual workflow)
A safe, ethical way to proceed is:
- Use Shodan to find potential Next.js sites (example query above).
- Collect domains/hosts for allowed targets (your scope matters).
- Check the application version non-intrusively (basic fingerprinting, public files/headers where appropriate).
- If you find an affected version, do not exploit it.
- Report it to the organization using a responsible disclosure approach.
Important: Always test only with permission. This is for educational and defensive security purposes.
Automated Version Checking (Python Script)
In my workflow, I export domain names from Shodan and feed them into my Python script. The script checks each website and identifies the Next.js and React.js versions (when detectable). This helps me review targets faster without doing any exploitation.
#!/usr/bin/env python3
"""
findnreact.py — Bulk Next.js + React detector (best-effort version guessing)
What it does (high level):
- Fetches the target HTML (follows redirects)
- Detects Next.js via headers + __NEXT_DATA__ + /_next/static/ signals
- Detects React via common DOM hints + scanning first-party JS bundles for React.version patterns
- Tries to extract:
- Next.js version (often NOT publicly exposed)
- React version (sometimes exposed inside bundles)
Usage:
python findnreact.py -t targets.txt
python findnreact.py -t targets.txt -c 30 --json-out results.json
python findnreact.py -t targets.txt --csv-out results.csv --timeout 15
Notes:
- This is best-effort. Many production apps do not expose exact framework versions.
- Only scan systems you own or have explicit permission to test.
Deps:
pip install -U requests beautifulsoup4
Optional (nicer progress bar):
pip install -U tqdm
Optional (slower, more “exact” when exposed; not recommended for huge lists):
pip install -U playwright
playwright install chromium
"""
from __future__ import annotations
import argparse
import csv
import json
import random
import re
import sys
import threading
import time
from dataclasses import dataclass, asdict
from typing import Optional, Dict, List, Tuple
from urllib.parse import urlparse, urljoin
import requests
from bs4 import BeautifulSoup
# ----------------------------
# Regexes / heuristics
# ----------------------------
SEMVER_RE = re.compile(r"\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b")
NEXT_VERSION_HINTS = [
re.compile(r'__NEXT_VERSION["\']?\s*[:=]\s*["\']([^"\']+)["\']', re.I),
re.compile(r'next[^a-z0-9]{0,10}version["\']?\s*[:=]\s*["\']([^"\']+)["\']', re.I),
re.compile(r'Next\.js[^0-9]{0,20}(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)', re.I),
]
REACT_VERSION_HINTS = [
# Common in React builds: exports.version="18.2.0"
re.compile(r'(?:exports|module\.exports)\.version\s*=\s*["\']([^"\']+)["\']', re.I),
# Sometimes: React.version="18.2.0"
re.compile(r'\bReact\.version\s*=\s*["\']([^"\']+)["\']', re.I),
# Sometimes: version:"18.2.0" nearby "react"
re.compile(r'version\s*:\s*["\'](\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)["\']', re.I),
]
NEXT_HTML_HINTS = [
"__NEXT_DATA__",
"/_next/static/",
"next-head-count",
]
REACT_HTML_HINTS = [
"data-reactroot", # legacy
"data-reactid", # legacy
]
USER_AGENTS = [
# A small rotating set (avoid “bypass” vibes; just reduce trivial blocks).
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
]
# ----------------------------
# Data model
# ----------------------------
@dataclass
class ScanResult:
target: str
final_url: str
status_code: Optional[int]
is_nextjs: bool
next_version: Optional[str]
next_method: Optional[str]
is_react: bool
react_version: Optional[str]
react_method: Optional[str]
error: Optional[str] = None
# ----------------------------
# Helpers
# ----------------------------
def normalize_url(raw: str) -> str:
raw = raw.strip()
if not raw:
raise ValueError("Empty URL")
if raw.startswith("#"):
raise ValueError("Comment line")
if "://" not in raw:
raw = "https://" + raw
p = urlparse(raw)
if not p.netloc:
raise ValueError(f"Invalid URL: {raw}")
return p._replace(fragment="").geturl()
def build_headers() -> Dict[str, str]:
return {
"User-Agent": random.choice(USER_AGENTS),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.8",
"Connection": "keep-alive",
}
.... click here for full code
when i runthis script i got this Error
❯ python script.py -t targets.txt
websites | reactjs | nextjs
------------------------ | --------------------- | ---------------------
example.com | -- | --
------------------------------------------------------------------------
https://vercel.com/ | 19.3.0-canary-65ee... | 16.1.1-canary.12
------------------------------------------------------------------------
e.amazonaws.com | -- | --
------------------------------------------------------------------------
www2.pekao-fs.com.pl | -- | --
------------------------------------------------------------------------
lpdn-switch.www.hpe.com | -- | --
------------------------------------------------------------------------
murmuras.com | 19.2.0-canary-0bdb... | 15.5.7
------------------------------------------------------------------------
suite.majors.finance | 3.38.1 | --
------------------------------------------------------------------------
gosac.com.br | -- | --
------------------------------------------------------------------------
www.pekao-fs.com.pl | 3.6.5 | --
------------------------------------------------------------------------
pekao-fs.com.pl | 3.6.5 | --
------------------------------------------------------------------------
education.hpe.com | 18.3.0-canary-60a9... | 14.1.0
------------------------------------------------------------------------
https://pwntrends.com/ | -- | --
------------------------------------------------------------------------
h10076.www1.hpe.com | 18.3.0-canary-60a9... | 14.1.0
------------------------------------------------------------------------
P1wg506470.dc01.its.h... | -- | --
------------------------------------------------------------------------
P1wg506449.dc01.its.h... | -- | --
------------------------------------------------------------------------
this is my output, i got this error and i got this domain name from shodan, then i use my python code to find nextjs and react versions.

Final note
Shodan “dorking” can be extremely helpful for recon in ethical hacking and bug bounty hunting — but it must be used responsibly. If you discover a potentially vulnerable system, the right step is responsible reporting, not attacking.
Thanks for reading!
