☰
渗透字典分类实战:框架、备份与配置文件泄露检测
2026/9/28 4:52:37 网站建设 项目流程

简介:一套面向渗透测试的实用字典合集,覆盖目录扫描、框架信息泄露、备份文件泄露、配置文件爆破等常见场景,可服务于Web安全测试人员、漏洞挖掘者及网络运维人员的日常评估工作。压缩包内含204个文件,大小约32.48MB;主体为171个txt字典文件,另搭配13个Python脚本、6个Markdown说明、3个CSV账号口令表、3个Excel清单及少量配置与工具类文件,便于直接导入Burp Suite或配合脚本进行批量测试。字典体系较完整,包含目录字典、子域名字典、备份文件名字典、弱口令字典、用户名字典等;其中备份文件字典支持将名称与后缀组合访问,适合挖掘信息泄露类漏洞。目前已有51人学习下载。除现成字典外,还附带少量辅助脚本与口令表,可供进一步整理或二次开发,是一份轻量但实用的渗透测试词库储备。

1. 先搞清这套渗透字典解决什么问题

做授权范围内的信息泄露检测时,第一步往往不是上扫描器,而是选字典。自己拼过的渗透字典通常分成三块:框架信息泄露、备份文件泄露、配置文件泄露。这三类泄露点的命中规律完全不同,硬塞进一个通用大列表里,结果就是框架路径扫不全、备份命名猜不中、配置文件误报一堆。这套字典包的定位,就是按泄露来源把词条分开组织,用指纹约束框架路径、用命名规律生成备份词条、用键值对特征匹配配置文件,让测试人员用更少请求摸到更高价值的泄露点。适合甲方安全自查、乙方授权测试和 SRE 做资产暴露面梳理的人用。

2. 为什么信息泄露检测需要分类字典:命名规律与词条结构

2.1 三类泄露来源不同,混在一起必然互相干扰

框架信息泄露来自框架自带的调试端点、管理接口和默认路由。Spring Boot 的 actuator、Django 的 admin、FastAPI 的 docs,这些路径是框架作者写死的,只要版本和配置吻合,路径基本固定,命中率极高。备份文件泄露来自开发者手动打包上传,文件名、压缩格式、放的目录都凭当天心情,属于典型的命名猜测问题。配置文件泄露来自部署失误、编辑器残留、版本控制目录暴露,路径规律介于前两者之间,但验证特征非常明确。

把这三类塞进同一个几十万行的字典,至少有三个副作用。第一,非目标框架的固定路径占了大量请求,例如目标是 PHP 站,却把 Spring 的 /actuator 列表也跑一遍,全部 404,浪费时间和带宽。第二,不同泄露类型的验证方式不同,actuator 要检查 JSON 里的 propertySources,.env 要检查等号键值对,zip 要看二进制文件头,混在一起没法挂统一验证器,只能靠状态码猜,误报率直接拉满。第三,大列表命中率低,请求量大,更容易触发 WAF 限速。拆开维护之后,目标指纹匹配哪类就加载哪类词条,请求量和误报率都能压住。

2.2 词条结构:路径、方法、指纹约束与预期特征

一套能长期维护的字典,不应该只是“每行一个路径”的裸列表。裸路径文件可以直接导入 dirsearch、ffuf 这类工具,但它丢掉了“这个路径什么时候该测、命中后怎么确认”的信息。我一般会维护两套格式:一套是纯路径文本,用于快速导入扫描器;另一套是带字段的结构化表,用于自研校验脚本。

字段名 | 示例 | 说明 path | /actuator/env | 相对根路径,不带协议和主机名,不带查询参数 method | GET | 大多数泄露路径是 GET,个别后台端点需要 POST fingerprint | spring-boot | 限定目标指纹,为空表示所有目标都测 expect | body:activeProfiles | 命中后的响应特征,用 body/header/status 三种前缀区分 weight | 5 | 扫描优先级,权重大的先请求

纯路径文件只保留 path 字段,靠目录名区分用途,例如 dict/framework/spring.txt、dict/backup/high.txt、dict/config/env.txt。结构化表则用于需要精细控制的脚本,尤其是 expect 字段能大幅降低误报。比如 /actuator/env 即使返回 200,响应体里没有 activeProfiles 或 propertySources,那很可能是前端路由回退或者伪 200,不能算数。

2.3 类间去重与低置信度词条分流

分类之后必须做跨类去重,否则同一路径会重复请求。例如 Laravel 的 /.env 既可以放进框架字典也可以放进配置文件字典,我通常约定:配置文件字典收录所有以 .env 开头的词条,框架字典只保留框架特有的端点,同一路径只保留一份。规范化处理时,统一小写入库、去掉尾斜杠、把反斜杠转成正斜杠。但要注意 Linux 目标区分大小写,所以对大小写敏感场景,生成器会额外保留原大小写变体,不能一刀切全转小写。

低置信度词条要单独分流,不能混进主列表。备份文件里带具体日期和随机数字的词条,命中率很低但数量巨大,每天全量跑会让扫描时间翻好几倍。我通常把字典分成 main 和 extended 两层:main 只放固定路径和最高概率命名,不带日期或只带当前月份,每次任务都跑;extended 放带历史日期、带环境标记、带随机成分的词条,只在每月巡检或者对特定目标深挖时启用。这样既不影响日常效率,又保留了对命名变态场景的覆盖能力。

3. 框架信息泄露字典:按指纹挂载的高价值路径

3.1 框架指纹如何决定词条加载

同一个 /env 路径,在 Spring Boot 下面是环境变量泄露端点,在 Nginx 静态站下面是 404。所以框架字典的加载不能靠“全量硬扫”,而是先抓目标指纹,再决定启用哪份词条列表。指纹来源主要是三个地方:响应头、首页 HTML、特定路径的响应特征。

框架 | 指纹特征 | 启用词条 Spring Boot | 默认错误页 Whitelabel Error Page、X-Application-Context 头 | spring.txt ThinkPHP | x-powered-by: ThinkPHP,错误页含 think\ | thinkphp.txt Django | Cookie 里的 csrftoken,/admin/login/ 返回登录表单 | django.txt Laravel | 默认首页 meta 含 Laravel,/storage/logs/laravel.log | laravel.txt FastAPI / Swagger | /docs、/openapi.json 可访问 | swagger.txt Ruby on Rails | /rails/info/routes 返回路由表 | rails.txt Tomcat | Server: Apache-Coyote,/manager/html 返回 401 | tomcat.txt 无框架特征 | Server: nginx 或 Apache,无框架 Cookie | common.txt

抓指纹只需要一两个请求,在扫描前做一次即可。常见做法是直接请求目标根路径,保存响应头和前几 KB 响应体,用 grep 匹配上面的特征。匹配不到就落到 common.txt,只跑通用路径,避免大量无效请求。

3.2 基础词条清单:从主流框架提炼的固定端点

框架字典的词条不一定越多越好,关键是每个词条都有明确的预期特征。下表是按实战价值排过序的基础词条,覆盖了信息泄露检测里最常见的场景。

框架 | 路径 | 预期响应特征 | 价值点 Spring Boot | /actuator/env | JSON 含 propertySources | 环境变量、数据库配置、密钥 Spring Boot | /actuator/heapdump | 二进制文件,体积大 | 堆内存里的密码和 Token Spring Boot | /trace | JSON 含请求头列表 | 会话信息和参数 ThinkPHP | /index.php?s=/think/app/invokefunction&function=call_user_func_array | 调试报错页 | 老版本 debug 开启时可 RCE Laravel | /.env | APP_KEY=、DB_HOST= 键值对 | 应用密钥和数据库账号 Laravel | /storage/logs/laravel.log | production.ERROR 堆栈 | SQL 语句和出参 Django | /admin/login/ | 表单含 CSRF | 管理入口暴露 Django | /static/ | Index of /static/ | 目录列表信息泄露 FastAPI | /docs | Swagger UI 页面 | 接口文档 Rails | /rails/info/routes | 路由表 | 接口路径枚举 Tomcat | /manager/html | 401 Basic 认证 | 管理台暴露

这里需要提醒两点。第一,/actuator/heapdump 在未授权时可能输出几十 MB 的二进制文件,扫描器要限制响应体积,不能整包下载,本文第 5 章会讲具体做法。第二,Rails 4 之后 /rails/info/routes 默认只允许本地 IP 访问,如果返回 302 或 403 不算命中,不要死磕。

3.3 一个指纹约束的批量探测脚本

下面这段 bash 脚本的思路是:先请求目标根路径,把指纹存下来,再根据指纹选择框架字典,逐条请求并输出候选命中。它不追求高并发,而是适合小目标精准验证。

#!/usr/bin/env bash # 框架字典探测:先抓指纹,按框架挂载词条并批量请求 TARGET="https://example.com" # 替换为授权目标 UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) DictProbe/1.0" TIMEOUT=8 DICT_DIR="./dict/framework" # 抓首页,保存响应体里的指纹 curl -sk -A "$UA" --max-time "$TIMEOUT" "$TARGET" -o /tmp/_fp.html FP="$(head -c 2000 /tmp/_fp.html)" if echo "$FP" | grep -q "Whitelabel Error Page"; then FRAMEWORK="spring" elif echo "$FP" | grep -qi "x-powered-by.*thinkphp"; then FRAMEWORK="thinkphp" elif echo "$FP" | grep -q "csrftoken"; then FRAMEWORK="django" else FRAMEWORK="common" fi DICT="${DICT_DIR}/${FRAMEWORK}.txt" [ -f "$DICT" ] || DICT="${DICT_DIR}/common.txt" # 逐行读取词条,发起 GET 请求 while IFS= read -r path; do [ -z "$path" ] && continue path="${path%%$'\r'}" code=$(curl -sk -A "$UA" --max-time "$TIMEOUT" \ -o /tmp/_body.txt -w "%{http_code}" \ "$TARGET$path") size=$(wc -c < /tmp/_body.txt) # 200 且响应体不为空,先输出,后续再人工确认 if [ "$code" = "200" ] && [ "$size" -gt 0 ]; then echo "$code $size $path" fi done < "$DICT"

这段脚本有几个关键点。一是IFS= read -r保留词条里的空格和特殊字符,避免路径被拆错;path="${path%%$'\r'}"处理 Windows 换行符,防止词条末尾带 \r 导致 404。二是这里刻意不加-L,不跟随重定向,因为很多站会把不存在的路径 302 到首页或登录页,跟随之后状态码变成 200,容易误判。三是-o /tmp/_body.txt把响应体落盘而不是打进管道,避免大响应占内存。

提升效率时,不推荐在这段脚本里做并发,直接用 ffuf 重放同一份字典更省事。命令形如ffuf -u https://target/FUZZ -w dict/framework/spring.txt -mc 200,301,302 -fs 1234,其中-fs 1234是过滤掉首页大小,很多 SPA 应用对所有路径返回同样大小的 index.html,这个过滤能去掉大半误报。

4. 备份文件泄露与配置文件泄露字典:命名穷举与组合生成

4.1 备份文件的命名规律与优先级

备份文件泄露是三类里最依赖“猜”的。开发者手动打包时,文件名通常逃不出几个习惯:直接用域名或项目名、加 backup 或 bak 关键字、加日期、扔在根目录或 backup 目录下。把高概率命名放在前面,低概率放后面,能显著提高扫描效率。

优先级 | 命名模式 | 示例 高 | 域名/项目名 + 扩展名 | example.com.zip、project.tar.gz 高 | 项目名 + backup + 扩展名 | site_backup.zip、web_bak.tar.gz 中 | 项目名 + 年月 | backup_202401.zip、bak_2024.tar.gz 中 | 项目名 + 精确日期 | db_20240101.sql、site_backup_202401.sql 低 | 带随机数字或环境标识 | web_8080.zip、www_root.7z

扩展名的优先级同样有讲究。zip 和 tar.gz 最常见,sql 排第二,rar 和 7z 次之,bak、old、gz、temp 这类的存在感也不低。生成词条时,扩展名列表要按这个顺序排列,生成的词条也带上优先级标签,方便扫描器分轮次执行。

4.2 配置文件泄露的高价值路径清单

配置文件泄露的路径规律比备份文件更清晰,高价值目标集中在根目录点文件、应用配置目录、版本控制目录三块。下面这份清单按通用优先级排列。

类别 | 路径 | 预期特征 根目录点文件 | /.env、/.env.local、/.env.production、/.env.bak | 等号键值对 应用配置 | /config.php.bak、/config.inc.php.old、/wp-config.php、/configuration.php、/settings.py | PHP/脚本配置 框架配置 | /config/app.php、/application.yml、/application.properties、/database.yml | YAML/属性文件 版本控制 | /.git/config、/.git/HEAD、/.svn/entries | ini 结构或 XML 中间件 | /server-status、/manager/html | Apache/Tomcat 后台

配置文件的 .bak .old 变体要单独生成,不能只测原路径。很多程序员习惯用cp config.php config.php.bak再改文件,原文件权限正常,备份文件却留在 web 目录里能直接访问。这类变体词条数量不多,但命中一个就是完整配置,价值很高。

4.3 用 Python 组合生成备份词条

备份文件纯靠手工写词条永远列不完,常见做法是写一个生成器,把项目名、分隔符、日期、扩展名、目录前缀做笛卡尔积。下面这段脚本生成的是候选路径,输出后按优先级拆成高置信和延后两批。

# -*- coding: utf-8 -*- """备份文件词条生成器:项目名 + 分隔符 + 日期 + 扩展名 + 目录前缀""" import itertools project_names = ["site", "web", "www", "backend", "api", "app", "config", "database", "db", "data"] exts = [".zip", ".tar.gz", ".sql", ".rar", ".7z", ".bak", ".old", ".gz", ".sql.gz"] dates = ["", "2024", "202401", "20240101", "20240101_1200", "20231231", "20230414"] seps = ["", "_", "-", ".", "__"] prefixes = ["/", "/backup/", "/bak/", "/old/", "/temp/", "/data/", "/database/"] lines = [] for pfx, name, sep, d, ext in itertools.product(prefixes, project_names, seps, dates, exts): candidate = f"{pfx}{name}{sep}{d}{ext}" lines.append(candidate) if name not in ("config", "db", "database"): lines.append(f"{pfx}{name}{sep}backup{sep}{d}{ext}") seen = set() for line in lines: if line not in seen: seen.add(line) print(line)

这段代码的核心参数有三个:dates 列表、prefixes 列表、backup 变体开关。dates 里放的目标日期要结合具体资产调整,我通常看证书起始时间或域名备案时间,从上线时间往后取最近 18 个月的第一天、月中、月末几个点,而不是把 2015 年以来的日期全塞进去,那样生成量会爆炸。prefixes 里/backup/、/bak/这些目录是人工排查时最高频出现的,放在后面会让生成词条更贴近真实站点的目录结构。

这个脚本不加控制直接跑,大约会生成三万条。三万条全量跑一轮,在小目标上也要半小时以上,而且备份文件响应体大,带宽和 WAF 都有压力。所以生成后要立刻分级:不带日期的词条是高概率,单独存 high.txt 先跑;带日期但日期在最近三个月的存 mid.txt 第二趟;历史日期存 low.txt 放低频任务。分级用 grep 就能做,grep -vE "202[0-9]{4,}" high.txt之类的过滤规则按自己习惯写就行。

5. 避坑:字典扫描常见的五个坑

字典看着是简单的文本文件,真正跑起来才会发现坑全在响应判断和扫描策略上。下面五条是实战里反复出现的踩坑记录,按现象、原因、解决分开写。

5.1 并发拉满,结果被封 IP

现象:字典只有两三千行,开 8 个线程跑,跑到第 10 分钟开始全是 403 和 429,再接下去直接超时。

原因:目标有 WAF 或全站限速,短时间密集请求触发了封禁策略。备份词条里大响应也多,每个请求都要传几十 KB 响应体,带宽占用很快把目标连接数打满。

解决:限制请求速率,单目标控制在每秒 2 到 5 个请求。ffuf 用-rate 3,自研脚本就在循环里加sleep 0.3。请求头里的 User-Agent 要随机化,但不要伪装成知名搜索引擎,合规测试需要让目标能认出你的来源。更重要的是,扫描前先确认测试范围,看到 403 响应体里带 WAF 厂商标识,直接降速,别硬顶。

5.2 状态码 200 全是前端壳

现象:一套 Vue 或 React 打包的站点,后端把所有不存在的路径都 fallback 到 index.html,字典跑完“命中”几百条,每条状态码都是 200,长度差异不到几十字节。

原因:这类 SPA 应用在 Nginx 里配置了 try_files,任何路径都返回同一个 HTML。扫描器只看状态码和长度,自然全判命中。

解决:先记录首页响应长度,再用-fs 首页长度过滤,ffuf 有现成参数。自研脚本则对比响应体和首页指纹,比如截取响应前 500 字节做哈希,和首页哈希相同就跳过。如果 SPA 的 index.html 会动态注入内容导致长度不稳定,就抓一个固定字符串特征,比如<div id="app">,响应体包含这个特征就丢弃。

5.3 备份文件太大,直接把验证流程拖死

现象:/backup.zip 命中且返回 200,脚本自动下载,文件 2GB,跑着跑着内存吃满,网络超时,验证进程直接崩溃。

原因:只判断了状态码,没有在下载前控制文件体积。

解决:先发 HEAD 请求看 Content-Length,超过 50MB 的只记录路径不下载正文。需要人工复核时用 Range 只取前 1KB,验证文件头特征就够了。

# 只取备份文件前 1024 字节,验证文件头 curl -sk -r 0-1023 -A "$UA" -o /tmp/_head.bin "https://target/backup.zip" xxd /tmp/_head.bin | head -n 2

zip 文件头是PK 03 04,gzip 是1F 8B,7z 是37 7A BC AF。用 file 命令也能直接识别,但 file 会把空文件和 HTML 误认成 ASCII text,所以要配合响应头里的 Content-Type 一起看。这个坑的根源是“命中了不等于能下载”,记录在案即可,不要当场全量拖回来。

5.4 HEAD 请求探测导致大面积漏报

现象:用 HEAD 请求方式扫 .env 和 .git/config,返回 405,于是这些路径全部漏报;换浏览器直接访问却能打开。

原因:部分后端框架没有实现 HEAD 路由,Django 默认也会对 HEAD 做处理,但有些自定义中间件会直接拒绝。还有的 WAF 对 HEAD 单独拦截,对 GET 放行。

解决:一律用 GET 请求探测,响应体在客户端用head -c截断。这样即使目标忽略 Range 头返回全量内容,本地也只读取前几 KB,不会拖垮内存。日志层面会产生一些连接中断记录,对授权测试影响不大。重点结论:HEAD 只适合做 Content-Length 预检,不适合做命中判断。

5.5 字典里的绝对路径和子目录部署冲突

现象:目标应用部署在 /webapp/ 子目录下,字典词条写成 /admin、/.env 这种绝对路径,全部 404,但真实路径是 /webapp/admin 和 /webapp/.env。

原因:词条本身是绝对路径,没有考虑目标站点的子目录前缀。很多 PHP 项目会把入口放到 /public 或 /web 下,Nginx root 指到子目录,根路径下的 .env 自然不存在。

解决:扫描前先请求根路径,看响应头里的 Location 或页面里的 baseUrl,确认是否存在子目录前缀。有前缀就批量生成一份带前缀的字典副本。

# 为字典追加子目录前缀 BASE_PATH="/webapp" sed "s#^/#${BASE_PATH}#" dict/config/env.txt >> dict/config/_with_base.txt

注意不要盲目生成所有可能的前缀,先确认哪个前缀真实存在。常见做法是请求 /webapp/、/public/、/www/ 几个候选路径,看哪一个返回 200 或 302,再决定用哪个前缀。这步放在指纹识别之后,两个请求就能完成,不费时间。

6. 进阶用法:把字典用成半自动验证流程

6.1 命中结果按内容特征自动验证

状态码和长度只能说明“路径存在”,不能说明“内容泄露”。更可靠的做法是加一个验证脚本,按响应特征给命中结果归类。判断依据不是状态码,而是内容本身。

# -*- coding: utf-8 -*- """候选命中验证:按响应内容特征决定入库或丢弃""" import re, sys, urllib.request cand_file = sys.argv[1] if len(sys.argv) > 1 else "candidates.txt" out_file = "verified.txt" def check(url): req = urllib.request.Request(url, headers={"User-Agent": "DictVerify/1.0"}) with urllib.request.urlopen(req, timeout=10) as r: ct = r.headers.get("Content-Type", "") head = r.read(2048) body = head try: body += r.read(65536) except OSError: pass text = body.decode("utf-8", "ignore") if head[:2] == b"PK": return "zip" if head[:2] == b"\x1f\x8b": return "gzip" if b"MySQL dump" in head or b"CREATE TABLE" in head: return "sql" if re.search(rb"(?m)^[A-Z_]{2,}=.+$", head): return "env" if b"activeProfiles" in head or b"propertySources" in head: return "actuator" if b"openapi" in head or b"swagger" in head: return "swagger" if "json" in ct and b"routes" in head: return "rails" return None with open(cand_file) as f, open(out_file, "a") as out: for line in f: url = line.strip() try: kind = check(url) except Exception as e: print("ERR", url, e) continue if kind: out.write(f"{kind}\t{url}\n") print("HIT", kind, url) else: print("NO ", url)

脚本的核心逻辑是把“响应体长什么样”作为判断依据。PK 头直接锁定 zip,等号键值对匹配 .env,JSON 键匹配 actuator,这些都比分页大小和状态码可靠。阈值上,单次读取限制在 64KB 左右,够看明文配置和文件头,又不会把大备份全拖回来。

6.2 验证结果回填字典的维护周期

验证脚本跑完,会得到一批带分类的命中 URL。分类标签本身就有价值,例如某次测试里 swagger 标签命中五个不同域名,那 /swagger-ui.html 这个词条置信度极高,可以直接提权重。某个 .env 路径只在单一目标命中,先放观察区,不要立刻进主字典。

我吃过一次亏:把带年份的备份词条直接写死进主字典,结果第二年它全是 404,还占掉了扫描额度。后来改成生成器按目标上线时间推算日期段,不再维护固定日期列表。每次项目结束后,从验证结果里挑 3 到 5 个非目标特有的词条入库,同一个词条在五个以上不同目标都命中,才升成高权重。这套流程跑两个季度之后,字典的命中率会比刚开始高一大截,误报也会明显变少。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询