1. 越权漏洞为什么是SRC的“常青树”
做SRC挖洞这几年,我最大的感受就是:SQL注入和RCE越来越难撞,但越权漏洞就像韭菜一样,割完一茬又长一茬。你登录了自己的账号,结果能把别人的订单查出来、把别人的资料改掉,或者一个普通员工账号直接干翻管理员后台——这类问题几乎在每个企业系统里都能翻到,而且很多团队做安全测试时压根没把这当回事,总觉得“反正有登录验证”。
这篇文章完整拆解一次我在某企业系统里的授权测试实战,从最开始的思路梳理、接口整理,到最终提交多个越权漏洞,每一步都还原当时的判断逻辑和实操动作。无论你是刚开始接触SRC的新手,还是要做企业自研系统安全评估的同行,这篇内容应该都能帮你省掉不少试错的功夫。
1.1 从一次“运气好”的提交说起
这次测试的起因,是某SRC平台给了一批授权范围内的测试目标。我一开始的思路还挺传统,先扫目录、翻JS、找API接口,想着能不能撞出个SQL注入或者未授权访问。结果一整天下来,常规漏洞一个没捞到,系统本身的安全基线做得比我预想中扎实。
后来我换了个思路:既然硬骨头啃不动,那就把重心放在业务逻辑上。我把所有带id、uid、orderId这类参数的接口全部提取出来,逐个替换身份标识做对比测试。结果当天下午就连续挖到了三个不同业务模块的越权漏洞,其中两个被定级为高危,还有一个涉及到批量数据导出,直接拉高了影响范围。
这不是什么玄学操作。企业系统做到一定规模,功能模块越加越多,前后端分离之后,权限校验的逻辑经常散落在各个微服务和接口里。只要有一个环节只校验了“是否登录”而没有校验“数据归属”,越权就出来了。而业务逻辑漏洞恰恰是很多自动化扫描器覆盖不到的盲区,所以挖掘的性价比特别高。
1.2 越权漏洞的分类与危害等级
行业内一般把越权分成两类:
- 水平越权:同一角色的用户A能访问用户B的数据。比如普通用户查看彼此的订单、简历、收货地址。
- 垂直越权:低权限角色能访问高权限功能。比如普通员工操作管理员的后台接口。
还有一种容易被归入越权的是未授权访问,严格来说它不算越权,但危害等级往往更高,尤其是在文件下载、订单导出这类接口上——连登录都不需要,数据直接裸奔。
SRC平台的定级标准一般是这样的:能读取他人敏感数据的水平越权,通常定级“中危”到“高危”,如果涉及订单金额、身份证号、手机号这类强敏感数据,直接高危;垂直越权如果是普通用户到管理员,基本也是高危起步。所以越权漏洞在SRC的性价比其实非常不错,不需要多高深的利用技巧,纯靠细心和对业务的理解。
2. 目标梳理与功能点画像
越权挖掘最怕的不是技术不够,而是没想清楚“这个系统到底有哪些接口,哪些接口值得试”。很多新手上来就开着Burp把站点所有请求乱点一遍,全凭运气,这其实很低效。我自己的习惯是先花一两个小时做信息梳理,把系统的功能地图画出来,然后再动手测,效果完全不一样。
2.1 先摸清系统到底有哪些“操作入口”
第一步肯定是基于授权范围内的目标做信息收集。资产测绘那些我不展开讲了,重点说说怎么从一个已经登录的Web系统里,把功能入口和接口关系弄清楚。
这次测试的目标是一个典型的前后端分离系统,前端所有API路径都写在JS文件里。这个阶段我做了三件事:
- 用浏览器的开发者工具把JS文件里的接口路径批量提取出来,按模块归类;
- 抓取页面正常操作时的请求,补充JS里没有暴露的隐藏接口。比如下拉框联动、自动保存草稿、列表排序这类接口,很多时候开发者会漏掉鉴权;
- 把每个接口的请求方法、参数名、当前登录身份能拿到的返回包记录下来,建立一份“接口-功能-数据归属”的对应表。
这里有个很容易被忽略的点:高权限账号能看到比普通账号更多的接口。如果手里只有普通用户账号,最好想办法注册或者申请一个更高权限的测试账号,越权测试时要用它来观察“目标接口长什么样”。如果没有高权限账号,就从普通用户能访问的接口往上钻,也能试出不少问题。
建立接口清单之后,判断优先级的标准就很重要了:
- 带id、uid、orderId、taskId这类明显标识参数的接口优先;
- 返回包里有手机号、身份证号、订单金额、文件路径的接口优先;
- 只接受POST但参数很简单、没有签名机制的接口优先;
- 新旧版本的接口同时存在时,旧版接口往往缺少新加的鉴权逻辑,优先试。
2.2 每个功能点背后的“身份逻辑”是什么
这一节其实是越权挖掘最核心的思考过程。拿到接口清单之后,要回答三个问题:
- 这个接口的数据归属于谁?是当前登录人,还是某个业务实体?
- 系统是靠什么来确定“当前是谁”的?是Cookie里的session,还是请求头里的token,还是干脆用参数里带上来的userId?
- 如果我把业务实体ID换一下,系统会拿什么来校验?
举一个我实际遇到的例子:某个订单查询接口,请求参数是orderId,返回包里有订单编号、金额、收货人手机号。我先观察了自己账号正常查询时的行为,发现系统是用Header里的token解析出当前用户,再拿orderId去查订单,但校验“这个订单是否属于当前用户”的环节缺失了。这时候把orderId换成别人订单的ID,返回包照样返回全量数据。
这种案例听起来特别简单,但真正麻烦的是系统不一定都这么耿直。有些系统在查询时会带上userId参数,很多开发想当然地认为“既然我能拿到userId,那这个接口就是安全的”,于是后端只校验userId对应的用户是否有效,却忘了校验这个userId到底是不是当前登录人。这类接口,直接在请求里改userId就能越权,比改业务ID还省事。
还有一种藏在“批量操作”里的越权,比如导出报表接口,参数是startTime和endTime,看起来人畜无害。但如果你发现它有uidList这类参数,把别人的uid加进去,可能就直接导出了别人的数据。
所以功能点画像这一步,最重要的产出物不是接口列表,而是一张“身份与数据归属关系表”。这张表里每一行就是一句话:
- 接口A:数据归属=当前用户,身份来源=token,参数=orderId,疑似无归属校验;
- 接口B:数据归属=指定用户,身份来源=参数userId,参数=userId,疑似可直接替换身份。
有了这张表,后面所有测试都变成了“验证猜想”,效率直接翻倍。
3. 实战挖掘:三个典型越权场景
这一节把这次实战里最有代表性的三个场景完整还原一遍。它们分别对应了水平越权、垂直越权和未授权访问三种形态,基本上覆盖了越权挖掘的绝大多数情况。
3.1 场景一:订单查询接口的水平越权
这个接口是我在JS文件里发现的,路径结构大概是/api/order/detail,参数是orderId。接口本身是POST请求,返回的是订单详情。
正常操作是:我在自己的订单列表里点击某一笔订单,前端用这笔订单的orderId调用detail接口,返回内容里能看到收货人姓名、手机号、详细地址、订单金额、支付记录。这是再典型不过的电商订单模块。
我第一时间做了两件事:
- 把orderId从正常值改成另一个随机数,比如原orderId是ORD2024XXXX001,改成ORD2024XXXX002,直接请求;
- 对比返回包的内容差异。
结果很直接,改成别人的orderId之后,返回包依然返回了完整的订单详情。这说明后端完全没有做“这个订单是否属于当前登录用户”的校验。
这里要说一个重要的细节:有些系统不是每笔订单的orderId都是纯数字或纯字符串的,可能是UUID,也可能是带日期前缀的复合编号。这种时候直接改一位数字大概率会得到一个“订单不存在”的返回包。别灰心,这不代表没有越权,而是你还没拿到别人的有效orderId。
怎么拿到别人的orderId?几个思路:
- 看订单列表接口是不是也越权。如果列表接口没限制,直接全量拉取;
- 看评论、售后、物流信息这些关联模块,它们的业务逻辑往往需要引用orderId,可能在其他接口的返回包里暴露;
- 看订单号生成规则,如果是日期+序号,可以尝试一个时间窗口内的序号遍历。
回到这个案例,确认单笔查询越权之后,我又顺着往下试了试批量导出接口。导出接口参数里有个orderIds数组,我把自己的一笔订单和别人的一笔订单同时放进去,结果导出的Excel里出现了两笔订单的数据。这就从单个查询越权升级成了批量数据泄露,危害级别直接拉满,厂商那边也很快就确认了漏洞。
3.2 场景二:用户资料修改接口的垂直越权
这个场景发生在系统的个人信息管理模块。我当时用的是一个普通员工账号,功能是自己改头像、昵称、手机号这类基础资料。
发现问题的过程挺有意思,不是靠猜参数猜出来的,而是靠“看返回包”看出来的。
修改手机号时,正常请求长这样:PUT /api/user/profile,参数有mobile和verifyCode。返回包里除了提示修改成功,还附带了一个字段role,值是EMPLOYEE。我第一反应是——这个接口居然在更新用户资料时把整个用户实体都返回了?那如果我往参数里塞一个role,后端会不会更新?
于是我构造了这样的请求:把role字段从EMPLOYEE改成ADMIN,同时保留mobile参数。结果返回包显示我的账号角色已变成ADMIN。这个接口的后端逻辑八成是把前端提交的整个用户对象拿去update,压根没做字段白名单。普通员工直接变管理员,属于教科书级别的垂直越权。
后来我又在另一个子系统的用户管理后台发现了一模一样的问题:管理员修改用户状态的接口/api/admin/user/status,参数是userId和status,没有校验当前操作人是否具备管理员权限。普通员工账号直接请求这个接口,也能把别人的账号禁用掉。
这件事给我的启发是:垂直越权不一定都要去撞“隐藏的后台接口”,有时候就藏在日常功能里——只是日常功能带了一些不该被前端控制的字段。所以看返回包、看接口文档里的字段说明,比自己瞎试参数要靠谱得多。
3.3 场景三:文件下载接口的未授权访问
前面两个场景都需要登录账号,这个场景更直接,连登录都不需要。
系统的附件管理模块有个文件下载接口,接口路径是/api/attach/download,参数是fileId。正常用法是用户点击附件链接,前端把fileId传给后端,后端返回文件流。
我先登录账号下载了一次自己的附件,抓包拿到请求,发现这个接口竟然没有强制校验token,把请求里的身份信息全部去掉之后直接请求,依然能下载成功。
紧接着我把fileId换成其他数值,结果能下载到其他人的附件。这些附件里有身份证照片扫描件、劳动合同扫描件。这个问题的本质是两层叠加:接口既没做登录校验,也没做文件归属校验。
这类问题的排查思路跟前面不太一样,因为它不在登录态里,所以你在正常功能里是“感觉不到”它的存在的。最常见的发现路径有两种:
- 一种是像这个案例一样,在正常下载附件时顺手把身份标识信息清了再重放,发现还能通;
- 另一种是翻前端代码或者历史接口文档,发现fileId这类参数本身没有鉴权逻辑,直接拼URL请求。
文件下载类越权的危害很容易被低估,因为很多开发觉得“文件名/文件路径是不可猜测的”。但实际上,一旦fileId是自增数字,或者文件名里有时间戳规律,遍历成本低到可以忽略不计。而且这类漏洞如果泄露的是合同、身份证扫描件,在SRC评级里基本就是高危起步。
4. 挖掘过程中的常见问题与排查技巧
越权挖掘看起来门槛低,但真正上手之后,各种奇怪的情况都有。这一节整理了自己多次实战中遇到的典型问题和对应的排查思路,很多都是文档里不会写的经验。
4.1 为什么明明越权了却返回401/403
我见过很多人反馈:我把订单id换了,结果返回401 Unauthorized或者403 Forbidden,是不是不存在越权?
不一定。这里有几种可能:
- 系统在最外层有一个网关或者中间件,会拦截不符合规范的请求。常见的情况是:网关只校验“请求头里有没有token”,如果你的测试姿势是直接把整个请求放Repeater里,token丢了一部分,网关这一层就给你拦了。这种时候先在原始请求上只改id,其他什么都不动,再试试;
- 系统做了“越权检测”风控,比如短时间内大量请求同一个接口,触发了风控策略,返回403。这种属于安全策略生效,不代表没有越权,换个低频率、模拟正常操作的姿势再测;
- 接口本身有独立的鉴权逻辑,但你没有触发它,比如某些接口要求特定Header、特定签名参数。
排查思路很简单:先在正常请求的基础上只改业务ID一个参数,保持用户身份标识不变,看返回值是否变化。如果返回的是“订单不存在”这类业务错误,说明系统已经在业务层校验了归属,大概率没戏;如果返回的还是正常数据,那就继续深入在身份标识上做文章。
4.2 绕过前端限制的三个思路
很多系统在前端做了权限控制,比如普通用户的菜单里压根没有“用户管理”这个入口,于是新手觉得“访问不到=没有接口”。
这种思路需要马上纠正。前端只是把接口藏起来了,不是把接口删掉了。后端接口如果没做权限校验,那么前端限制根本不构成安全边界。
三个绕过的思路:
- 直接看前端源码。前端路由和按钮权限的代码里,会以字符串形式暴露所有接口路径。用浏览器的开发者工具搜索关键词,比如“admin”“manage”“delete”“export”,把后端地址拼出来直接请求;
- 从后端端口和服务发现入手。前后端分离架构里,后端可能有独立的服务端口或者子域名。某个前端页面没接入的后台服务,直接访问它的根路径,有时候能拿到接口文档或者Swagger页面;
- 用不常用的请求方法。有些系统对GET和POST做了权限控制,但对PUT、DELETE、PATCH或者OPTIONS没做控制。比如禁用一个用户的功能在页面上是POST /api/admin/disable,你可以试试PUT /api/admin/disable,或者把参数从JSON改成form-data格式,偶尔就能绕过。
4.3 越权漏洞的“误报”陷阱
提交SRC报告之前,最怕的就是误报。被厂商驳回一次,不仅浪费时间,还影响白帽子的信誉。
最常见的误报场景是:你把orderId改成别人的,返回了“订单不存在”或者报错,你以为是漏洞,但其实系统根本没有那笔订单,或者那笔订单属于一个已经被删除的用户。
避免误报的核心方法:必须有“同一请求、同一参数、不同身份得到不同数据”的对照实验。
具体做法是:
- 用自己的两个账号A和B,各自创建一条数据;
- 用A的身份请求B的数据ID,如果返回B的数据,铁证如山;
- 如果返回“数据不存在”,再看是“业务层面确实查不到”还是“权限被拒绝”,前者不要提交,后者再确认是不是权限校验机制;
- 如果只有单个账号,就找公开数据的ID(比如订单列表里可见的、评论里展示的ID),先确认目标ID的数据确实存在,再测越权,避免拿一个不存在的ID去测。
另外一个观察点是:很多系统的接口返回“500”或者“401”,不是因为校验了权限,而是因为接口本身对异常参数处理不好。这类情况也要判断清楚,如果是因为把ID改成别人的导致程序报错,那可能是越权未遂,也可能是权限校验代码抛异常,不要盲目提交。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 改ID后返回正常数据 | 后端未校验数据归属 | 确认数据敏感度后提交 |
| 改ID后返回“订单不存在” | 业务层已过滤或ID无效 | 换有效ID再测 |
| 改ID后返回401/403 | 网关拦截、风控或独立性鉴权 | 保持身份标识,只改业务参数 |
| 改ID后返回500 | 程序异常或鉴权代码抛错 | 谨慎判断,避免误报 |
| 改身份字段后返回成功 | 后端信任前端提交的身份字段 | 严重越权,高危提交 |
5. 修复建议与防护体系
漏洞找到了,报告也提交了,接下来要聊的是怎么修。很多SRC白帽子习惯性只报漏洞不管修复,但如果你能提出可落地的修复方案,报告的价值会明显高出一截,厂商的认可度也会更高。
5.1 后端鉴权:永远不要在接口层相信前端
越权漏洞的根源,本质上是后端把“用户身份”和“数据归属”的校验分开了,或者干脆漏了数据归属校验。
修复的第一步是统一身份识别。登录用户的身份必须从服务端会话中获取,而不是从前端参数里获取。也就是说,userId、role这些字段,不该出现在前端请求参数里,更不该被后端当作身份来源。
第二步是“数据归属校验”的标准化。在业务代码里,凡是涉及查询、修改、删除数据实体的操作,都必须把“当前登录用户”和“数据实体的owner”做比对。最常见的方式是:先根据ID查出数据实体,再比对实体上的userId字段和当前登录用户是否一致,不一致直接拒绝,而不是先查后放。
第三步是接口层的统一权限注解或中间件。现在很多后端框架都支持在路由上声明所需的权限角色,比如Spring Security的@PreAuthorize,应该把所有接口的权限要求显式声明出来,默认拒绝,需要时才放行,而不是默认放行。
5.2 权限模型的三种落地方式
修复权限问题,不是写几行if判断就完了,还需要一个完整的权限模型。这里分享三种常用的落地方式:
- RBAC模型(基于角色的访问控制):用户归属角色,角色绑定权限,接口按角色授权。最通用,适合大部分业务系统;缺点是细粒度的数据归属控制还是需要配合行级校验一起做。
- ABAC模型(基于属性的访问控制):根据用户属性、资源属性、环境条件动态计算是否放行。适合复杂组织架构的业务系统,比如流程审批、多租户系统,但实现成本高,不宜一刀切全上。
- 数据权限过滤层:不纠结角色,而是让所有查询SQL都自动带上数据范围条件,比如“WHERE user_id = 当前登录人”。实现上可以用MyBatis拦截器之类的组件统一处理,减少业务代码里的重复判断。
5.3 从漏洞到体系:业务安全测试清单
最后分享一份我在做企业系统安全评估时常用的业务安全测试清单,直接在复测或自查时照着用:
- 所有的业务列表接口,尝试修改页码、数量、排序字段,看是否能横向越权;
- 所有带ID的详情接口,尝试修改ID为其他值;如果是复合ID,注意测试不同字段的组合(比如ticketId+eventId);
- 所有下载、导出接口,尝试清除或篡改身份标识后重放;
- 所有修改接口,尝试在请求体里追加没有在页面上出现的字段(比如role、admin、level),观察字段是否被更新;
- 所有“批量”操作接口,尝试在批量ID列表里混入其他人的ID;
- 所有后台管理接口,用普通用户身份测试一遍,不要只看前端隐藏了没有;
- 涉及手机号、邮箱、仓库地址等资料修改的功能,修改后检查是否真的校验了验证码或二次确认。
这份清单不是一次性做完就完事了,更重要的是形成“功能上线前必测”的流程。安全漏洞这个东西,修完还存在低水平重复的根因,往往是流程上没有把测试动作固化下来。我见过不少团队,漏洞报了、修了,过两个版本换了个业务模块又重新出现同样的问题,就是因为没有把“越权自测”这件事变成强制动作。
6. 写在最后
这次实战挖到的三个越权漏洞,从提交报告到厂商修复,前后只用了一周时间。其实每个漏洞的产生原因都不复杂,一个是没有做数据归属校验,一个是把角色字段暴露给了前端,另一个是下载接口完全缺鉴权。
我个人在实际操作中的体会是:越权漏洞挖掘最值钱的能力不是会多少工具,而是对业务的理解——你得知道一个企业系统里,哪些数据是敏感数据,哪些业务流程里藏着“本不该出现的操作入口”。把这个逻辑想清楚,再配合耐心的接口梳理和逐项验证,你会发现,越权漏洞远比想象中多。
如果你正在做SRC或者企业自我安全评估,建议把重心从撞SQL注入转移到业务逻辑测试上,尤其是用户身份与数据归属的边界,这一个点上的投入回报率,比大多数漏洞类型都高。
最后再分享一个小技巧:提交越权漏洞报告时,一定要把“影响范围”写透。不要只说“可以查看他人订单”,而是要说明“通过这个接口可以批量遍历订单号,大量获取收货人姓名、手机号、地址等敏感信息”。影响范围写得越清晰,厂商对漏洞的定级就会越准确,你的报告也更容易被认可。
越权漏洞听起来名字不起眼,但它在企业系统里的生命力,恰恰来自业务逻辑的复杂性和开发流程中的疏漏。把每一次测试当成一次对业务边界的审视,你会发现很多有意思的东西。