path-traversal-lfi

star 72

检测路径穿越和本地文件包含(LFI)风险;当目标存在文件读取/下载/预览功能且含路径参数时触发;适用于文件下载、日志查看、模板加载等场景。

Q16G By Q16G schedule Updated 6/7/2026

name: path-traversal-lfi description: 检测路径穿越和本地文件包含(LFI)风险;当目标存在文件读取/下载/预览功能且含路径参数时触发;适用于文件下载、日志查看、模板加载等场景。 when-to-use: 当目标存在文件读取/下载/预览功能且含路径参数(文件下载、日志查看、模板加载)时 allowed-tools: bash,read_file,list_files,rg user-invocable: false

路径穿越 / LFI 检测

成因引用

路径穿越成因:source(任何用户可控输入)→ sink(文件读取决策:os.Open / filepath.Join / include / 任何"决定打开哪个文件"的代码)。输入决定要打开/读取哪个文件,无路径规范化或白名单。不限于 /static/* FileServer,业务接口的 ?file= 同属此类。详见同根目录 pentest/web-security-testing/SKILL.md 漏洞成因图谱 · 路径穿越行(不在本 skill 重复成因)。

触发线索(基线检查项)

以下是已知的常见路径穿越触发线索,作为基线起点而非必检硬清单。核心原则:按"语义"识别,不按"参数名"识别——任何"外部输入决定服务端要打开/读取/包含哪个文件"的端点都属本范围。

基线触发线索按"sink 语义"分类:

  • 业务文件读取接口(D-014 形态):?file= / 类似参数 + filepath.Join + os.Open,参数名可能完全不像"路径"——按代码逻辑判定
  • 工单 / 订单 / 消息附件下载/api/<resource>/{id}/attachments/{filename} 等业务路径
  • 头像 / 图片 / 资源代理/img?src=... / /proxy?url=...
  • 日志查看接口/admin/logs?file=... / /api/logs/{name}
  • 模板 / 语言 / page 加载lang= / template= / page= / view=
  • 文档预览:office / pdf 文档预览功能调用文件路径
  • 导入 / 导出功能:CSV / Excel 导出可能涉及临时文件路径
  • 静态资源服务/static/*):FileServer 类入口——但禁止只测这一类就下"安全"结论
  • 代码模式:后端代码出现 filepath.Join(uploadsRoot, userInput) + os.Open(...) / ioutil.ReadFile(...) / http.ServeFile(...) 等"用户输入直接决定文件路径"形态

思考检查点

加载本 skill 时按这些问题思考:

  • 这个端点的响应是文件流吗?Content-Type 是非 JSON 吗?后端语义是不是"读文件"?
  • 同子系统其他文件读取入口是否同模式?(不限于"看起来像路径"的参数名)
  • 跨子系统是否有同模式端点?
  • 后端是否有 filepath.Join(...) / os.Open(...) 等"输入决定文件"的形态?参数有没有经过 filepath.Cleanrealpath 校验?

前置条件与安全边界

  • 仅在授权环境测试
  • 单参数最多 12 次请求
  • 目标文件使用无害文件(/etc/hostname/etc/passwdC:\Windows\win.ini
  • 不尝试读取应用凭据文件(如数据库密码),仅证明穿越可行性

检测步骤

Step 1:基线与端点枚举

  1. 从端点账本(recon-methodology 产出的 endpoint-ledger.jsonl)查询所有"响应是文件流 / Content-Type 非 JSON / 后端语义是读文件"的端点,逐入口独立测试
  2. 不限于参数名含 file/path/doc 的端点——按 sink 语义而非参数名筛选
  3. 正常请求,记录基线响应
  4. 观察参数值的模式(相对路径、绝对路径、文件名)

Step 2:路径穿越探测

逐步测试穿越 payload:

payload 说明
../../../etc/passwd 基本穿越
..%2f..%2f..%2fetc/passwd URL 编码绕过
....//....//....//etc/passwd 双写绕过(过滤 ../ 时)
..%252f..%252f..%252fetc/passwd 双重编码绕过
/etc/passwd 绝对路径直接访问
....\/....\/....\/etc/passwd 反斜杠混合(Windows)

Step 3:Windows 路径(若适用)

payload 说明
..\..\..\Windows\win.ini Windows 穿越
..%5c..%5c..%5cWindows\win.ini 编码反斜杠

Step 4:LFI 到 RCE(仅评估可行性)

若确认文件包含存在(PHP 场景):

  • php://filter/convert.base64-encode/resource=index → 源码泄露
  • 日志包含:确认日志路径可访问性
  • 不实际执行 RCE,仅评估路径是否可行

示例库

正例形态(代码层根因)

  • f, _ := os.Open(filepath.Join(uploadsRoot, r.URL.Query().Get("file"))) — D-014 形态:用户输入直接拼接到 os.Openfilename-path-join-traversal-lfi
  • http.ServeFile(w, r, "/var/www/" + r.URL.Path) — 静态 ServeFile 不规范化路径(urlpath-servefile-traversal-lfi
  • tmpl := template.Must(template.ParseFiles("/views/" + page + ".html")) — 模板文件加载用户输入(page-parsefiles-traversal-lfi
  • include($_GET['view'] . '.php') — PHP LFI 形态(view-include-lfi

窄化反例(必须避免)

以下是路径穿越维度的典型窄化误判:

  • "只测了 /static/* FileServer 都被 path.Clean 拦截 → 全站无路径穿越" — 错。这是 D-014 误判的直接根源。FileServer 类入口与业务接口(?file= + filepath.Join + os.Open)是两类完全不同的入口,FileServer 安全不代表业务接口安全
  • "参数名不像 file/path/doc → 跳过路径穿越" — 错。按 sink 语义判定,不按参数名筛选。业务接口可能用 attachment / name / id 等参数名但实际是文件路径
  • "已在某子系统命中路径穿越,其他子系统的同名端点不测了" — 错。跨子系统的隐式推广是漏报高发模式
  • "响应是 PDF / 图片二进制 → 看不到文件内容 → 跳过" — 错。可通过 Content-Length 差异 / 错误响应(不存在文件→特定错误码)等侧信道判定,也可读取 /etc/passwd 这类文本看是否能正常返回
  • "参数被 filepath.Clean 处理过 → 安全" — 错。Clean 只规范化 .. 但不阻止绝对路径,且不检查最终路径是否在允许目录内;必须配合 filepath.Rel 或白名单校验

反例义务(必须遵守)

为什么这里是「必须」:反例义务属于交付契约——"未发现路径穿越"或"path.Clean 拦截"结论是覆盖完整性的产物声明,缺失反向验证清单会让下游误信"该维度全站安全"。

写"未发现路径穿越"或"path.Clean 拦截"前,产物必须包含:

  • 测过的文件读取入口完整清单(按 sink 语义枚举,按参数名筛选;含业务文件接口 / 工单附件 / 头像代理 / 日志查看 / 模板加载 / 静态资源 / 文档预览等全部触发线索类别)
  • 每个入口测过的 payload 类型(基本穿越 / URL 编码 / 双重编码 / 绝对路径 / Windows 路径等)
  • 每个入口的响应证据(基线响应 / 穿越响应 / 文件内容确认)

清单不完整 → 结论降级为 partial-coverage 并显式声明未覆盖范围。

特别警示:只测 /static/* 而未测业务文件接口的,结论降级为 partial-coverage-only-static禁止宣称"全站安全"——这是 redhaze-range D-014 误判直接根源。FileServer 类入口(Go http.FileServer 自动调 path.Clean)与业务接口(?file= + filepath.Join + os.Open 手动拼接)是两类完全不同的入口。

闭环验证要求(必须遵守)

通用闭环口径见同根目录 common/closure-verification.md(技能表 path 列同一抽取根下,需要时 read_file 读取)。核心:完整证据链才判 confirmed,中间信号最多 suspected。本漏洞特有要点:

  • 仅凭"路径参数存在"不得判定漏洞,必须确认实际读取到目标文件内容
  • 响应中需包含文件内容(如 /etc/passwd 的用户列表格式)作为证据

判定标准

现象 判定
响应中包含目标文件的实际内容 confirmed
响应有差异(大小/状态码变化)但未泄露文件内容 suspected
路径被规范化或白名单限制,无法穿越 not vulnerable
只测了部分文件读取入口(如仅 FileServer 类),未覆盖业务文件接口 partial-coverage-only-static(不得宣称 safe)

修复建议

  • 使用文件 ID 而非文件路径作为参数,通过映射表查找实际路径
  • 对文件路径做规范化后校验(realpath 后检查是否在允许目录内)
  • Go 场景:filepath.Clean 后用 filepath.Rel 验证最终路径在允许目录内;或使用白名单
  • 禁止 ../ 和编码变体
  • PHP 场景禁用 allow_url_includeallow_url_fopen
Install via CLI
npx skills add https://github.com/Q16G/aster --skill path-traversal-lfi
Repository Details
star Stars 72
call_split Forks 6
navigation Branch main
article Path SKILL.md
More from Creator