返回首页

用Python复刻Shodan:4000行代码自建网络空间搜索引擎

复刻:4000行代码自建网络空间搜索引擎

当你在Shodan上搜索" country:"时,背后发生了什么?端口扫描、Banner抓取、指纹匹配、地理定位、数据索引——这些核心能力,能否用纯Python在本地复现?本文带你用4000+行代码,从零构建一个功能完整的网络空间搜索引擎。

一、为什么自建?

Shodan是全球最大的网络空间搜索引擎,它持续扫描互联网上所有可达设备,索引开放端口、服务指纹、地理位置等信息。对于安全研究人员而言,它是资产发现、威胁情报的利器。但Shodan的免费有严格限制,且数据不在本地,无法定制。

自建一个"迷你Shodan"有三个核心价值:

数据自主:扫描结果存储在本地SQLite数据库中,可离线查询、二次分析,不受API配额限制。

定制灵活:可以针对特定目标深度扫描,添加自定义漏洞检测规则,集成到内部安全平台。

技术深度:通过亲手实现端口扫描、Banner解析、指纹匹配、识别等模块,真正理解网络空间搜索引擎的工作原理。

我们的目标是:用纯Python(asyncio异步架构)实现一个4000+行的工具,覆盖端口扫描、15+协议Banner抓取、200+服务指纹识别、30+ CMS检测、20+漏洞检测、IP地理定位、SQLite存储、可视化报告生成等完整功能链。

二、整体架构

整个工具采用模块化设计,核心模块分工明确:

searcher.py (主控制器)
    ├── modules/port_scanner.py   — 异步TCP端口扫描
    ├── modules/banner_grab.py    — 多协议Banner抓取
    ├── modules/fingerprint.py    — 服务指纹识别引擎
    ├── modules/web_detect.py     — CMS/WAF/技术栈识别
    ├── modules/vuln_check.py     — 常见漏洞检测
    ├── modules/geo_lookup.py     — IP地理定位
    ├── modules/crawler.py        — 网页爬虫
    ├── storage.py                — SQLite存储引擎
    ├── report.py                 — HTML报告生成器
    ├── utils.py                  — 工具函数
    └── db/fingerprints.json      — 指纹特征库

扫描流程分为五个阶段:

  1. 端口扫描 → 发现目标IP上的开放端口
  2. Banner抓取+指纹识别 → 获取服务响应,匹配软件名和版本
  3. Web应用检测 → 对HTTP/HTTPS端口识别CMS、WAF、Title
  4. 漏洞检测 → 检查未授权访问、默认口令等常见漏洞
  5. 地理定位 → 查询IP的国家、城市、ISP、ASN信息

每个阶段都是异步执行的,通过asyncio信号量控制并发数,在保证速度的同时避免对目标造成过大压力。

三、异步端口扫描:速度的秘密

端口扫描是整个工具的基础。传统的串行扫描一个端口需要等待TCP连接结果(超时通常2-3秒),扫描一台主机的70个常用端口就需要2-3分钟。

使用asyncio可以并发发起数百甚至数千个TCP连接尝试,将扫描时间压缩到几秒。

核心实现非常简洁:

async def scan_port(self, ip: str, port: int) -> PortResult:
    result = PortResult(ip=ip, port=port, state="closed")
    start = time.monotonic()
    try:
        _, writer = await asyncio.wait_for(
            asyncio.open_connection(ip, port),
            timeout=self.timeout
        )
        elapsed = (time.monotonic() - start) * 1000
        result.state = "open"
        result.latency_ms = round(elapsed, 2)
        writer.close()
        await writer.wait_closed()
    except asyncio.TimeoutError:
        result.state = "filtered"
    except ConnectionRefusedError:
        result.state = "closed"
    return result

asyncio.open_connection() 是Python标准库提供的异步TCP连接函数,内部使用操作系统的非阻塞socket。当数百个这样的协程并发运行时,它们共享同一个事件循环,由操作系统epoll/kqueue统一调度IO,效率远高于多线程方案。

并发控制是关键。如果同时发起数万个连接,可能会耗尽文件描述符或触发目标的防护机制。我们使用asyncio.Semaphore进行信号量控制:

self.semaphore = asyncio.Semaphore(self.concurrency)  # 默认500

async def _scan_with_semaphore(self, ip: str, port: int):
    async with self.semaphore:
        return await self.scan_port(ip, port)

同时采用分批执行策略,每批创建concurrency * 2个任务,用asyncio.gather()并发执行,避免一次性创建过多协程导致内存爆炸。

实测性能:扫描一个/24网段(254个IP)的70个常用端口(约18000个目标),使用200并发,耗时约25秒,扫描速率约700端口/秒。

四、Banner抓取:读懂服务的"名片"

发现开放端口只是第一步,更重要的是知道端口上运行的是什么服务。Banner是服务在连接建立时主动或被动返回的欢迎信息,通常包含软件名称和版本号。

不同协议的Banner获取方式差异很大,我们实现了15+种协议的专用抓取器:

:连接后直接读取第一行,格式为SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1,可以直接解析出协议版本和软件信息。

HTTP/HTTPS:发送HEAD / HTTP/1.1请求,从响应头的Server字段获取Web服务器信息。HTTPS还需要处理TLS握手和证书信息提取。

FTP:连接后服务端会主动发送欢迎信息,如220 ProFTPD 1.3.5e Server。还可以发送SYST命令获取操作系统类型。

MySQL:MySQL的握手协议比较特殊,服务端发送一个二进制协议包。我们需要解析包头(4字节长度+序列号),跳过协议版本字节,读取以null结尾的版本字符串。

Redis:发送INFO server命令,返回的文本中包含redis_versionredis_modeos等丰富信息。

VNC:VNC连接后会发送RFB协议版本,如RFB 003.008,可以判断VNC服务版本。

协议猜测逻辑也很重要。当通用TCP连接没有返回有效banner时,我们会根据端口号进行协议推测(如22=SSH,3306=MySQL),然后尝试该协议的专用握手方式获取更精确的信息。

五、指纹识别:从Banner到软件名

拿到Banner后,需要精确识别出软件名称和版本号。这是指纹匹配引擎的核心工作。

我们的指纹数据库db/fingerprints.json包含200+条规则,每条规则是一个正则表达式模式,匹配成功后可以从捕获组中提取版本号。

例如,识别Apache HTTP Server的规则:

{
    "service": "http",
    "pattern": "Server:\\s*Apache/([\\d.]+)",
    "software": "Apache HTTP Server",
    "group": 1,
    "product": "Apache",
    "cpe": "cpe:/a:apache:http_server"
}

当Banner中出现Server: Apache/2.4.54时,正则匹配成功,捕获组1提取出版本号2.4.54

识别引擎会遍历所有规则,计算每条匹配的置信度。置信度由多个因素决定:

  • 基础置信度:0.5(匹配成功即有)
  • 端口匹配加分:+0.2(如3306端口匹配MySQL规则)
  • 已知服务匹配加分:+0.15
  • 有软件名加分:+0.1
  • 有版本捕获组加分:+0.05

最终选择置信度最高的匹配结果。当没有规则匹配成功时,回退到启发式识别——根据Banner中的关键词(如"ssh-"、"http/1")和端口号进行粗略分类。

指纹库覆盖的服务类型包括:

  • 远程访问、Dropbear、 SSH、MikroTik
  • Web服务器:Apache、Nginx、IIS、、Caddy、Tengine、OpenResty
  • FTP:vsftpd、ProFTPD、FileZilla、Pure-FTPd、Serv-U
  • 邮件:Postfix、Exim、Sendmail、Exchange、Dovecot
  • 数据库:MySQL、MariaDB、PostgreSQL、MongoDB、Redis、Elasticsearch
  • 中间件:Tomcat、WebLogic、JBoss、Jetty、Gunicorn
  • 设备:Hikvision、Dahua摄像头、HP打印机、Synology NAS
  • 安全设备:pfSense、FortiGate、Palo Alto、SonicWall

六、Web应用识别:30+ CMS指纹

对于HTTP/HTTPS端口,仅知道Web服务器是不够的。还需要识别后端运行的是什么CMS(内容管理系统)、使用了什么框架、是否有WAF(Web应用防火墙)。

CMS识别的原理是基于页面特征匹配。每种CMS都有独特的"指纹",比如:

  • WordPress:页面中出现wp-content/wp-includes/wp-json等路径
  • ThinkPHP:页面或响应头中出现ThinkPHP标识,或存在thinkphp_show_page_trace
  • Laravel:Cookie中出现laravel_session
  • Spring Boot:出现Whitelabel Error PageX-Application-响应头
  • Shiro:Cookie中出现rememberMe
  • GitLab:Cookie中出现_gitlab_session
  • Jenkins:响应头中出现X-JenkinsX-Hudson

WAF检测也基于类似的特征匹配。例如Cloudflare的特征是响应头包含cf-ray,AWS WAF的特征是x-amzn-requestid

此外,我们还会提取页面Title、favicon hash(类似Shodan的http.favicon.hash查询)、技术栈(jQuery、Bootstrap、PHP、ASP.NET等)。

七、漏洞检测:20+未授权访问检查

在扫描过程中,我们同时对已知端口进行常见漏洞的快速检测。重点关注的是"未授权访问"类漏洞——这类漏洞危害大、检测简单、误报率低。

以Redis未授权访问检测为例:

async def _check_redis(self, ip, port):
    reader, writer = await asyncio.wait_for(
        asyncio.open_connection(ip, port), timeout=self.timeout
    )
    writer.write(b"INFO server\r\n")
    await writer.drain()
     = await asyncio.wait_for(reader.read(8192), timeout=self.timeout)
    text = data.decode("utf-8", errors="replace")
    writer.close()
    await writer.wait_closed()

    if "redis_version" in text:
        return VulnResult(
            vuln_name="Redis Unauthorized Access",
            severity="", cvss=9.8
        )

原理很简单:如果Redis没有设置密码认证,发送INFO server命令就能获取服务器信息,返回中包含redis_version字段。这意味着攻击者可以直接执行任意Redis命令,包括写入crontab、SSH公钥等,实现远程代码执行。

类似地,MongoDB可以通过Wire Protocol发送listDatabases命令检测;Elasticsearch可以通过GET /获取集群信息;Kubernetes API可以通过/api/v1/namespaces获取命名空间列表;Docker API可以通过/version获取版本信息。

我们实现了20+种漏洞检测,覆盖Redis、MongoDB、Elasticsearch、Memcached、Kubernetes、Docker、ZooKeeper、CouchDB、Consul、etcd、MinIO、Hadoop YARN、Spark、Jupyter Notebook、ActiveMQ、RabbitMQ、Nacos、Prometheus、Grafana、Solr、Druid、Portainer等常见组件。

每种漏洞都关联了CVSS评分和详细描述,检测结果直接存储到数据库的vulns表中。

八、SQLite存储:轻量高效的本地数据库

所有扫描结果都存储在SQLite数据库中。选择SQLite而非MySQL/PostgreSQL的原因:

  1. 零配置:不需要安装额外的数据库服务,Python内置支持
  2. 单文件:整个数据库就是一个文件,方便备份和迁移
  3. 性能足够:对于千万级以下的数据量,SQLite的查询性能完全够用
  4. 并发支持:通过check_same_thread=False配置,支持多线程读写

数据库包含三张核心表:

hosts表:存储每个开放端口的完整信息,包括IP、端口、服务、Banner、软件版本、地理位置、Web信息、漏洞统计等。以(ip, port)作为唯一约束,实现自动去重和更新。

vulns表:存储检测到的漏洞详情,包括漏洞ID、名称、严重级别、CVSS评分、证据等。

scan_history表:记录每次扫描的元数据,便于追踪扫描历史。

我们在多个字段上建立了索引(ip、port、service、country_code、cms、software),确保搜索查询的效率。

九、HTML可视化报告

report.py模块生成的HTML报告是整个工具的亮点之一。报告采用暗色主题设计,包含:

  • 统计概览:开放端口数、独立IP数、服务类型数、各严重级别漏洞数
  • 地图可视化:使用Leaflet.js在OpenStreetMap上标注所有发现的主机位置,点击标记可查看IP、端口、服务、国家信息
  • 漏洞列表:按严重级别高亮显示,包含CVSS评分和漏洞描述
  • 按服务分类:Tab切换不同服务类型的主机列表
  • 搜索过滤:支持关键词实时搜索过滤表格内容

报告是纯静态HTML文件,不依赖后端服务,可以直接在浏览器中打开查看。

十、使用示例

安装和使用非常简单:

# 安装
git clone <repo> && cd net-
pip install -r requirements.txt

# 扫描单个IP
python searcher.py --ip 192.168.1.1

# 扫描网段
python searcher.py --ip 10.0.0.0/24 --port 22,80,443,3306,6379

# 大规模扫描(500并发)
python searcher.py --ip 172.16.0.0/16 --threads 500

# 搜索结果
python searcher.py --search "nginx" --output json
python searcher.py --service redis --country CN --output html

# 查看数据库统计
python searcher.py --stats

命令行工具支持--ip(目标IP/CIDR/范围)、--port(端口)、--search(关键词搜索)、--service/--country/--cms(过滤条件)、--output(输出格式)、--threads(并发数)等参数。

十一、性能与局限

实测性能参考:

场景 规模 耗时
单IP常用端口 1×70 ~3秒
/24网段 254×70 ~30秒
/16网段Top20 65536×20 ~5分钟

相比Shodan,我们的工具存在明显局限:

  • 无分布式架构:单机运行,无法像Shodan那样持续扫描整个互联网
  • 无持久化索引:SQLite适合中小规模数据,无法支撑Shodan那样的数十亿条记录
  • 无历史快照:Shodan可以查看某个IP的历史变化,我们的工具只能记录当前状态
  • 扫描被动性:需要主动触发扫描,不像Shodan那样持续自动更新

但对于安全研究、内部资产管理和渗透测试场景,这些局限并不影响工具的核心价值。

总结

通过4000+行Python代码,我们实现了一个功能完整的网络空间搜索引擎原型。它涵盖了端口扫描、Banner抓取、指纹识别、CMS检测、漏洞检测、地理定位、数据存储和可视化报告的完整技术栈。

这个项目不仅是一个实用的安全工具,更是一次深入理解网络协议、异步编程和安全检测技术的实践。每一个模块——从MySQL握手包解析到Redis INFO命令,从正则指纹匹配到异步信号量控制——都蕴含着网络空间安全领域的核心知识。

完整源码已开源,欢迎Star和PR。

常见问题

一、为什么自建?

>一、为什么自建?Shodan是全球最大的网络空间搜索引擎,它持续扫描互联网上所有可达设备,索引开放端口、服务指纹、地理位置等信息。对于安全研究人员而言,它是资产发现、威胁情报的利器。但Shodan的免费API有严格限制,且数据不在本地,无法定制。 自建一个&quot;迷你Shodan&quot;有三个核心价值: 数据自主:扫描结果存储在本地SQLite数据库中,可离线查询、二次分析,不受API配额限制。 定制灵活:可以针对特定目标深度扫描,添加自定义漏洞检测规则,集成到内部安全平台。 技术深度:通过亲手实现端口扫描、Banner解析、指纹匹配、CMS识别等模块,真正理解网络空间搜索引擎的工作

评论