星巡研究院
本册目录

第2章 · EX-D

渗透视角审计的固定六阶流程

免费章节
3543 字约 8 分钟本章 7 小节

2.1六阶总纲与顺序铁律

结论:六阶顺序不可颠倒。每一阶的产出是下一阶的输入。跳过任一阶,结论不可信。

顺序:测绘 → 技术栈推导 → 攻击面推导 → 数据流追踪 → 成链 → 伤害定级。

阶动作产出退出判据
1 测绘列入口、列资产、列依赖、列配置入口清单、资产边界表能说出全部外部可达入口,无"应该走这里"的猜测
2 栈推导版本落包、部署形态落文件技术栈表每个版本能落到具体包名与版本号
3 攻击面标可控性、鉴权要求、数据去向攻击面清单(排序)每行都有可控性与业务价值判断
4 数据流脏标记传播、收敛点标注传播路径图每个收敛点能回溯到具体入口参数
5 成链补最小增量、验证跨请求状态链路草案链路能过五问法前四问
6 定级按整链最大可达定级定级结论、修复建议有证据、有范围、有整改方案

铁律的现实理由:先测绘再推导,避免用"猜的版本"去查组件漏洞;先攻击面再数据流,避免把三个月时间花在无外部入口的模块上;先成链再定级,避免按单点定级造成的高估或低估。

倒着走的审计,一定会变成"危险函数清单"。

2.2第一阶 源码情报测绘:找入口,不找漏洞

结论:这一阶的任务只有一个:画出"外部请求 → 具体文件"的完整映射。

六个观察面:

目录结构。 找入口目录(controller、api、route、handler、servlet)、危险目录(upload、template、cmd、exec、export)、遗留目录(backup、old、test、demo、bak)。遗留目录是二次开发项目的高发区,接口还在,鉴权常已失效。

配置文件。 连接串、密钥、开关、白名单、上传限制、调试开关、跨域策略。配置错误常是链路第一环,比代码缺陷更容易被忽略。

路由注册。 全量导出路由表,逐条标注鉴权装饰器、注解、拦截器、白名单例外。导出方式按框架而定,Java 看注解扫描结果,PHP 看路由文件,Python 看装饰器注册与 OpenAPI 描述。

依赖清单。 优先锁文件:composer.lock、pom.xml、build.gradle、requirements.txt、poetry.lock。声明文件给范围,锁文件给事实。

非代码资产。 .git 残留、.env、备份压缩包、CI 脚本、Dockerfile、部署文档、测试用例与默认账号。这些不在代码里,但在源码包里。

日志与任务。 定时任务、消息消费、CLI 入口、健康检查、内部回调。这些入口多数无鉴权,是横向移动的稳定落点。

判据:能在纸上画出全部映射。做不到就继续测绘,不要进入下一阶。

2.3第二阶 技术栈推导:把版本落到具体包

结论:版本必须落到包名与版本号。推不到就写"未知",不要用"大概是 PHP 7"往下推。

五个推导方向:

  • 锁文件优先。声明是范围,锁是事实。同一份 composer.json 在不同环境可能装出两个版本。
  • 路由写法反推框架。 注解式、配置文件式、约定式,三种写法对应三代框架风格。框架确定后,参数绑定规则、默认过滤、异常处理全部可预期。
  • 模板语法反推引擎。 这是模板注入类链路的前置条件。不同引擎的表达式语义不同,危害边界也不同。
  • 响应特征反推部署。 响应头、错误页、Cookie 键名、默认路径,能定位容器与语言,部分框架的会话键名长期固定。
  • 部署文件反推形态。 php-fpm、mod_php、gunicorn、uwsgi、JVM 单进程,五种形态的权限天花板完全不同。

版本与形态一旦确定,攻击面立即收敛。例如 fpm 形态下,文件包含与命令执行的权限边界,是 fpm worker 的 uid,不是 web 目录的属主。假设错这一层,定级就错一档。

2.4第三阶 攻击面推导:可控性 × 可达性 × 业务价值

结论:攻击面推导是给入口排序,不是给入口定性。

三要素:

可控性指参数值是否由外部决定,以及外部能决定到什么程度。路径参数、查询串、请求体能决定完整值,属高;请求头、文件名能决定部分值,属中;服务端生成的值属低。

可达性指需要什么身份。匿名可达最高,普通用户次之,特定角色再次之,内部服务最低。

业务价值指汇聚点后面是什么。碰到批量用户数据、资金操作、权限变更,价值最高;只碰展示串,价值最低。

参数来源可控性典型入口
路径段 / 资源 ID高REST 详情、导出、下载
查询串高列表、搜索、排序、分页
请求体(JSON / form)高业务提交、批量导入
请求头(Cookie / UA / 自定义头)中会话、日志、限流、审计、回调签名
文件名 / MIME / 文件内容高头像、附件、导入解析
服务端会话值低除非存在会话固定或反序列化

排序规则:可控性高 × 匿名可达 × 直连汇聚点,排最前。排序是时间分配,不是结论。排在前面的先追,不等于后面的没洞。

2.5第四阶 数据流追踪:脏标记与收敛点

结论:每个收敛点必须能写出一条从入口参数到该行的完整路径,路径上每一层校验都要判定有效性。

三个概念定死:

脏标记。 外部可控的值标脏。传播发生在赋值、拼接、容器传递、跨函数参数、跨文件返回。

收敛点。 脏值进入汇聚函数的那一行。这一行是审计靶心,其余都是路径。

脱脏。 只有三种形态算真脱脏:白名单枚举(允许集有限且闭合)、类型强转(值不再进入字符串语法)、参数化(值不参与语法构建)。其余一律算部分脱脏,按未脱脏处理。

追踪三条规则:

  1. 同函数内按语句顺序走,遇分支分开走,不合流。
  2. 跨函数按参数映射,不按调用顺序猜。同名参数不等于同一个值。
  3. 跨中间件与过滤器看执行顺序。过滤在解包之前还是之后,结论完全不同。
// 入口:GET /order/detail
public function detail(Request $r) {
    $id = $r->input('id');                      // ① 入口参数,外部可控,标脏
    $id = (int)$id;                             // ② 类型强转,真脱脏(数字上下文)
    $row = Order::where('id', $id)->first();    // ③ 收敛点,参数化,值不参与语法
    return view('order.detail', ['row' => $row]);
}

同一个入口,把 ② 换成 trim($id),把 ③ 换成字符串拼接,链路立刻成立。审计看的是 ② 有没有改变值的类型,不是"有没有过滤函数"。

这也是 1.1 讲的检索法失效的具体形态:Order::where 不在任何危险函数表里,但它就是收敛点。

2.6第五阶 成链:补最小增量

结论:单点升级为链路,缺哪一环补哪一环。补的那一环叫最小增量。

链的三种形态:

同请求链。 一个请求内完成全部环节。最短、最稳、最容易复现,优先构造这一种。

跨请求链。 写入与触发分离,典型是存储型、缓存投毒、任务队列。需要证明两次请求之间的状态保持:写入落在哪张表、哪个缓存、哪个队列,读取方什么时候消费。

跨主体链。 借用他人权限或借用服务端权限,典型是越权、SSRF、模板注入回显。需要证明执行主体不是你:要么服务端替你发请求,要么别人的会话替你读数据。

四种最小增量:

  • 补一个可控点。当前入口参数不可控,去别的入口找同名参数,或找可覆盖的参数。
  • 补一段回显。汇聚点执行了但看不见结果,找同数据的另一个读出侧。
  • 补一次权限跨越。当前身份不够,找未校验主体的接口,或找服务端代发的出口。
  • 补一个持久化载体。需要跨请求生效,找未被清理的落点。

判据:链上每一环都要有代码位置或报文位置。出现"通常可以""一般会""大概率能",这一环就是没补上。

2.7第六阶 伤害定级:按整链最大可达

结论:定级对象是整条链最终可达的最大业务伤害,不是链上最危险的那一行代码。

四档:

档判据抽象示例
信息级仅泄露非敏感元信息,或前置条件极高内部版本号、公开配置项
中泄露受限数据,或可做低影响操作单用户非敏感资料、有限枚举
高可跨主体获取数据,或执行敏感操作批量用户数据、关键业务操作、权限提升
严重可直达或接近最高权限,或造成不可逆伤害管理员接管、全量数据、审计链破坏

定级必须写三段:

可达范围。碰到哪些数据、哪些操作、多少个主体。 前置条件。需要什么身份、什么环境、什么开关状态。 可复现性。证据形态是什么,换环境是否成立。

定级前回五问法。第 1 至 3 问任一不过,降档或撤案。第 4 问不过,标"未证实",不进正式结论。