第 19 章 自动化与漏洞挖掘思路

2 阅读15分钟

上一章你学会了"读系统",这一章学"规模化地读"。 一个测试项目可能有几百个域名、几千个接口、几万个参数—— 手工点不过来,必须把"逻辑"交给自动化,把"判断"留给自己。 核心心法:自动化放大你的能力,但绝不代替你的判断。


19.0 开篇:手工 vs 自动化,差在哪里

一个真实场景:某公司授权你测试它全部资产。

手工方法:
  你打开浏览器,一个个点……第一天测完了主站。
  老板问:"子域名呢?" 你说:"我还没找到。"

自动化方法:
  ① 用子域枚举工具,10 分钟拿到 300 个子域名
  ② 用爬虫 + 端点提取,拿到 5000 个 URL
  ③ 用 nuclei 批量扫,筛出 50"可疑点"
  ④ 你只在这 50 个上手工深挖

差距不在"技术",在"规模"。

🔥 自动化的正确位置:

自动化负责:发现(Discovery)+ 筛选(Triage)—— 把"候选"从一万个缩到十个
人负责:    判断(Analysis)  + 利用(Exploit)  —— 在那十个里找到真的漏洞

❌ 错误认知:自动化能"直接找到漏洞"
✅ 正确认知:自动化能"把值得看的东西挑出来,让你少看 99% 的噪音"

19.1 资产发现:先知道"打什么"

一切自动化的起点,是"资产清单"。 你不可能测试你不知道存在的系统。

19.1.1 子域名枚举

一个主域名背后,往往藏着几十上百个子域名——它们是最容易被忽略的攻击面。

方法一:证书透明度日志(被动,最推荐)

# crt.sh:所有公开 TLS 证书都记录在证书透明度日志里
# 直接访问:
https://crt.sh/?q=%25.example.com

# 或用命令行取
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u

💡 为什么 ctsh 最有用完全被动——你只是在查别人公开的信息,目标完全感知不到

方法二:字典爆破(主动)

# ffuf 挖子域
ffuf -w subdomains.txt -u https://FUZZ.example.com -mc 200,301,302,403

# 或 gobuster dns
gobuster dns -d example.com -w subdomains.txt

方法三:其他被动源

□ DNS 数据集:SecurityTrails、VirusTotal、PassiveTotal
□ 搜索引擎:site:example.com
□ GitHub 代码搜索:搜 "example.com"(可能泄露内部域名)
□ 第三方服务:Shodan、Censys(按证书/组织搜)

子域枚举的产出:一份"资产列表",后面所有测试都基于它。

19.1.2 域名 → IP → 端口

# 解析 IP
dig example.com +short

# 端口扫描(仅在授权范围内!)
nmap -sV -p- target

# 用 masscan 快速扫大量主机(更快)
masscan -p1-65535 --rate=1000 192.168.1.0/24

19.1.3 技术栈指纹识别

知道目标是"什么技术做的",才能"针对性地测"。

# whatweb:识别 CMS、框架、服务器
whatweb https://example.com

# wappalyzer:浏览器插件,或命令行版
# 从响应头、HTML、JS、favicon 综合判断

# 单独看响应头
curl -sI https://example.com

识别什么

□ Web 服务器(Nginx/Apache/IIS)
□ 语言/框架(Spring/PHP/Django/Node)
□ CMS(WordPress/ThinkPHP/Shiro)
□ CDN / WAF
□ 前端框架(从 JS 文件特征)
□ 用到的第三方服务(从资源域名判断)

19.1.4 一个"资产发现流水线"

① 子域枚举(crt.sh + 字典爆破 + 被动源)→ 得到一堆域名
② 存活探测(批量 HTTP 请求,过滤掉打不开的)
③ 指纹识别(whatweb / wappalyzer)→ 知道每台是什么
④ 端口扫描(nmap)→ 发现非 Web 服务
⑤ 汇总成"资产表格":域名 | IP | 端口 | 技术栈 | 负责人

💡 这份表格,就是你整个测试的"作战地图"。 后面所有工作,都从它出发。


19.2 接口与参数发现:找到"可测的点"

漏洞藏在"接口 + 参数"上。所以自动化的一大任务,就是"把所有接口和参数挖出来"。

19.2.1 爬虫:把"能点的"都点一遍

# katana:现代爬虫(Burp 也有类似能力)
katana -u https://example.com -d 3 -o urls.txt

# 或用 Burp:正常浏览一遍,HTTP history 里就是"爬到的"

爬虫的局限:只能爬"静态链接能到达"的页面。JS 动态渲染、需要交互的页面爬不到

💡 技巧用 Burp 的"手动浏览 + 代理记录"往往比自动爬虫更全——因为你会点击 JS 生成的按钮

19.2.2 从 JS 文件里挖接口(现代应用必做)

现代 SPA 的接口,往往藏在 JS 里,而不是 HTML 链接里。

# 下载所有 JS 文件
katana -u https://example.com -jc        # -jc 表示爬 JS
# 或用浏览器把 JS 文件都下载下来

# 在 JS 里搜"像 URL 的字符串"
# 搜索形如 /api/xxx 的路径片段
grep -oE '/[a-zA-Z0-9_/-]*api[a-zA-Z0-9_/-]*' app.js | sort -u

# 搜索完整的 http(s) 链接
grep -oE 'https?://[a-zA-Z0-9._/-]+' app.js | sort -u

💡 LinkFinder 之类的工具专门做这件事——从 JS 里提取 API 路径。

19.2.3 目录 / 文件爆破

# ffuf 目录爆破(过滤掉 404)
ffuf -w dirs.txt -u https://example.com/FUZZ -fc 404

# 带后缀
ffuf -w dirs.txt -u https://example.com/FUZZ -e .php,.html,.bak,.zip,.txt

# 用"递归":对发现的目录继续爆

19.2.4 参数发现(容易被忽略)

同一个接口,多带一个隐藏参数,行为可能完全不同。

# arjun:专门发现隐藏参数
arjun -u https://example.com/api/user

# 或用 ffuf 爆破参数名
ffuf -w params.txt -u "https://example.com/api?id=1&FUZZ=1" -fs 0

# 从 JS 里找(参数名常写在 JS 里)
# 搜索 "params" / "query" 等关键字附近的字段

💡 隐藏参数的经典案例:?admin=true、?debug=1、?test=1、?role=admin——开发者留下的"调试开关",往往就是漏洞入口

19.2.5 历史 URL(被遗忘的接口)

□ Wayback Machine:web.archive.org —— 历史版本可能暴露已删除的接口
□ 各种"历史 URL"数据集
□ 搜索引擎缓存

为什么重要"删掉的接口"往往比"现存的接口"更脆弱——因为开发者可能忘了它还在,或其防护没跟上。

19.2.6 一个"接口清单"的构成

最终产出:一份包含所有"可测点"的清单
  每一行:方法 | URL | 参数 | 来源(爬虫/JS/猜/历史)
  去重后往往有几千到几万条
  → 交给下一步(扫描 / 批量化测试)

19.3 把"经验"变成"扫描规则":nuclei 模板

nuclei 的核心价值:把"你知道怎么检测某个漏洞"这件事,写成一份可复用的 YAML 模板。

19.3.1 一个最简单的模板

id: exposed-git-config

info:
  name: Exposed .git/config
  author: me
  severity: medium
  description: 网站的 .git 目录可被访问,可能泄露源码

requests:
  - method: GET
    path:
      - "{{BaseURL}}/.git/config"
    matchers:
      - type: word
        words:
          - "[core]"           # .git/config 里必然出现的字符串
        part: body

结构解析

id       模板的唯一标识
info     元信息(名称、作者、危害等级、描述)
requests 怎么发请求(method / path / headers / body)
matchers 怎么判断"命中了"(关键字 / 正则 / 状态码 / 响应长度)

19.3.2 几种常用的 matcher

matchers:
  # 状态码
  - type: status
    status: [200]

  # 关键字(body 里出现某段文字)
  - type: word
    words:
      - "root:x:0:0"
    part: body
    condition: and          # 或者 or

  # 正则
  - type: regex
    regex:
      - "uid=\d+"

  # 二进制 / 按响应长度差异
  - type: dsl
    dsl:
      - "status_code == 200 && content_length > 100"

19.3.3 多请求模板(提取数据再用)

id: my-multi-step-check

info:
  name: 多步检测
  severity: low

requests:
  # 第一步:拿 token
  - raw:
      - |
        GET /api/token HTTP/1.1
        Host: {{Hostname}}
    extractors:
      - type: regex
        name: token
        regex:
          - '"token":"([a-f0-9]+)"'
        group: 1

  # 第二步:用 token 访问受保护接口
  - raw:
      - |
        GET /api/admin/data HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{token}}
    matchers:
      - type: word
        words: ["admin_data"]

💡 这个"提取 → 复用"的能力,让 nuclei 能表达"复杂逻辑",而不只是"发个包看响应"。

19.3.4 一个实用的例子:检测 Actuator 泄露

id: spring-actuator-env

info:
  name: Spring Boot Actuator env 暴露
  severity: high

requests:
  - method: GET
    path:
      - "{{BaseURL}}/actuator/env"
    matchers-condition: and
    matchers:
      - type: status
        status: [200]
      - type: word
        words:
          - "systemProperties"
          - "propertySources"
        condition: or
        part: body
    extractors:
      - type: regex
        name: secrets
        regex:
          - '"(password|secret|key|token)"[^}]{0,200}'

19.3.5 使用与组织模板

# 用官方模板库扫
nuclei -u https://target.com

# 用你自己的模板目录
nuclei -u https://target.com -t ./my-templates/

# 批量扫描
nuclei -l urls.txt -t ./my-templates/ -o results.txt

# 按标签筛选
nuclei -u https://target.com -tags exposure,misconfig

# 只跑高危
nuclei -u https://target.com -severity critical,high

🔥 nuclei 的真正威力,在于"模板化"这件事:

你每发现一种"可检测的模式",就把它写成一份模板。 于是,你的经验不再只服务于"这一次测试",而是沉淀成"可复用的资产"。


19.4 写自己的扫描脚本

当 nuclei 表达不了你的逻辑时,就用脚本(Python 最常用)。

19.4.1 一个基础框架

import requests

# 会话(保持 Cookie,处理登录态)
session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 ...",     # 伪装成正常浏览器
})

# 目标列表
targets = ["https://a.com", "https://b.com"]

# 测试的 payload
payloads = ["'", "\"", "../", "<script>alert(1)</script>"]

for url in targets:
    for p in payloads:
        try:
            r = session.get(url, params={"q": p}, timeout=10, verify=False)
            # 判断命中的标准(状态码 / 关键字 / 长度差异)
            if r.status_code == 500 or "SQL syntax" in r.text:
                print(f"[!] 可疑: {url} payload={p} status={r.status_code}")
        except Exception as e:
            print(f"[x] 出错: {url} {e}")

19.4.2 关键设计点

① 【并发】用 concurrent.futures / asyncio,不要在"一个个循环"
   ⚠️ 但要注意"别把目标打挂"——控制并发数

② 【基线】先发一次"正常请求",记录状态码和响应长度作为基线
   之后的异常判断,都是"和基线比"

③ 【去重】同一个漏洞别报一万遍

④ 【限速】请求间隔、失败重试、超时

⑤ 【稳健】任何异常都不能让脚本崩,要 try/except 包住

⑥ 【输出】结果存文件(JSON / CSV),方便后续处理

19.4.3 一个"批量越权测试"的脚本(思路)

import requests

# 两个账号(A 创建资源,B 尝试访问)
sess_a = requests.Session()
sess_b = requests.Session()

# 假设已登录,拿到了各自 Cookie
sess_a.cookies.set("session", "A_SESSION")
sess_b.cookies.set("session", "B_SESSION")

# A 创建资源,拿到 ID
r = sess_a.post("https://target/api/order", json={"productId": 1})
order_id = r.json()["id"]
print(f"[*] A 创建了订单 {order_id}")

# B 尝试访问 A 的资源
r = sess_b.get(f"https://target/api/order/{order_id}")
if r.status_code == 200:
    print(f"[!!!] 越权漏洞: B 能访问 A 的订单 {order_id}")
else:
    print(f"[ ] 有防护: {r.status_code}")

💡 这个"两账号对比"的思路,是越权自动化的核心。 Burp 的 Autorize 插件做的就是这件事,但自己写脚本你可以控制得更精细

19.4.4 把脚本"模块化"

不要写一个"万能大脚本",而是拆成小工具:
  discover.py    资产发现 / 子域枚举
  endpoints.py   接口 + 参数提取
  scan.py        批量扫描(调用 nuclei + 自定义逻辑)
  vuln_xxx.py    针对某个漏洞的专用脚本
  report.py      汇总结果、去重、生成报告

每个工具"只做一件事,做得好",再用管道串起来。

🔥 这是"自动化"的正确姿势:

不是"一个脚本搞定一切",而是"一组小工具,各司其职,用数据串起来"。


19.5 Google Dork:用搜索引擎挖资产

搜索引擎是"最被低估的信息收集工具"。 很多人已经"把内部信息暴露在搜索引擎里",而自己不知道。

19.5.1 常用 Dork 语法

语法含义
site:example.com限定域名
inurl:adminURL 里包含关键词
intitle:登录标题包含关键词
intext:"密码"正文包含关键词
filetype:pdf指定文件类型
ext:sql指定扩展名
-排除
竖线
""精确匹配

19.5.2 常见的"信息泄露"搜索

【找后台】
site:example.com inurl:admin
site:example.com inurl:login
site:example.com intitle:"管理"

【找敏感文件】
site:example.com filetype:sql
site:example.com filetype:env
site:example.com ext:log
site:example.com filetype:bak

【找配置/凭证】
site:example.com intext:"api_key"
site:example.com intext:"password"
site:example.com inurl:".git"

【找调试接口】
site:example.com inurl:swagger
site:example.com inurl:actuator
site:example.com inurl:phpinfo

【找目录列表】
site:example.com intitle:"Index of"

19.5.3 对"人"的 Dork(社工前置)

□ 员工邮箱格式:site:linkedin.com "example company"
□ GitHub 泄露:site:github.com "example.com" password
□ 员工技术博客(可能泄露内部架构)
□ 公开的会议资料 / 招聘 JD(可能泄露技术栈)

⚠️ 注意Dork 本身不违法,但"用 Dork 找到的东西去攻击"是违法的。 只能在授权范围内使用。

💡 对授权测试:Dork 帮你找到"目标自己都没注意到的暴露面"——这对于"评估真实暴露面"极有价值。


19.6 一条完整的自动化流水线

把前面所有工具串起来,就是一条完整的流水线。

【阶段 1:资产发现】
  子域枚举(crt.sh + ffuf + 被动源)
    → 存活探测(httpx)
      → 端口扫描(nmap)
        → 指纹识别(whatweb)
          ↓ 产出:资产清单

【阶段 2:接口发现】
  爬虫(katana)
    → JS 提取(LinkFinder)
      → 目录爆破(ffuf)
        → 参数发现(arjun)
          → 历史 URL(wayback)
            ↓ 产出:可测点清单

【阶段 3:批量扫描】
  nuclei(模板库 + 自定义模板)
    → 自定义脚本(针对特定漏洞)
      ↓ 产出:可疑点清单

【阶段 4:人工深挖】
  对"可疑点"逐个手工验证
    → 确认漏洞 → 构造 POC → 评估危害
      ↓ 产出:漏洞报告

19.6.1 用 httpx 做"存活 + 指纹"

# 批量探测哪些域名是活的,并提取标题/技术栈/状态码
cat subdomains.txt | httpx -title -tech-detect -status-code -o alive.txt

19.6.2 用一条命令串起来

# 一个简化的"子域 → 存活 → 扫描"流水线
cat subdomains.txt \
  | httpx -silent \
  | nuclei -t ./my-templates/ -silent \
  | tee results.txt

💡 Linux 管道的哲学每个工具"读取输入、输出结果",用管道串起来,就能组合出复杂的能力。

19.6.3 让流水线"可复用"

□ 把每个阶段写成独立脚本 / 命令
□ 用一份"配置文件"管理:目标列表、字典路径、并发数、输出目录
□ 用一个"主脚本"按顺序调用各阶段
□ 输出统一格式(JSON),便于后续处理
□ 关键:加日志 + 加异常处理 + 加"断点续跑"

19.7 自动化的边界与陷阱

自动化很强,但用错了会"翻车"。

19.7.1 四个陷阱

① 【过度依赖工具】
   工具报的"可能漏洞"90% 是误报或需人工确认
   → 不做人工验证就上报 = 被打脸

② 【把目标打挂】
   高并发扫描可能让目标服务不可用(尤其生产环境)
   → 严格限速;授权书里通常有"不得影响业务"的条款

③ 【触发告警 / 被封】
   激进扫描很容易被 WAF / IDS 发现并封 IP
   → 适当伪装、控制速率、必要时用代理

④ 【测试了范围外的资产】★ 最危险
   自动化"顺手"扫到了不在授权范围的域名
   → 这可能构成"越权测试",法律风险
   → 严格的"scope 白名单",脚本层面做限制

🔥 第 ④ 点最危险。 自动化脚本很容易"一不小心"扫到授权范围外的资产。一定要在脚本里做 scope 校验。 这不是技术问题,是执业底线

19.7.2 自动化"做不了"什么

□ 业务逻辑漏洞(第 16 章)—— 需要理解业务
□ 复杂的越权(需要多账号、多状态组合)
□ 需要"多步交互"的漏洞(验证码、状态机)
□ 需要"上下文理解"的漏洞(判断 IDOR 是否真的有危害)
□ 需要"创造性"的绕过(第 20 章)

💡 一句话:自动化处理"模式化"的问题,人处理"模式之外"的问题。

19.7.3 自动化检查清单

□ 有 scope 白名单,脚本层面强制
□ 有严格的速率限制
□ 有异常处理和超时
□ 有日志记录(万一出问题可追溯)
□ 结果有人工验证环节
□ 不碰"破坏性"操作(DELETE / DROP / 大量写)
□ 敏感数据(拿到的凭证 / 隐私)妥善处置,不传播
□ 测试时间 / 范围符合授权书

19.8 动手任务

任务 1:搭一条最小流水线

① 拿一个你自己搭的靶场(或授权目标)
② 用 ffuf 做目录爆破,产出结果
③ 把结果喂给 nuclei
④ 对 nuclei 报出的东西,**逐个手工验证**
⑤ 记录:哪些是真的,哪些是误报

任务 2:写一个 nuclei 模板

□ 找一个"你熟悉的、有明确特征的"检测点
   (比如某个特定框架的特定路径)
□ 写成一份 nuclei 模板
□ 在靶场上验证它"能命中"
□ 再在"不存在该漏洞的目标"上验证它"不误报"

任务 3:写一个"两账号越权"脚本

① 在靶场注册两个账号 AB
② 用 Python 写脚本:A 创建资源,B 尝试访问
③ 自动判断"是否越权"
④ 体会"自动化越权测试"的思路

任务 4:做一次"被动信息收集"

□ 对一个你**有授权**的域名,用 crt.sh 查子域
□ 用搜索引擎 Dork 查"暴露的文件"
□ 全程不用任何"主动扫描"(纯被动)
□ 记录:光靠被动收集,能发现什么?

任务 5:为你自己建一个"工具箱"

把常用的命令、脚本、模板,整理进一个目录:
  tools/
  ├── recon/        资产发现
  ├── endpoints/    接口提取
  ├── templates/    nuclei 模板
  ├── scripts/      自定义脚本
  └── wordlists/    字典
以后每次测试,直接从这里出发。

19.9 本章小结

核心结论

  1. 自动化的定位发现 + 筛选(把候选从一万缩到十),判断 + 利用留给人
  2. 资产发现是起点:子域枚举(crt.sh 被动最推荐)、端口扫描、指纹识别。
  3. 接口发现:爬虫 + 从 JS 挖接口(现代应用必做) + 目录爆破 + 参数发现 + 历史 URL。
  4. 隐藏参数(?admin=true / ?debug=1)是常见的漏洞入口。
  5. nuclei 的核心价值把"经验"写成"可复用的 YAML 模板",且有"提取→复用"能力表达复杂逻辑。
  6. 自定义脚本:控制并发、建立基线、去重、限速、异常处理、结果存文件。
  7. 越权自动化的核心:两账号对比(Burp Autorize 做的就是这件事)。
  8. Google Dork 是"最被低估的被动信息收集"手段;只能在授权范围内使用
  9. 自动化流水线:资产 → 接口 → 扫描 → 人工深挖;用 Linux 管道把工具串起来。
  10. 四个陷阱:过度依赖、把目标打挂、被封 IP、扫到范围外(法律风险最大)务必在脚本里做 scope 校验。

随身口诀

自动化只做"发现和筛选",判断永远留给自己; crt.sh 挖子域,JS 里挖接口,arjun 挖参数; nuclei 把经验变成模板,脚本把模板变成流水线; 别忘了 scope 白名单——扫到范围外就是事故。


19.10 预告:下一章我们去哪

有了资产、有了接口、有了自动化的"筛选能力",接下来就是"怎么真正打进去"。

真实的系统早就不"裸奔"了——它们前面有 WAF,代码里有各种过滤,参数有各种校验

所以,"怎么绕过"就成了实战中最重要的能力之一。而绕过不是"背 payload",而是理解一件事:

防护是"某种规则",绕过是"让它看到的东西"和"实际执行的东西"不一样。

第 20 章:绕过大全。 从"方法论"高度讲透绕过。


📌 本章金句 "自动化的价值不是'让机器替你思考',而是'让机器替你搬砖'。它能帮你把值得看的十个从一万个里挑出来,但'这十个里哪个是真的',永远需要你的脑子。 一个不懂手工测试的人写的自动化脚本,只会生产更多的噪音。"