Web未授权访问自动化扫描Skill实现"

admin 2026-10-17 09:47:55 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

Web 未授权访问自动化扫描 Skill 实现

小猴
小猴

只会看监控的实习生

2026年7月25日 08:00
广东

在小说阅读器读本章

去阅读

一、项目背景

1.1 业务场景

在企业 Web 应用(Vue/React SPA + 前后端分离)的安全评估中,未授权访问与 IDOR(不安全的直接对象引用) 是最常见、也最容易被漏测的一类问题。典型表现:

  • 前端 JS 打包后接口分散,人工从 Burp 历史里逐条重放效率低;
  • 部分接口 不在 JS 静态文本中,只在运行时或字典命名规则下才能推断;
  • 带参接口(如 ?id=1)无参探测返回 400/500,容易被误判为「安全」;
  • 同一产品存在 多个 API 域名(读/写/管理),跨域探测必须用浏览器上下文,Python requests 常遇 SSL/CORS 失败。

1.2 痛点来源(真实漏报案例)

在对 某 SPA 业务站点(spa-demo-a.example.com)的早期手工/抽样测试中,曾出现:

接口 现象 根因
/integral/listIntegralOrders 未授权返回 116 条订单 JS bundle 中无此路径 ,抽样测试未覆盖
/address/getAddressInfo?id=2 未授权返回手机号、地址 JS 中有路径但 未拼 id 参数 测试

这两例直接推动了「全量覆盖 + 字典补全 + 参数矩阵」三项硬性要求,并最终沉淀为 Skill。

1.3 设计目标

用户只需提供:

测试 https://xxx.com/#/login 未授权账号 手机号 / 密码(可选)

Agent 自动完成:采集 → 建矩阵 → 浏览器内无凭证探测 → 输出 {域名}.md 报告。


二、整体架构

┌─────────────────────────────────────────────────────────────┐│  用户输入:target_url + 可选账号                              │└──────────────────────────┬──────────────────────────────────┘                           ▼┌─────────────────────────────────────────────────────────────┐│  Cursor Agent + web-unauth-scan SKILL.md(编排流程)          │└──────────────────────────┬──────────────────────────────────┘                           ▼        ┌──────────────────┴──────────────────┐        ▼                                      ▼┌───────────────────┐              ┌───────────────────────┐│ Chrome DevTools   │              │ Python unauth_scan.py ││ MCP               │              │                       ││ · navigate_page   │              │ · extract_apis        ││ · list_network    │              │ · build_matrix        ││ · get_network_req │              │ · browser_scan_js_fn  ││ · evaluate_script │◄─────────────│ · classify            ││   (fetch omit)    │  batch_*.js  │ · render_report_md    │└───────────────────┘              └───────────────────────┘                           ▼              ┌────────────────────────┐              │  {域名}.md + result.json │              └────────────────────────┘

分工原则:

层 职责
MCP 浏览器 登录、抓 Network/JS、在页面上下文执行 fetch(credentials:'omit')
Python 正则提取 API、字典补全、参数变体、批量脚本生成、判定逻辑、Markdown 报告
Skill 文档 约束 Agent 行为:禁止抽样、禁止 DELETE、输出格式、脱敏规则

三、实现过程

3.1 阶段一:Prompt 方法论(js.md)

最初以 195 行 Markdown 提示词 定义完整工作流,不依赖固定代码:

  1. Chrome MCP 登录并采集 XHR/Fetch + Script;
  2. 多源合并提取 API(Network / JS 正则 / 字典补全);
  3. 按路径语义拼参(id=1/2、page=1&pageSize=10);
  4. 全量 evaluate_script + credentials:'omit';
  5. 按 retCode、敏感字段输出报告。

关键约束(写入提示词,后迁入 Skill):

  • 禁止只测 20 条样本;
  • 无参 500 不代表有参安全;
  • retCode: 1001

+ data 需重点审查(部分业务后端的成功码约定)。

3.2 阶段二:脚本化(unauth_lib.py → unauth_scan.py)

将重复逻辑固化为 Python,避免 Agent 每次重写正则与判定:

模块 功能
extract_apis() 从 JS 文本正则提取 /api/*、/v1/* 等路径
dict_complete() 按模块前缀补 list、page、detail、getXById
build_variants() 按路径名生成 GET/POST + query/body 变体
build_matrix() 合并 JS + Network + 字典 → 全量 cases[]
browser_scan_js_function() 生成可在 MCP 中执行的 async fetch 批处理 JS
classify() 统一漏洞判定(支持 retCode 与嵌套 status.code)
render_report_md() 输出标准 {域名}.md

后期将 5 个分散脚本(render_report.py、merge_and_report.py 等)合并为单一入口 unauth_scan.py,提供 CLI 子命令:

python unauth_scan.py merge --domain boss-demo-c.example.compython unauth_scan.py render result.json boss-demo-c.example.com.mdpython unauth_scan.py build-matrix --domain admin-demo-b.example.com

3.3 阶段三:Cursor Skill 封装

在 .cursor/skills/web-unauth-scan/SKILL.md 中声明:

  • 触发词

:「未授权扫描」「测未授权接口」+ 登录页 URL;
* 输出约定

:工作区根目录 {域名}.md;
* 中间产物

:.unauth-scan/{域名}/matrix.json、batch_*.js、result_*.json;
* 安全约束

:不写 git 密码、报告脱敏手机号。

3.4 阶段四:批量执行与站点适配

问题 解决方案
Python 直连目标域名 SSL EOF 强制浏览器内 fetch
MCP 单次 payload 过大 每批 150~180 条,生成 batch_00.js …
站点 B 跨域 API 前缀 /api/v1/web/ Network 路径优先 + 站点级补判处理 status.code=401
站点 C code=001108 站点级 reclassify.py 补判 + 手工验证 project/config POST body

批量调度示例(run_batches.py)通过 MCP Python SDK 循环调用 evaluate_script:

await session.call_tool(\"select_page\", {\"pageId\": 4})result = await session.call_tool(\"evaluate_script\", {    \"function\": fn,  # batch_NN.js 全文\"filePath\": str(out),})

四、核心代码块

4.1 API 提取与字典补全

PATTERNS = [    re.compile(r'[\"\\'](/api/[^\"\\']+)[\"\\']'),    re.compile(r'[\"\\'](/v\\d+/[^\"\\']+)[\"\\']'),    re.compile(r'[\"\\'](/admin/[^\"\\']+)[\"\\']'),    re.compile(r'[\"\\'](/[a-zA-Z][a-zA-Z0-9_]*/[a-zA-Z0-9_/\\-]+)[\"\\']'),]defdict_complete(apis: set[str]) -> set[str]:    modules = {module_prefix(p) for p in apis if\"/\"in p.strip(\"/\")}    extra = set()    for m in modules:        cap = m[0].upper() + m[1:] if m else\"\"for suffix in [            f\"/{m}/list\", f\"/{m}/page\", f\"/{m}/detail\",            f\"/{m}/get{cap}ById\", f\"/{m}/listPage\",            f\"/{m}/list{m.capitalize()}Orders\",        ]:            extra.add(suffix)    return extra - apis

4.2 参数变体生成

defbuild_variants(path: str) -> list[dict]:    name = path.lower()    # get*Info / *Detail → id=1, id=2, 无参if re.search(r\"get.*info|get.*byid|detail\", name):        return [            {\"method\": \"GET\", \"query\": f\"id={i}\", \"body\": None, \"label\": f\"id={i}\"}            for i in (1, 2)        ] + [{\"method\": \"GET\", \"query\": \"\", \"body\": None, \"label\": \"none\"}]    # list* / *Page → 分页 GET + POST bodyif re.search(r\"list|page|logs\", name):        return [            {\"method\": \"GET\", \"query\": \"page=1&pageSize=10\", \"body\": None, \"label\": \"page\"},            {\"method\": \"GET\", \"query\": \"\", \"body\": None, \"label\": \"none\"},            {\"method\": \"POST\", \"query\": \"\", \"body\": {\"page\": 1, \"pageSize\": 10}, \"label\": \"post_page\"},        ]    ...

4.3 浏览器批探测 JS(MCP 执行体)

Python 生成如下结构,由 Chrome 在 已打开目标页 的上下文中执行:

async () => {  const cases = [ /* host, path, method, query, body */ ];  const LOGIN_OK = /1002|请登录|未登录|token不能为空/i;  functionverdict(body, status) { /* 与 Python classify 对齐 */ }  const out = [];  for (const c of cases) {    const url = c.host + c.path + (c.query ? '?' + c.query : '');    const opts = { method: c.method, credentials: 'omit', headers: { Accept: 'application/json' } };    if (c.body) { opts.headers['Content-Type']='application/json;charset=UTF-8'; opts.body=JSON.stringify(c.body); }    const r = await fetch(url, opts);    const text = await r.text();    out.push({ path: c.path, method: c.method, status: r.status, verdict: verdict(text, r.status), ... });  }  return out;}

credentials: 'omit' 等价于 不带 Cookie 的未授权请求,是整套方案的核心探测方式。

4.4 漏洞判定(多后端格式兼容)

defclassify(body: str, status: int) -> str:# HTTP 层if status in (401, 403):        return\"正常\"    j = json.loads(body)    # 嵌套 status 格式: {\"status\":{\"code\":401,\"msg\":\"token不能为空\"},\"data\":null}    st = j.get(\"status\") ifisinstance(j.get(\"status\"), dict) else {}    rc = str(j.get(\"retCode\", j.get(\"code\", st.get(\"code\", \"\"))))    msg = str(j.get(\"retMsg\", j.get(\"message\", st.get(\"msg\", \"\"))))    if st.get(\"code\") in (401, 403) or LOGIN_OK.search(msg):        return\"正常\"# retCode 格式: retCode 1001 + dataif rc in (\"1001\", \"0\", \"200\") and j.get(\"data\") isnotNone:        if _has_sensitive_data(j.get(\"data\")):            return\"高危\"return\"待复核\"# 或 404/405/缺参/异常

4.5 报告生成

defrender_report_md(target_url, domain, hosts, stats, results, login_note=\"\", note=\"\") -> str:    high = [r for r in results if r.get(\"verdict\") == \"高危\"]    # 输出:基本信息 → 统计摘要 → 高危表 → 全量附录(可折叠)

五、核心提示词

5.1 Skill 触发描述(SKILL.md frontmatter)

name:web-unauth-scandescription:>-  对指定 Web 登录页做 API 提取与全量未授权访问测试,输出 Markdown 报告到 {域名}.md。  用户提供测试 URL + 可选账号密码即可。使用 Chrome DevTools MCP 采集 JS/Network,  浏览器内 fetch(credentials:omit) 批量探测。用户说「未授权扫描」「测未授权接口」、  给出登录页 URL 和账号时使用本 skill。

5.2 用户一句话调用示例

测试 https://boss-demo-c.example.com/#/login 未授权

或带账号:

测试 https://admin-demo-b.example.com/#/login 未授权账号 13800138000 / ******

5.3 方法论提示词摘录(js.md)

角色定义:

你是一位安全研究员,负责自动化挖掘 Web 应用的未授权访问漏洞。

覆盖率硬约束:

对第二步清单中的每一个接口都测,不得只测「代表性样本」。带参接口:无参 + 有参都要测(无参 500/400 不代表有参也安全)。

字典补全示例:

/integral/listIntegralOrders     ← JS 可能只有 listIntegralRules/address/getAddressInfo?id=1     ← 带参详情类/{module}/list*、/get*、/detail*、/page*

高危判定:

无凭证返回 retCode: 1001(或业务 success)且 data 含敏感字段(姓名、手机、地址、订单…)HTTP 200 + JSON 列表 total > 0 且无登录提示

经验教训(必须写入执行逻辑):

  1. JS 提取 ≠ 完整 API 面,必须字典补全
  2. 抽样测试会漏报
  3. 带参接口必须拼参
  4. retCode 1001 + data 要重点审查
  5. 读写在不同域名,写域名也要测只读 list/get

5.4 Agent 执行检查清单(Skill 内嵌)

步骤 MCP / 脚本 禁止项
打开登录页 navigate_page —
可选登录 fill + click 登录失败不中断
抓 JS get_network_request → .unauth-scan/{域名}/ —
建矩阵 build_matrix() 禁止跳过字典补全
全量探测 evaluate_script(batch_NN.js) 禁止抽样
出报告 unauth_scan.py merge 禁止明文密码入报告

六、目录结构

.cursor/skills/web-unauth-scan/├── SKILL.md                 # Agent 编排说明├── report-template.md       # 报告结构参考└── scripts/    └── unauth_scan.py       # 库 + CLI(唯一主脚本).unauth-scan/{域名}/├── app.network-response     # 抓取的 JS├── matrix.json              # 全量 cases├── batch_00.js … batch_NN.js├── result_00.json … result_NN.json├── run_batches.py           # MCP 批量调度(可选)└── reclassify.py            # 站点专用补判(可选){域名}.md                    # 最终报告(工作区根目录)

七、实践结果


八、已知局限与改进方向

局限 说明 改进方向
字典补全噪声 无 /api/v1 前缀的路径跨域 CORS 失败 REST 站点仅补全 Network 真实前缀
特殊 POST body project/config 需 {url:...} 非 {} Network 抓包还原 body 模板
判定漏报 appToken 不在默认敏感字段规则 扩展 SENSITIVE 含 appToken
浏览器 Cookie 污染 页面已有登录 Cookie 时 fetch 仍带 cookie 隔离上下文或专用无痕页
写操作过滤 仅跳过 path 含 delete/save 等 维持只读策略,避免误伤生产

九、如何使用

  1. 在 Cursor 中确保 Chrome DevTools MCP 已连接(chrome-devtools-mcp --autoConnect)
  2. 对 Agent 说:

使用 web-unauth-scan,测试 https://目标域名/#/login 未授权
3. 等待生成 {域名}.md
4. 可选本地合并:

python .cursor/skills/web-unauth-scan/scripts/unauth_scan.py merge --domain 目标域名


十、总结

web-unauth-scan 将 安全测试方法论(js.md)、可复用 Python 引擎(unauth_scan.py)与 Cursor Agent 编排(SKILL.md)结合,形成「一句话输入 → 全量未授权报告」的闭环。其核心价值不在于替代 Burp,而在于:

  • 强制全量 + 拼参

,避免抽样与无参误判;
* 浏览器内探测

,绕过 SSL/CORS 与跨域 API 限制;
* 字典补全

,覆盖 JS 未声明的隐藏接口;
* 标准化报告

,便于归档与复测对比。

在上述三个示例系统的实践中,共确认 4 条高危未授权/信息泄露 路径,验证了该 Skill 在真实业务中的有效性。

原文链接:https://forum.butian.net/ai\_security/218


Web未授权访问自动化扫描Skill实现" 网络安全文章

Web未授权访问自动化扫描Skill实现"

本文详细介绍了基于CursorAgent实现的Web未授权访问自动化扫描Skill方案。该方案结合ChromeDevToolsMCP进行浏览器上下文API采集与探测,配合Python脚本实现正则提取、字典补全、参数矩阵构建及批量fetch(credentials:omit)无凭证请求测试,有效解决了SPA应用中JS未暴露接口漏测、带参接口误判及跨域CORS失败等痛点,最终输出标准化Markdown报告,并在多个站点实践中验证了其有效性。
独家丨极大似然估计简明解析" 网络安全文章

独家丨极大似然估计简明解析"

本文以抛硬币为例解析极大似然估计的核心思想,即选择使观测数据出现概率最大的参数。文章厘清了概率与似然由因推果与由果推因的区别,为避免数值下溢,引入对数似然将乘法转为加法且不改变最优解。结合Python代码演示参数寻优,并揭示最小化负对数似然与机器学习损失函数的内在联系,为理解模型训练提供统计学视角。
Ubuntu提权漏洞曝光,普通用户可直取root" 网络安全文章

Ubuntu提权漏洞曝光,普通用户可直取root"

QUALYS披露Ubuntu桌面版本地提权漏洞CVE-2026-8933,评分7.8分。漏洞源于snap-confine组件在沙箱初始化时的竞争条件,攻击者利用普通用户权限可提权至root。受影响版本包括UbuntuDesktop26.04、25.10及部分24.04。建议用户立即执行sudoaptupdate&&sudoaptupgradesnapd更新snapd以修复漏洞。
告别复杂流程!500行IMDB数据搞定LLM情感分类" 网络安全文章

告别复杂流程!500行IMDB数据搞定LLM情感分类"

本文介绍使用Scikit-LLM与GroqAPI构建IMDB情感分类Pipeline的方案。该方案省去传统ML特征提取,可在scikit-learn框架内直接调用Llama模型做零样本推理,500条样本测试准确率达0.95。建议开发者以此经典语法快速接入LLM处理分类任务,并控制请求量规避配额限制。
评论:0   参与:  0