你第一次听说“越权漏洞”这个词,可能是在某次被甲方按在地上摩擦的渗透报告里:明明没爆出什么“高危”“严重”,但偏偏一个“中危——水平越权”就把你钉在耻辱柱上。说实话,越权这东西,是最容易出,但靠传统扫描器又几乎扫不出来的一类漏洞。它藏在一行业务逻辑里,藏在“你把订单ID改成别人的试试”这种操作里。catfish这个工具,我盯了很久,试用之后发现它是真能把这类问题批量捞出来的那种,所以这篇把它从头到尾拆开写清楚——包括它的原理、部署、配置、实战踩坑,以及最后那层“工具给线索、人来判断”的关键防线。
1. 先搞懂越权漏洞:水平越权和垂直越权的本质
1.1 水平越权:同级别用户之间,凭什么我能看到你的数据
水平越权,又叫IDOR(Insecure Direct Object Reference),说人话就是:两个平级用户,按理说各自的订单、个人资料、文件、优惠券都是互相隔离的,但因为接口只凭前端传过来的ID去查数据,又没有校验这个ID到底属于谁,结果我把URL里的订单号从10086改成10087,就直接看到了别人的订单。
我在某内部教育系统的测试里遇到过最典型的案例:一个获取课件的接口长这样:
GET /v1/user/homework?homeworkId=12345 Cookie: session=xxx(学生小李的登录态)把参数改成别人的homeworkId,直接返回了那位同学的作业全文和成绩。这里没有任何复杂攻击技巧,就是改了ID。水平越权的本质,是服务端对“资源归属”缺少校验:只验证你有没有登录,没有验证“这条数据是不是你的”。这种漏洞分布在查询类接口里最多,其次是修改、删除类接口——改别人资料的POST请求,危害比单纯读取更重。
1.2 垂直越权:普通用户把管理员的功能点了
垂直越权则是角色越界。系统里有很多功能是分角色的:管理员能导出用户表、能改配置,普通用户不能。但很多开发在做接口权限时只做了“前端菜单隐藏”,后端接口不设防,普通用户直接拼URL或者改请求方法就能访问。
某次在某后台管理系统测试,前端隐藏了“一键重置密码”按钮,但让我抓到了这么个请求:
POST /admin/user/resetPwd Body: {"userId": 1} Cookie: 普通用户登录态普通用户从理论上根本不该有这个接口的调用权,结果后端没校验角色,请求照常执行。垂直越权对比水平越权,更稀里糊涂——起码水平越权还知道你是谁,垂直越权连“你是谁”都不管。本质是服务端缺少对“功能权限”的判定,这是个纯业务逻辑问题,和有没有注入完全无关,所以传统漏洞扫描器基本拿它没辙。
1.3 为什么越权成为“检测黑洞”
越权漏洞难检,核心在于它必须带着登录态去测,而且必须跨身份测。常规扫描器无状态、不登录,扫完只能发现文件上传、SQL注入这类不依赖用户身份的问题;越权这玩意儿,你没登录,根本触发不了那条业务链路。就算有的扫描器支持登录,它也只是“单账号扫描”,没法自己切换成另一个账号去重放请求,所以扫出来的结果永远是白色的“无漏洞”。
另一个原因是越权与正常请求的响应差异可能很小。一个JSON接口,返回码都是200,body里只是字段值不同,自动化研判稍不留神就放过去了。这就是越权测试真正费人的地方——它需要人有耐心地一个参数一个参数改,一个接口一个接口比对。所以,把“更换身份→重放请求→对比响应”这个过程做成自动化,是提升效率的唯一正路,catfish干的就是这件事。
1.4 越权不算“技术含量高”,但危害等级真的不低
越权漏洞对业务的影响按数据类型分,可以是普通信息泄露,也可以是账号接管、资金篡改、隐私批量泄露。某大厂因为水平越权导致几十万用户身份证号被刷走的新闻,相信大家都有印象。这里得说句公道话:越权漏洞挖掘难度低,但修复不一定好修——涉及每个接口的资源归属校验,改起来要动非常多业务代码。
所以要手动去测,工作量你永远干不完;要自动化去测,又容易误报满天飞。理想状态是工具先做“疑似清单”,人再做“二次复核”。catfish的价值,正在于把“疑似清单”的生成效率拉满。
2. 为什么越权检测要选catfish而不是全手工
2.1 catfish的检测逻辑:一个“身份证替换器”的思路
catfish是我个人在整个越权检测工具链条里比较看好的一款开源Java工具。它的核心思路非常简单,一句话总结:把A账号的请求,用B账号的身份重新发送一遍,然后看响应是否“越界”。
它的工作逻辑拆开大致是这个样子:
1. 浏览器走代理访问被测系统,catfish自动记录下登录用户A发出的请求 2. 测试人员配置好另一个身份B(普通用户B,或者管理员) 3. catfish对每个记录的请求,替换Cookie为B的登录态 4. 保持请求的URL、Method、Body完全不变,重放一次 5. 将A身份的原响应与B身份的重放响应进行比对,输出差异这个模型的聪明之处在于,它不关心系统有多复杂,只关心“同一条请求换个人发,结果变没变”。如果B身份重放后依旧能拿到A身份的数据,或者能访问到只有管理员才有的功能,那基本就说这里有越权疑点。相比手工一个一个改ID,catfish把最枯燥的步骤——请求采样、身份替换、重放、响应采集——全部接管了,人能腾出手来专注最后的研判。
2.2 我自己对比过的几种检测方案
先说自写脚本。很多人第一反应是“不就改cookie重放嘛,我写个Python脚本就行了”。这话对,但实际操作下来,自写脚本的最大问题在请求采集:你得先手动从Burp导出流量、清洗、分类,找Cookie位置,又要处理登录态过期。做完这批流量,下一批业务更新又失效了。catfish是自己代理抓包,脚本这一层它直接替你完成了。
再对比Burp Suite的插件方案。Burp确实有类似越权检测的扩展,能改个Cookie头重放,但用起来有门槛,而且Burp插件版本的配置流程比较绕,对新手极不友好。catfish的优势是独立运行、界面直观、配置项集中,部署完一看就明白怎么操作。
还有一种思路是商业漏扫加越权模块。说实话,商业产品的越权检测也主要是重放对比,但价格不菲,很多中小团队根本不够预算。如果你想低成本解决“批量越权自理”的诉求,catfish确实是个很务实的选择。
2.3 catfish的适用边界:别拿它当全能扫描器
工具必须知道它能干什么、不能干什么。catfish擅长的是接口/页面级的水平越权和垂直越权检测;它不擅长的是:
- 带强校验的场景:接口参数里有时间戳、随机数、签名等,在同一身份下重放都可能失败,跨身份重放更没法正常判定。
- 图形验证码/短信验证码登录:自动化登录受阻。
- 客户端加密/私有协议:它只认HTTP,App里的私有协议先抓包转HTTP再说。
所以,用catfish前得先看目标系统是什么调性。纯网页端、普通Cookie/Session认证的,干起来很爽;接口带签名又有风控的,你会先被签名校验折腾一轮。我的习惯是它拿来做正向的系统性排查,跑完出报告再人工复核就好。
3. catfish部署和配置的完整落地流程
3.1 环境准备:一台装了JDK的机器就够了
catfish是Java写的,所以部署门槛低得吓人。只要机器有JDK,都能跑。用以下命令看一眼环境:
java -version有回显就说明没问题。我本地用的JDK 8,后来换到JDK 11也正常,版本这块没有硬性限制。然后拿到catfish的可执行包,直接启动就行:
java -jar catfish.jar启动之后,catfish默认会开一个本地代理监听端口和一个管理界面端口。管理界面就是浏览器访问的检测平台,我用的时候是在浏览器里打开http://127.0.0.1:8080/这样的地址。服务起来了,第一件事是打开设置页把代理端口和检测策略看一遍,别急着操作业务。
注意:本地如果已经占用8080端口,会直接冲突启动失败。遇到这种情况,改一个不常用的端口即可,改完记得把浏览器代理端口也同步改过去。
3.2 代理配置与登录状态采集:让catfish“看见”你的业务请求
catfish在代理方面的工作模式,非常像那些本地抓包工具——你在浏览器里配置HTTP代理,指到catfish的监听端口,之后浏览器所有访问目标系统的请求都会先经过它。
当时我图省事,直接在浏览器设置里填了代理地址,然后访问被测系统。这一步的操作顺序很有讲究:
- 先让catfish正常监听。
- 配置浏览器HTTP代理指向
127.0.0.1:代理端口。 - 用一个账号(比如账号A)登录被测业务系统。
- 先在系统里把核心业务功能完整逛一遍——查列表、点详情、改资料、下载文件,动作越多越好。
这一步是“流量采样”。catfish会把经过代理的请求记下来,到了重放阶段,这些请求就是你做越权测试的测试用例。采样有个教训:千万别没登录就逛系统,那样采到的全是登录页和公共接口,测试等于白干。
3.3 账号配置:两个“人”才是越权检测的核心
越权检测至少需要两个身份,catfish的配置逻辑也是围绕账号展开:账号A负责“采样”,账号B负责“重放”。我一般习惯这样搭配:
| 场景 | 账号A(采样身份) | 账号B(重放身份) | 期望结果 |
|---|---|---|---|
| 水平越权 | 普通用户A | 普通用户B | A能访问的数据,B不能看 |
| 垂直越权 | 普通用户 | 管理员 | 普通用户无管理功能权限 |
| 混合 | 普通用户A | 管理员 | 双向验证权限边界 |
配置时,会把两个账号的登录方式都填进去。catfish在重放阶段并不是真的一次一次登录B去发请求,而是会拿B的会话标识做替换。如果被测系统支持免登录复用会话,建议先在浏览器里把B账号登录好,把B的Cookie复制到配置里,这样能大幅降低登录态过期概率。如果把“自动登录”玩不转,例如系统有操作验证码,最强方案就是手动收集B的Cookie再喂给它。
3.4 检测范围和策略配置:先框住要测的地盘
一个系统接口可能有几百条,但并不是每个都值得做越权重放。静态资源、登录接口、文件下载接口里某些无关请求全都重放一遍,不但浪费时间,还会制造大量噪音。所以配置里必须做白名单和黑名单。
我实测里最终的策略组合是:
- 限定了检测域名和URL前缀,只测业务主路径,比如
/api/或/user/。 - 把静态资源后缀排除掉:
.js、.css、.png、.jpg、.ico。 - 把登录接口、登出接口、验证码接口排除掉。
- 设置了重放时保留原始请求的Header内容,只替换Cookie,避免因为缺Header导致请求不生效。
- 设置Cookie替换时对Session相关的字段做整体替换,不是只替换小半截。
这套配置做完,再跑就是比较干净的“有效测试流量”。方便起见,建议第一次先拿10条请求跑通全流程,确认响应对比在正常出报告,再放开跑全量,不然配置不对,跑完几小时全浪费。
4. 用catfish做真实业务系统的越权检测:从采到测的完整实操
4.1 流量采样阶段:把业务点“喂”给工具
第一次正式测某内部管理系统时,我在采样阶段拿了账号A,把系统里主要的业务模块逐个点了一遍:
- 个人资料页:查自己的手机号、邮箱。
- 订单查询:查自己的订单列表。
- 订单详情:查单个订单的具体内容。
- 文件下载:下载了一个附件。
- 用户列表:一个普通用户正常情况下是没有访问权限的,但我也点了一下,看返回什么。
每点一个功能,catfish这边就会同步多出对应的请求记录。采样结束,我把请求梳理了一遍,发现大概几十条候选请求,其中大部分是静态资源和登录类,需要过滤,剩下的业务请求大概有十几条,是真正值得重放的。
这一步最容易被忽略的,是把“前端看不到但真实存在的接口”漏掉。很多越权测试,问题恰恰出在隐藏接口上。我的做法是开着浏览器开发者工具,在页面里操作完正常功能后,把Network面板里的XHR请求全部看一遍,看到与功能相关的接口,就会手动在页面上触发一次,确保它进代理流量。
4.2 执行检测与响应对比:观察差异信号
流量采样完成后,我在catfish管理界面勾选了要重放的业务请求,选择身份B作为重放身份,点了“开始检测”。这时候catfish就按我之前说的逻辑跑起来了:每个请求把Cookie替换成B的,然后保持原始请求的大部分属性不动,重新发出去,把B身份下的响应抓回来。
检测过程中,catfish的界面会列出每个URL两种身份的响应对比信息。我比较关注的指标主要有这三个:
- 状态码变化:A身份返回200,B身份返回403,这说明权限校验正常;如果A和B都返回200,且返回体一致,那基本是“数据越权”的信号。
- 响应长度差异:同一请求,A拿到完整数据,B拿到的响应体长度和A几乎一样,那也是重点怀疑对象。
- 响应内容关键词:响应体里出现了不该出现的用户名、手机号、订单号、姓名等字段,直接列为高价值线索。
单看状态码其实不够。我曾跑出来一个接口,两个身份都是200,但A返回的是“你好,管理员”,B返回的是“你好,普通用户”——长度几乎一致,但内容完全不同,这说明接口只是把欢迎语换了个称呼,没有数据越权。可如果B能读到A的私密字段,那才是真出问题。所以只看“响应是否一致”还不够,要把响应体存下来看内容。
4.3 结果研判与报告输出:工具列清单,人来下结论
跑完后catfish会列出疑似越权请求。我是按这个逻辑去确认的:
先看请求的URL和参数是否带用户ID、订单ID、文件ID这类“资源标识”。带资源标识的接口是越权高发地,重点看。拿到了别人的数据,确认水平越权。功能URL是管理员的,比如/admin/前缀,但普通用户重放后还能访问,确认垂直越权。
后看影响面。同样是越权,查别人手机号的危害和重置别人密码的危害不在一个量级。我在报告里一般会标注:“该接口存在水平越权,可查看任意用户手机号”,而不只是笼统写“越权漏洞”。
报告里必须写清楚复现步骤和证据,我把catfish导出的请求信息和响应体贴在报告里,并标注两个账号的使用情况。交付给开发时,他们拿着证据直接能定位到接口,省得来回扯皮。
4.4 修复后的回归验证:用同一套测试用例重跑
开发上线修复后,有个容易犯的错:换了接口参数格式,修复了漏洞,但老测试用例已经失效了。所以我在回归的时候会重新采一次流量,用修复后的最新请求跑一遍同样流程。实操里,“给某接口加上数据归属校验”和“给接口加个角色判断”是最常见的修复方式。回归测试如果两个身份重放后,B的响应变成了无权限/无数据,而A的响应正常,这就算修复到位。回归这一个来回很花时间,但越权这东西不回归,代码一改就可能把问题带到别处。
5. 检测过程中的雷区与排查经验
5.1 请求重放全是401/403,连登录态都丢了怎么查
最让人崩溃的不是漏洞多,而是跑完一看,所有请求返回的响应都是“未登录”或“无权限”,整个测试等于白做。这个现象,八成是身份切换没切完整——有些系统不只有Cookie,还有特定的Header来标记用户,只换Cookie不换Header,服务端不认识你。
排查思路是:在浏览器无痕窗口里重新登录账号B,打开开发者工具把请求里的身份相关字段全部找出来——不止Cookie,可能还有自定义Header。然后回到catfish设置里,把这些字段整体替换成B的值,重跑一轮。另外,也要看看系统是否绑定了IP或者设备指纹,如果服务端把Session跟IP绑定了,A机器上重放B的Cookie,照样会被踢。这种情况直接在同机浏览器里先把B登录好,让请求从B的真实会话环境发出,风险更小。
5.2 误报多到无法看,怎么快速把噪音压下去
跑出来的“疑似越权”几百条,点开全是正常内容,这通常不是catfish出了问题,而是你采样的流量太脏。误报主要来自这几种情况:
- 静态资源没过滤,JS/CSS文件被重放后长度一致,被视为“疑似”。
- 登录接口被重放,B身份直接登录成功了,响应自然一样。
- 带随机参数的请求,本身就每次响应不同,拿来对比意义不大。
- 时间戳、验证码、随机数拼接在URL里的动态请求被当成了越权线索。
把这些请求从测试范围里剔除,是最直接的降噪方法。可以在配置里把动态接口、登录登出接口直接排除,只保留带资源标识的核心业务接口。我的经验是,跑全量之前先用一两个已知的正常接口验证配置,确认输出是“无越权”的时候,再放开跑,能避免浪费整轮测试时间。
5.3 越权检测的几条独家建议
第一,优先测修改类接口。很多人测越权只顾“查询”,是因为查询接口改个ID就能看到数据,证据链清晰。但“修改类”越权更致命——用一个普通账号B重放A的修改资料请求,如果能把A的头像、手机号都改了,这已经不是信息泄露,而是账号数据被篡改。这种请求响应体往往跟正常请求长得一模一样,不细看根本发现不了。我在跑的时候,会专门挑出POST/PUT请求做二次人工验证,观察完响应后再去登录A账号确认数据有没有真的变化。
第二,匿名访问也要测一遍。catfish的测试重点是“身份替换”,但有一种越权是“未登录直接访问受限接口”。这种一般在权限过滤没做好的系统里出现,比如直接访问/admin/list,不登录也能返回数据。我一般会在测完身份替换后,在浏览器里开无痕窗口,直接访问一遍核心URL,看看有没有无认证越权。
第三,工具永远代替不了眼光。自动检测给你圈出一堆线索,但哪些真要上报、影响范围多大、数据敏感程度如何,都得靠人去判断。我见过有人拿着catfish的输出直接丢给甲方,结果里面一半以上是正常权限请求,这是自己坑自己。宁可多花点时间把关键接口人工复核一遍,也不要交一份注水报告。
按照我的习惯,每轮检测结束都会留一份“测试环境与用例快照”,包括当时用的两个账号、采样时间、接口范围、catfish的关键配置。这样下次回归复测,整套逻辑能复用,不会因为换个账号、换台机器就全乱套。越权测试这件事,工具负责把重复劳动消灭掉,人负责把判断力发挥出来,配合好,事情就能干得又稳又利索。