ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python漏洞扫描系统实战:从端口扫描到POC验证的完整实现

Python漏洞扫描系统实战:从端口扫描到POC验证的完整实现 简介Python漏洞扫描系统项目源码是一套基于Django的完整Web应用实战项目面向有一定编程基础、希望深入Web后端开发的开发者与高校学生适用于课程设计、毕业设计或日常技术学习。项目包含通用管理框架、数据库脚本、设计文档及演示PPT覆盖后端开发、数据库集成、前端交互等关键环节演示了从数据模型定义、业务逻辑封装到模板渲染与静态资源管理的完整构建流程。资源包共380个文件压缩后大小约83.89MB。除26个Python源码与编译文件外还包含前端样式与脚本css/js/html、页面模板tmpl、图像素材gif/png/jpg、SQL数据库脚本以及Word/PPT等说明材料同时收录了Nikto等安全扫描工具的配置参考目录按功能模块划分便于按需查阅。已有182人学习下载。通过分析项目设计思路与实现细节读者可系统理解漏洞扫描功能的构建方法与扩展策略提升Django框架应用、数据库操作和前后端协同开发的能力。配套文档与PPT能帮助快速梳理系统架构、部署流程和演示要点适合作为项目复现、二次开发及技术交流的基础。1. Python漏洞扫描系统拿到这套交付包先别急着跑main.py你可能是因为毕设、课程设计或者公司内网自查才搜到“Python漏洞扫描系统_(项目源码数据库脚本文档LWPPT)”这个标题。这类交付包网上很多结构大同小异源码负责扫数据库脚本负责存文档和LW论文说明书负责讲清楚设计PPT负责答辩。但说句难听的十份里有八份原样跑不起来要么缺依赖要么数据库脚本导入就报错要么扫了半天结果全为零。这套系统的本质是一个用Python写的轻量级漏洞扫描框架核心链路是“资产录入→端口扫描→服务指纹识别→漏洞规则匹配→结果落库”能解决的是几十到几百台规模内网的自动化漏扫问题不是要替代Nessus或OpenVAS这种商业级扫描器。适合谁适合打算二次开发、想搞懂扫描器原理、或者需要一个可演示可答辩项目的从业者和学生。这篇不帮你洗稿PPT只讲怎么让人信服地把它跑通、改好、用起来。2. 漏洞扫描系统的骨架选型逻辑、模块划分与数据流2.1 为什么这个场景用Python合理以及它的边界在哪常见的说法是“Python写扫描器太慢”这话一半对一半不对。端口扫描的并发模型里真正的瓶颈往往不是CPU计算而是网络往返时延和socket超时等待。Python的concurrent.futures线程池加上非阻塞socket在千兆内网扫一个C段254个IP的常用端口几分钟量级是能接受的。真正慢的是需要逐一发送HTTP请求并读取响应的Web漏洞POC检测那种场景下瓶颈在IO等待Python和Go的差距远没有想象中大。另一个用Python的硬核理由是生态requests、paramiko、python-nmap、cryptography这些库让协议处理从零写变成拼装开发效率高出一个量级这对项目包里的“文档LWPPT”来说也是巨大的便利——代码量少讲起来清楚。但这套系统的适用边界必须说透。第一目标规模有上限线程池开到几百以后本机文件描述符和网络栈会先扛不住第二它对协议的理解是“够用就好”处理不了加密流量里的深度检测第三它不做主动攻击只做“验证型探测”。所以当你的需求是扫几万台公网资产、或者要检测WebLogic反序列化这类深度漏洞时这个框架给的是思路不是答案。把它当作一个可裁剪的基线去加自己的POC规则才是正确的使用姿势。2.2 四层模块拆解从任务下发到报告生成的数据流不管项目包里源码怎么组织成熟的Python漏洞扫描系统基本逃不出下面四层结构。第一层是任务管理层负责读取扫描目标、拆分成IP:端口组合、管理扫描任务状态。第二层是采集层做端口存活探测和Banner抓取产出“哪些主机开放了哪些端口、跑着什么服务”的资产清单。第三层是检测层把资产清单和漏洞知识库做匹配挑出适用的POC规则并执行。第四层是存储与报告层把命中结果写回数据库同时导出HTML或CSV报告给你的LW和PPT用。数据流就是一条管道任务表给出目标 → 主机表记录资产 → 服务表记录端口和Banner → 漏洞检测命中后写入结果表。项目包里的数据库脚本SQL文件通常就是为这四张表准备的。拿到代码先别急着读main函数先梳理这四张表的字段你就知道这个系统的检测逻辑大概覆盖哪些层面如果漏洞表里存的是CVE编号加正则那它的逻辑就是指纹比对如果存的是URL路径和返回码那它走的是POC请求验证。2.3 从LW和PPT里提炼信息但以代码和数据库脚本为准像“Python漏洞扫描系统”这种交付包里LW和PPT的价值比很多人以为的大但用法不是“照着背”。打开LW的目录直接找“系统设计”和“数据库设计”两章。数据库设计章节里的ER图和表结构说明是你理解数据库脚本最快的索引。而PPT的价值在于它的架构图往往比代码里的注释更清晰能帮你用五分钟建立起模块归属的认知。但有个血泪教训PPT和LW里画的功能代码里不一定实现了。文档可能是上一届的学长从别的项目改来的数据库脚本的表名和代码里的SQL查询对不上这是非常常见的事。所以我一般会先打开数据库脚本看里面建了几张表、每张表的字段名然后去源码里搜索这些表名。搜得到说明这个模块是真的搜不到说明代码要么不完整要么查询逻辑走得是别的路径。记住一个原则文档描述系统应该是什么样代码和SQL描述系统实际上是什么样后者永远优先。3. 从源码跑通最小可用系统环境准备、数据库脚本与首次启动3.1 环境准备用虚拟环境隔离依赖避免毁掉系统Python拿到项目包第一步不是直接pip install是决定把环境装在哪。项目源码多半带着一份requirements.txt里面列着requests、pymysql、paramiko之类的常用库。直接装进系统Python风险极大——你系统里可能已经有别的项目在用某个库的旧版本一升级全崩。用虚拟环境隔开是目前最可靠的做法。以下命令在项目目录下执行我已经把注释写在里面# Python版本建议3.8以上先确认版本 python3 --version # 创建虚拟环境venv目录会出现在当前文件夹下 python3 -m venv venv # 激活虚拟环境Linux/Mac用sourceWindows用venv\Scripts\activate source venv/bin/activate # 升级pip避免后续安装解析出旧版依赖 pip install --upgrade pip # 安装项目依赖如果镜像源慢可以追加 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -r requirements.txt逻辑说明venv是Python自带的虚拟环境模块不需要额外装工具。激活之后当前命令行里的python和pip都指向venv/bin目录下的解释器不会再污染系统环境。requirements.txt里的库版本如果和Python版本冲突比如老项目里写了pymysql0.9.3这类旧版本一般也能装上但如果遇到编译型库装不上优先看是不是Python版本太高导致的解决方案是换装3.8或3.10。安装后跑一句pip list核对核心依赖是否齐全。漏掉最常见的不是requests而是pymysql——因为有的源码连数据库用的是MySQLdb它是Python2时代的老库在Python3里要装mysqlclient名字对不上就会报“ModuleNotFoundError: No module named MySQLdb”。这个坑后面避坑章我会展开讲。3.2 导入数据库脚本建库、建表、灌初始数据的正确顺序数据库脚本是整个交付包里最容易翻车的部分。原因很现实脚本可能是用MySQL 5.7写的你现在装的是MySQL 8.0脚本里写了utf8你数据库默认排序规则是utf8mb4_general_ci脚本里表之间有外键约束导入顺序错一个就中断。下面是标准的导入流程先建库再指定字符集然后导入脚本文件-- 1. 创建数据库显式指定字符集避免中文乱码 CREATE DATABASE IF NOT EXISTS vuln_scanner DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 2. 进入该库 USE vuln_scanner; -- 3. 创建主机资产表记录扫描目标的IP、操作系统、开放端口 CREATE TABLE IF NOT EXISTS t_host ( host_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主机ID, ip_address VARCHAR(64) NOT NULL COMMENT 主机IP, os_guess VARCHAR(128) DEFAULT NULL COMMENT 操作系统猜测, open_ports VARCHAR(1024) DEFAULT NULL COMMENT 开放端口列表逗号分隔, scan_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 最后扫描时间 ) ENGINEInnoDB COMMENT主机资产表; -- 4. 创建漏洞知识库表存储指纹和漏洞规则 CREATE TABLE IF NOT EXISTS t_vuln_kb ( vuln_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 漏洞ID, vuln_name VARCHAR(255) NOT NULL COMMENT 漏洞名称, cve_id VARCHAR(32) DEFAULT NULL COMMENT CVE编号, fingerprint VARCHAR(512) DEFAULT NULL COMMENT 服务指纹特征串, poc_type VARCHAR(32) DEFAULT banner COMMENT 检测类型banner/http_path, poc_detail TEXT COMMENT POC细节如URL路径或正则 ) ENGINEInnoDB COMMENT漏洞知识库;参数说明utf8mb4不是普通的utf8它能存四字节的Emoji和生僻字漏洞描述里常有不规范字符用utf8容易出现“Incorrect string value”报错。ENGINEInnoDB必须显式写MyISAM不支持外键和事务。AUTO_INCREMENT是自增主键保证每行记录唯一的ID业务代码里的外键都依赖它。导入脚本用mysql命令行客户端执行source命令或者重定向导入都行。如果是Windows环境注意脚本文件路径不能带中文导入日志里一旦出现ERROR 1452说明外键关联的表还没建按脚本里表依赖的先后顺序重新导入。导入完执行SHOW TABLES;确认表数量与LW数据库设计章节一致。到这里数据库这半边就算落地了。3.3 首次启动之前配置文件里的三个必调参数绝大多数Python项目包都有一个配置文件常见名字是config.ini、settings.py或db_config.py。不管叫啥里面总有几个参数是环境相关的不改成你本机的值程序连数据库都连不上。最典型的配置项长这样[database] host 127.0.0.1 port 3306 user root password 123456 database vuln_scanner charset utf8mb4 [scanner] threads 50 timeout 3 retry_times 2 enable_ping true参数说明[database]段的配置必须和3.2节创建的库完全对应user别用root做生产环境但本地调试阶段为了方便可以先用root。threads是扫描并发线程数不是越大越好——上一节说要考虑本机文件描述符上限笔记本上开到200以上扫大网段时操作系统会先报“Too many open files”。timeout是单次socket连接的超时秒数内网设2到3秒足够公网目标可以放宽到5秒太长会拖慢整个扫描任务。enable_ping决定是否先做ICMP存活探测如果你的目标主机禁ping这个开关反而会漏掉主机关掉直接扫更保险。改完配置怎么验证配置正确到这一步别急着跑扫描先写一个几行的连通性测试确认能查到数据再放全量任务# 进入项目目录并激活虚拟环境 source venv/bin/activate # 用Python快速验证数据库连接和表是否存在 python -c import pymysql conn pymysql.connect(host127.0.0.1, port3306, userroot, password123456, databasevuln_scanner, charsetutf8mb4) cur conn.cursor() cur.execute(SELECT COUNT(*) FROM t_host) print(host count:, cur.fetchone()[0]) conn.close() 这段命令的逻辑通过pymysql建立连接后执行一个最简单的SELECT COUNT(*)如果输出host count: 0说明连接成功、表也存在只是还没数据。如果输出的是1146 Table doesnt exist说明表名不对回到3.2节检查导入是否完整。这一步几十秒的验证能省下后面排查“扫描结果为空”的几小时属于典型的后悔药。3.4 首次试扫描先用一条记录验证完整链路数据库和配置都就绪后不要直接扫描大网段先在t_host表里手动插入一条你知道肯定开放的记录。拿自己局域网里的网关IP或者一台测试服务器举例INSERT INTO t_host (ip_address, os_guess) VALUES (192.168.1.1, unknown);然后看项目主程序怎么接收目标。有的系统是python main.py --target 192.168.1.1有的是python main.py --task 1差别在于目标是从命令行传还是从数据库任务表读。去看LW里的“运行环境与启动方式”部分或者直接翻main.py的argparse参数定义。跑起来以后观察屏幕日志里扫描链路是否走通先出ping探测结果再出端口列表再出指纹信息最后出一个“正在匹配漏洞库”的日志。到第三步就算链路通了漏洞检测部分单独在第四章拆开讲。4. 核心模块复现端口探测、服务指纹识别与POC判定4.1 端口扫描的并发写法线程池、超时与结果收束端口扫描是整个系统的地基端口不对后面全是空的。这部分主流做法两种一种是调用python-nmap做系统级nmap扫描跑得快、指纹准但依赖目标机器装了nmap二进制另一种是直接用socket做TCP全连接探测没有外部依赖纯Python就能跑。项目包源码里十有八九是第二种因为它不需要额外安装演示起来也更“纯”。它的并发模型是线程池核心代码如下import socket from concurrent.futures import ThreadPoolExecutor, as_completed def check_port(ip: str, port: int, timeout: float 3.0) - tuple: 探测单个TCP端口是否开放 返回 (ip, port, state)state为open或closed sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) try: result sock.connect_ex((ip, port)) if result 0: return (ip, port, open) return (ip, port, closed) except socket.timeout: return (ip, port, filtered) except socket.error: return (ip, port, closed) finally: sock.close() def scan_ports(ip: str, ports: list, max_workers: int 50, timeout: float 3.0) - list: 并发扫描一组端口返回开放端口列表 open_ports [] with ThreadPoolExecutor(max_workersmax_workers) as executor: future_map {executor.submit(check_port, ip, port, timeout): port for port in ports} for future in as_completed(future_map): _, port, state future.result() if state open: open_ports.append(port) return sorted(open_ports)逻辑说明connect_ex和connect的区别在于前者不会抛异常而是通过返回值判断结果0表示连接成功。这个设计特别适合并发场景——异常处理放在一个函数里线程池回调逻辑干净。超时设为3秒是针对内网环境的经验值如果你扫公网这个值建议调到5到8秒否则回包慢的端口会被误判为closed。as_completed的作用是“谁先完成谁先返回”避免某个慢端口阻塞整个线程池的收束。参数说明max_workers不要拍脑袋设成几千。线程池的线程数超过系统fd限制后新线程创建会直接失败。用ThreadPoolExecutor并且不显式写max_workers的话Python默认是min(32, os.cpu_count() 4)这在一个C段内网够用扫大端口范围不够。比较省心的公式是min(200, os.cpu_count() * 20)再往上就开始出现socket创建失败收益急剧下降。4.2 服务指纹识别Banner匹配漏洞库别迷信正则端口开放只是第一步识别端口背后跑的是什么服务才是漏洞判定的依据。Banner抓取的做法并不神秘连接上端口后发一个简单的请求比如对HTTP端口发GET / HTTP/1.0\r\n\r\n对SSH端口什么都不发直接读等待服务端主动推送版本信息。抓到一段文本后和漏洞知识库t_fingerprint里的指纹串做匹配。最常见的匹配方式是“包含匹配”因为现实中的Banner千奇百怪正则写得太精确反而误报高。示例代码如下import socket import re def grab_banner(ip: str, port: int, timeout: float 3.0) - str: 抓取端口BannerHTTP端口发简单请求其他端口直接读取 try: sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((ip, port)) banner b if port in (80, 8080, 8000, 443, 8443): sock.send(bGET / HTTP/1.0\r\nHost: %s\r\n\r\n % ip.encode()) while True: chunk sock.recv(1024) if not chunk: break banner chunk if len(banner) 4096: break sock.close() return banner.decode(utf-8, errorsignore).strip() except Exception as e: return def match_vuln_by_fingerprint(banner: str, vuln_list: list) - list: 用包含匹配的方式把Banner和漏洞规则里的特征串比对 hits [] for vuln in vuln_list: pattern vuln.get(fingerprint, ) if pattern and pattern.lower() in banner.lower(): hits.append(vuln) return hits逻辑说明grab_banner里对HTTP端口发请求是有讲究的。HTTP/1.0不需要处理chunked编码服务端返回完就断开适合快速抓取HTTP/1.1需要额外解析Connection: close逻辑复杂度翻倍。recv(4096)上限是为了防止某些服务无限输出垃圾数据吃满内存。banner.decode(..., errorsignore)不能省——Banner可能是任意编码直接decode()会抛异常导致整个扫描线程挂掉这个位置是典型的“黑匣子”翻车点。参数说明errorsignore表示遇到非法字节序列时跳过而不是报错这在指纹识别里是保底策略。指纹匹配要做lower()统一小写因为很多服务端Banner大小写混用比如Apache/2.4.41 (Ubuntu)和apache/2.4.41其实是同一个东西。这条代码的逻辑是pattern in banner意味着指纹串越长越准确但也会越漏实际使用中建议单条指纹控制在20到60个字符之间太短误报太长漏报。4.3 POC判定与结果回写从命中到入库再到报告生成指纹匹配能发现“疑似”但漏洞扫描系统最终交付的必须是“确认”。POC校验阶段的工作是对指纹命中的服务做一次无害的验证请求比如检查Web服务是否存在某个已知路径的目录遍历或者SSH服务是否存在弱口令。以Web路径型漏洞举一个典型写法import requests def verify_http_poc(url: str, poc_path: str, expect_code: int 200, timeout: int 5) - bool: 对目标URL发起请求判断指定路径是否存在 full_url url.rstrip(/) poc_path try: resp requests.get(full_url, timeouttimeout, verifyFalse, allow_redirectsFalse) if resp.status_code expect_code: return True # 兼容部分服务返回302但Location揭示了存在的情况 return False except requests.RequestException: return False def save_scan_result(db_conn, host_id, vuln_id, detail: str) - None: 将确认的漏洞写入scan_result表 sql ( INSERT INTO t_scan_result (host_id, vuln_id, detail, found_time) VALUES (%s, %s, %s, NOW()) ) cur db_conn.cursor() cur.execute(sql, (host_id, vuln_id, detail)) db_conn.commit() cur.close()逻辑说明verifyFalse是关闭SSL证书校验因为扫描场景下目标证书经常是自签的校验会导致大量漏报。allow_redirectsFalse防止跟随跳转——有的漏洞路径本身返回302但跟随跳转之后是200反而会误判。expect_code200并不是所有POC都适用一些漏洞的判定标志可能是401、403或者特殊响应体所以这个函数只是其中最基础的一种项目包里如果有更复杂的POC基本也是在这个框架上加响应体关键词匹配。参数说明poc_path的格式要带前导斜杠比如/../../etc/passwd被编码后是/%2e%2e/%2e%2e/etc/passwd。注意requests会自动处理URL编码但你传入的字符串不能是空格否则会被自动编码成%20和目标路径不一致导致验证失败。save_scan_result里使用了参数化查询(%s, %s, %s)目的是防止SQL注入——这一点务必不要改成字符串拼接漏洞扫描系统本身就在检测安全漏洞自己的代码反而注入那就太讽刺了。5. 避坑与常见问题跑通之后的5个关键检查点5.1 现象启动即报ModuleNotFoundError缺MySQLdb或pymysql现象python main.py执行不到两秒就退出错误信息是ModuleNotFoundError: No module named MySQLdb。原因源码里写的是import MySQLdb这是Python2时代最流行的MySQL驱动在Python3里没有官方同名包。它的替代品是pymysql但很多项目源码不会改import语句需要你自己加一行兼容代码。解决在项目入口文件或者db_connect.py开头加上一句import pymysql; pymysql.install_as_MySQLdb()这会让MySQLdb名字指向pymysql的实现。前提是已经安装pymysql。如果安装pymysql失败检查pip源和网络换清华源重试。5.2 现象数据库脚本导入时反复报错ERROR 1452外键失败现象执行source xxx.sql时建表到一半中断提示Cannot add or update a child row: a foreign key constraint fails。原因表之间的外键有依赖顺序。比如t_scan_result引用了t_host和t_vuln_kb的主键如果结果表先于主机表或漏洞表创建外键约束无法建立。另一个常见原因是已有旧表存在结构不一致导致约束冲突。解决重新导入前先清理DROP TABLE IF EXISTS按子表到父表的顺序执行导入时严格按“父表→子表”顺序。如果脚本文件里已经包含DROP TABLE语句直接重跑即可。导入完用SHOW CREATE TABLE t_scan_result;检查外键约束是否存在。5.3 现象扫描流程正常走完但漏洞结果表和报告全是0条命中现象日志显示端口扫到十几个Banner也抓到了但“漏洞命中数”永远是0。原因这是最常见也最隐蔽的坑。漏洞知识库表里指纹数据为空或者指纹的字段名与代码里读取的不一致。很多项目包为了控制交付物体积数据库脚本只建了表没灌初始数据。没有漏洞规则扫描器再能干也匹配不出任何东西。解决先查SELECT COUNT(*) FROM t_vuln_kb;如果是0去源码里找有没有vuln_data.sql或CSV种子文件有就导入没有就按LW漏洞库设计手写插入几条典型指纹。另外核对代码里读取指纹的字段名——比如脚本里字段叫pattern代码里读的是fingerprint那匹配条件永远为假这种bug靠眼睛看很难发现打印一行日志就能暴露。5.4 现象扫描开始后界面卡死线程池不再并发现象GUI版本的程序点击“开始扫描”后界面白屏、无响应命令行版本表现为日志停止输出。原因扫描任务直接跑在GUI主线程里socket等待超时期间主线程被阻塞界面无法刷新。这不是扫描器逻辑错误是并发模型设计错误。解决把扫描任务丢进后台线程GUI线程只负责读日志。命令行版本不存在这个问题但如果源码里用了tkinter或PyQt可以把扫描函数用threading.Thread(targetscan_runner).start()包一层。同时把日志用队列queue.Queue传递给界面而不是直接在子线程里操作控件。这条不改演示PPT时必翻车。5.5 现象漏洞报告命中一堆无意义的目标误报率极高现象扫描完发现漏洞报告里出现了大量奇怪的命中比如同一个Banner匹配了五种不同服务的漏洞。原因指纹特征串写得太短或太常见。比如用Apache做指纹所有nginx前端反代的响应头里也可能带Apache字样用2.4做版本指纹会把所有含这个数字的Banner都拉下水。这是指纹匹配的经典误报也是LW答辩时老师最喜欢问的一个点。解决收紧指纹长度至少配合Server:前缀一起做包含匹配例如Server: Apache/2.4.41明显比Apache可靠得多另外在POC阶段增加一步响应验证指纹命中不算漏洞必须经过4.3节的HTTP路径验证通过才算。这样误报率能下降一大截。6. 从演示到可验证用受控靶机校准这套扫描器要让人相信你的Python漏洞扫描系统真的有用光扫自己网关是不够的结果无法验证。推荐的做法是搭一个本地靶机下载一个开源靶场如DVWA或者更轻量直接用Python写一个返回特定Banner的假服务。核心目的是构造“已知结果”然后检查扫描器扫出来的结果和已知结果是否一致。我一般会建两个服务一个返回Server: Apache/2.4.41的假HTTP服务配一条对应漏洞指纹一个返回SSH-2.0-OpenSSH_7.2的假SSH服务也配一条指纹。扫描后对照报告命中率、误报率一目了然。调参方面有几个值得反复试验的设置项线程池从50调到200观察扫描耗时曲线的拐点超时从1秒加到5秒观察误报率变化指纹匹配从短特征换成Server:前缀特征看命中报告集变化。把这些测试结论写进LW的实验章节就是最扎实的“系统验证”素材比抄一篇“实验结果与分析”有说服力得多。还有一个我自己的习惯把扫描结果导出CSV之后用pandas做个简单透视按漏洞类型看分布按端口看命中率。这一步不增加任何代码复杂度但答辩或汇报时拿出来的是一张专业的图表而不是一张密密麻麻的文本清单。整套系统真正值得投入的地方不是照着PPT美化界面而是把新的POC规则一条条加进漏洞知识库把指纹匹配从“包含”升级到“正则分组提取版本号”。做到这一步这份交付包就不再是存档的毕设而是能持续用的内部自查工具。到今天我拿到任何一套漏扫项目包第一件事仍然是先导数据库脚本读表结构再回头看代码路径这个顺序帮我避掉过无数次“跑不起来”的尴尬。希望帮到你。本文还有配套的精品资源点击获取
返回列表