简介:这是一套基于Python开发的Web安全渗透测试工具集成资源,包含完整源代码与使用文档,主要面向计算机相关专业学生、安全测试初学者以及需要毕设/课设项目的开发者。工具提供从漏洞扫描到渗透测试的多种功能模块,并附带若干CVE漏洞检测脚本(如CVE-2020-14882、CVE-2018-2894等)与弱口令字典,可用于网站安全防护与漏洞验证。压缩包共124个文件,其中49个Python源码文件为核心程序,49个txt文件为说明文档与密码列表,另有20个abak备份文件及Markdown文档、License、RAR压缩包等辅助材料,整体约29.19MB,结构清晰便于查阅。目前已有48人学习下载,代码经作者毕设实测通过,答辩平均分96分,适合在此基础上进行二次开发或迁移到实际安全测试场景。学习时建议优先阅读README文档,按指引配置运行环境,即可快速上手并理解渗透测试工具的实现思路。
1. 一套能跑通答辩的 Python 渗透测试工具:先看它解决什么问题
做网络安全方向的毕业设计,最怕的不是写不出代码,而是答辩时被老师追问“你这套工具和网上现成的扫描器有什么区别”。如果你手里只有一堆零散脚本,这个问题基本答不上来;但如果有一套自己一行一行敲出来的工具集,还带了完整文档说明,整个答辩逻辑就顺了。
这套“基于 Python 的 Web 安全渗透测试工具集成”就是干这个用的:它不是某个单点脚本,而是把端口扫描、子域名探测、SQL 注入检测、XSS 检测、报告生成串起来的工具集,压缩包里带着源代码和文档说明。适合两类人——一类是安全方向或软件工程方向要做毕业设计的学生,另一类是刚入门渗透测试、想靠读源码理解工具原理的开发者。它解决的最大问题不是“扫描功能多强”,而是“有完整源码、能讲清每个模块怎么工作、可复现”。
2. 整体架构与核心脚本:把工具集拆开看,它由哪几块组成
拿到压缩包先别急着跑 demo,把目录结构看清楚。这类毕设工具集最怕的就是“一个文件里塞了所有功能”,跑是能跑,答辩时老师一看代码组织就给减分。这套项目用的是比较稳妥的分层写法:入口、模块、数据、文档四层分开,每个检测模块独立成文件,后续加功能不需要动主入口。
2.1 目录结构:先分清工具模块和辅助脚本
典型的结构是这样的:
websec-toolkit/ ├── main.py # 入口:命令行参数解析、模块调度 ├── requirements.txt # Python 依赖清单 ├── modules/ │ ├── port_scanner.py # 端口扫描模块 │ ├── subdomain_brute.py # 子域名爆破模块 │ ├── sql_detector.py # SQL 注入检测模块 │ ├── xss_detector.py # XSS 检测模块 │ └── reporter.py # 报告生成模块 ├── data/ │ ├── payloads/sqli.txt # SQL 注入 payload 字典 │ ├── payloads/xss.txt # XSS payload 字典 │ └── subdomains.txt # 子域名词典 └── docs/ ├── README.md # 项目说明 ├── 安装与使用.md # 环境搭建与命令示例 └── 测试报告示例.md # 答辩用的报告模板这个目录设计里有两个点是答辩容易被问到的。第一,data 目录单独放,说明 payload 字典和数据文件是解耦的,换字典不用改代码——老师问“我能不能换成自己的字典”时,你只需要回答“把文件放进去就行”。第二,docs 目录里的测试报告示例不是摆设,它是照着真实渗透测试报告格式写的,里面规定了漏洞描述、危害等级、复现步骤、修复建议四段结构。
依赖清单里需要重点说一句的是版本问题。requirements.txt 里一般会锁 Python 版本要求,常见的是 Python 3.8+,requests 和 dnspython 这两个是核心。装依赖前先确认本机 Python 版本,用 python --version 看一眼,低于 3.8 的话很多新语法用不了,高于 3.12 的话某些旧版依赖安装会出问题。这里我一般建议直接建一个虚拟环境,避免把系统 Python 环境搞乱。
2.2 端口扫描模块:从 socket 连接到线程池并发
端口扫描是整个工具集里最容易理解也最适合在答辩时现场讲的模块。原理不复杂:对目标主机的每个端口尝试建立 TCP 连接,连得上就说明端口开放。但实现上有三个细节决定扫描质量:超时时间、并发方式、端口列表。
# modules/port_scanner.py import socket from concurrent.futures import ThreadPoolExecutor, as_completed COMMON_PORTS = [21, 22, 23, 25, 53, 80, 443, 3306, 3389, 8080, 8443] DEFAULT_TIMEOUT = 3 # 单次连接超时,单位秒 DEFAULT_WORKERS = 50 # 并发连接线程数 def check_port(host, port, timeout=DEFAULT_TIMEOUT): """对单个端口发起 TCP 连接测试,返回 (端口号, 是否开放)""" try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(timeout) s.connect((host, port)) return port, True except (socket.timeout, OSError): return port, False def scan(target, ports=None, timeout=DEFAULT_TIMEOUT, max_workers=DEFAULT_WORKERS): """并发扫描目标主机的指定端口列表""" ports = ports or COMMON_PORTS open_ports = [] with ThreadPoolExecutor(max_workers=max_workers) as pool: futures = {pool.submit(check_port, target, p, timeout): p for p in ports} for fut in as_completed(futures): port, opened = fut.result() if opened: open_ports.append(port) return sorted(open_ports)这段代码的选型理由值得讲清楚。为什么用多线程而不是多进程?因为端口扫描的大部分时间花在等待 TCP 握手上,这个等待是网络 IO 阻塞,线程在阻塞时会主动让出 CPU,所以 50 个线程跑下来 CPU 占用几乎可以忽略;多进程反而要额外承担进程创建和 IPC 的开销。为什么用 ThreadPoolExecutor 而不是自己写 thread.join 循环?因为线程池替你管了任务的提交、执行、异常回收,代码量少一半,答辩时也更好解释。
参数调整按场景走。timeout=3 适合本机、局域网或者你已知目标网络质量不错的环境;扫描公网目标建议调到 5,因为跨运营商丢包严重,3 秒经常误判“连接超时”,把本来开放的端口漏掉。max_workers=50 是一个相对保守的值,校园网环境下跑不会触发系统限制;你拉到 200 试试,很快会碰到系统报错,这个在第五章展开讲。另外一个容易忽略的细节是端口列表:默认列表里的 8443 是很多管理后台的备用 HTTPS 端口,3306 是 MySQL,3389 是 Windows 远程桌面,这几个端口的命中结果对后续的漏洞检测方向很有参考价值。
2.3 子域名爆破模块:字典驱动加 DNS 并发解析
子域名收集在渗透测试里的作用有两个:扩大攻击面、找到测试环境中直接暴露的内部服务。这个模块做的事情很简单——拿字典里的每个前缀拼上主域名,然后做 DNS 的 A 记录解析,能解析出 IP 就是有效子域名。
# modules/subdomain_brute.py import dns.resolver from concurrent.futures import ThreadPoolExecutor def resolve_domain(full_domain): """解析域名的 A 记录,返回 IP 列表;解析失败返回空列表""" try: answers = dns.resolver.resolve(full_domain, 'A') return [answer.address for answer in answers] except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.exception.Timeout): return [] def subdomain_brute(domain, dict_path, threads=20): """读取字典文件,逐个拼接子域名并做并发解析""" found = {} with open(dict_path, 'r', encoding='utf-8') as f: words = [line.strip() for line in f if line.strip()] with ThreadPoolExecutor(max_workers=threads) as pool: futures = { pool.submit(resolve_domain, f"{word}.{domain}"): word for word in words } for fut in futures: ips = fut.result() if ips: found[futures[fut]] = ips return found这里有一个技术选型容易踩坑的点:为什么不用 Python 自带的 socket.getaddrinfo 来做解析,而是装一个 dnspython 依赖?socket.getaddrinfo 对“域名不存在”和“DNS 服务器暂时无响应”这两类情况都会抛异常,你没法干净地判断到底哪种情况,会把大量正常失败当成网络异常直接重试;而 dns.resolver 能精确捕获 NXDOMAIN(域名不存在)和 Timeout(查询超时),这两个分支在代码里分开处理,逻辑就清晰很多,调试的时候也容易定位问题。
并发线程数 threads=20 是刻意比端口扫描低的选择,原因是 DNS 查询的请求量非常大——字典里如果有几千条记录,解析失败的情况也占了绝大多数,如果线程数拉太高,很容易把校内 DNS 服务器或者运营商解析器打出过载限流,到时候整个网段的解析都会变慢,反而拖累扫描。另外这个模块的输出格式是字典,键是子域名字符串,值是 IP 列表,后续如果想做“子域名指纹识别”,可以直接对着这里的 IP 列表去做 HTTP 请求探测,数据结构不需要改。
3. 核心检测模块:SQL 注入与 XSS 的检测逻辑和参数调优
如果说第二章的端口扫描和子域名爆破是“工具集的外围”,那 SQL 注入检测和 XSS 检测才是这套渗透测试工具的“丹田”。这两个模块的检测逻辑直接决定你答辩时能不能讲出深度。很多毕设写到这一步就露怯了,因为实现上只会拿正则去匹配 payload 特征,漏报和误报都严重。这套工具带的做法值得一提:用“状态码变化 + 返回体错误特征”双信号判断 SQL 注入,用“标记字符串探路 + payload 反射验证”判断 XSS,下面拆开讲。
3.1 SQL 注入检测:双信号判断,给 payload 分层次
SQL 注入的检测原理说起来很直白:往参数里塞破坏 SQL 语句结构的 payload,然后看数据库和 Web 应用的反应。但反应的类型很复杂,有的报错、有的返回状态码 500、有的页面白屏、有的执行时间变长。如果只靠一两种信号,误报率会很高。这套工具的检测模块用了双信号判断,同时看 HTTP 状态码变化和返回体里是否出现数据库错误特征。
# modules/sql_detector.py import re import requests # 按探测强度分层:先轻后重,避免上来就用时间盲注把目标拖垮 SQLI_PAYLOADS = [ "'", # 第一层:探测单引号闭合是否被破坏 "' OR '1'='1", # 第二层:恒真条件,判断是否存在注入点 "' OR 1=1-- -", # 第三层:注释掉尾部语句,确认注入成立 "' AND SLEEP(3)-- -", # 第四层:时间盲注验证,靠响应延迟判断 ] ERROR_PATTERNS = [ r"SQL syntax", # MySQL 报错 r"Unclosed quotation mark", # SQL Server 报错 r"mysql_fetch", # PHP 老接口 r"ORA-[0-9]{4,5}", # Oracle 报错 r"SQLite3::", # SQLite 报错 r"Microsoft OLE DB", # Access 报错 ] def test_sqli(base_url, param, payload, session, timeout=5): """对单个参数、单个 payload 执行一次 SQL 注入探测""" normal_params = {param: "1"} test_params = {param: payload} # 先请求正常值作为基线,再请求带 payload 的地址 normal_resp = session.get(base_url, params=normal_params, timeout=timeout) test_resp = session.get(base_url, params=test_params, timeout=timeout) # 信号一:状态码差异 if test_resp.status_code != normal_resp.status_code: # 信号二:返回体中匹配数据库错误特征 if test_resp.text and any(re.search(p, test_resp.text) for p in ERROR_PATTERNS): return True return False def scan_target(base_url, param_list): """对目标 URL 的所有参数逐一执行 SQL 注入检测""" session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", }) for param in param_list: for payload in SQLI_PAYLOADS: try: if test_sqli(base_url, param, payload, session): print(f"[+] SQL 注入发现: 参数 {param}, payload: {payload}") break except requests.RequestException: # 单次请求异常不影响整体扫描,跳过继续 continue这段代码里有几个位置需要仔细解释。第一,请求参数用的是 params 而不是自己拼字符串,这个区别非常关键——requests 的 params 会自动做 URL 编码,payload 里的单引号、空格、反斜杠交给库处理,如果自己拼,碰到特殊字符就要手动 quote 一遍,漏一次就白搭一个 payload。第二,payload 分层的思想体现在列表顺序上:第一层是裸单引号,只用于探测“SQL 结构是否被打乱”,很多站点对裸单引号会直接 500;第三层才上恒真条件,确认注入成立;时间盲注放在最后,因为它消耗目标资源,不能一上来就用于所有参数。第三,ERROR_PATTERNS 里的正则其实覆盖了多种数据库,答辩时如果被问“你的工具支持哪些数据库”,直接对着这个列表念就行。
超时参数 timeout=5 是给检测模块单独设的,比端口扫描高是因为 SQL 注入请求里包含 SLEEP(3) 这样的拖延型 payload,超时太短会直接中断响应读取,时间盲注信号就丢了。requests.Session 只创建一次并复用的原因以实际场景再确认一遍:Session 会自动保持 TCP 连接,还会带上前一次请求拿到的 Cookie,保持登录态的同时提升扫描速度,也能降低被 WAF 封 IP 的风险。
3.2 XSS 检测:先探反射点,再验证 payload,过滤转义干扰
XSS 的检测思路和 SQL 注入完全不同。SQL 注入看的是后端数据库的报错,XSS 看的是前端页面的响应内容——你提交进去的 payload 是否原样反射回了响应体里。这里的难点有两个:一是怎么区分“反射了”和“没反射”,二是怎么处理 HTML 实体编码造成的误判。这套工具的 XSS 检测模块用了一个非常实用的两阶段策略。
# modules/xss_detector.py import requests XSS_PAYLOADS = [ "<script>alert(1)</script>", "<img src=x onerror=alert(1)>", "\"><svg/onload=alert(1)>", ] def detect_xss(base_url, param, session, timeout=5): """对单个参数执行 XSS 检测,返回可触发的 payload 或 None""" # 阶段一:用唯一标记字符串探测反射点 marker = "xss_marker_2024" probe_params = {param: marker} probe_resp = session.get(base_url, params=probe_params, timeout=timeout) if marker not in probe_resp.text: return None # 没反射,直接跳过,省流量 # 阶段二:逐个 payload 做验证 for payload in XSS_PAYLOADS: test_params = {param: payload} resp = session.get(base_url, params=test_params, timeout=timeout) # 先看 payload 是否原样出现在响应中 if payload not in resp.text: continue # 再看是否被 HTML 实体转义处理过: # 如果页面把 < 转成了 <,payload 虽然反射了但不可执行 if "<script>" in resp.text or "<img" in resp.text: return payload return None阶段一的做法是很多扫描器的通用套路:用一串几乎不可能出现在正常页面里的标记字符串(xss_marker_2024)做一次探测请求,响应里有它,说明这里存在反射点;没有就直接跳过这个参数,不浪费时间。好处很明显——XSS payload 往往有十个以上,如果不先确认反射点,每个 payload 都要发一次完整请求,流量和耗时都是成倍增加。
阶段二的转义检查是这个模块里最容易被忽略但最值钱的部分。很多目标站点的过滤方式是 HTML 实体编码,也就是把 < 转成 <、> 转成 >。这种情况下 payload 确实反射回了响应体里,但它被浏览器当作文本渲染了,不会执行,不能算漏洞。如果只拿“payload in response.text”做判断,这种场景就会大量误报。加一个“响应里是否含有原始标签”的判断,就能把实体编码干扰滤掉。这里有一个边界要承认:如果过滤方式是去掉 script 关键字但保留尖括号(比如把