拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

渗透字典分类实战:框架、备份与配置文件泄露检测

渗透字典分类实战:框架、备份与配置文件泄露检测 简介一套面向渗透测试的实用字典合集覆盖目录扫描、框架信息泄露、备份文件泄露、配置文件爆破等常见场景可服务于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/invokefunctionfunctioncall_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 # 框架字典探测先抓指纹按框架挂载词条并批量请求 TARGEThttps://example.com # 替换为授权目标 UAMozilla/5.0 (Windows NT 10.0; Win64; x64) DictProbe/1.0 TIMEOUT8 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 FRAMEWORKspring elif echo $FP | grep -qi x-powered-by.*thinkphp; then FRAMEWORKthinkphp elif echo $FP | grep -q csrftoken; then FRAMEWORKdjango else FRAMEWORKcommon 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 idapp响应体包含这个特征就丢弃。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 2zip 文件头是PK 03 04gzip 是1F 8B7z 是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, timeout10) 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] bPK: return zip if head[:2] b\x1f\x8b: return gzip if bMySQL dump in head or bCREATE TABLE in head: return sql if re.search(rb(?m)^[A-Z_]{2,}.$, head): return env if bactiveProfiles in head or bpropertySources in head: return actuator if bopenapi in head or bswagger in head: return swagger if json in ct and broutes 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等号键值对匹配 .envJSON 键匹配 actuator这些都比分页大小和状态码可靠。阈值上单次读取限制在 64KB 左右够看明文配置和文件头又不会把大备份全拖回来。6.2 验证结果回填字典的维护周期验证脚本跑完会得到一批带分类的命中 URL。分类标签本身就有价值例如某次测试里 swagger 标签命中五个不同域名那 /swagger-ui.html 这个词条置信度极高可以直接提权重。某个 .env 路径只在单一目标命中先放观察区不要立刻进主字典。我吃过一次亏把带年份的备份词条直接写死进主字典结果第二年它全是 404还占掉了扫描额度。后来改成生成器按目标上线时间推算日期段不再维护固定日期列表。每次项目结束后从验证结果里挑 3 到 5 个非目标特有的词条入库同一个词条在五个以上不同目标都命中才升成高权重。这套流程跑两个季度之后字典的命中率会比刚开始高一大截误报也会明显变少。希望帮到你。本文还有配套的精品资源点击获取
返回列表