做SRC项目这么久,我发现自己提交的漏洞里,逻辑漏洞占的比例越来越高。早些年大家一窝蜂去挖SQL注入、文件上传,如今这些常规技术漏洞在主流厂商的防御体系下已经很难啃,反而是逻辑漏洞成了提交量最大、通过率最稳的方向。原因很简单:技术漏洞是代码写错了,能靠WAF、RASP这类通用设备挡掉一大半;逻辑漏洞是业务设计错了,安全设备根本看不懂,只能靠人去测、去推理。
这篇文章就围绕“逻辑漏洞”这个方向,结合我实际测试中的案例和踩坑记录,把最常见的几类问题、挖掘思路、修复建议和报告技巧一次讲清楚。无论你是刚接触SRC的新人,还是已经在做红蓝对抗的工程师,这篇内容应该都能给你一些参考。
1. 逻辑漏洞为什么是SRC挖掘的“常青树”
1.1 逻辑漏洞到底是什么
逻辑漏洞,简单说就是程序在业务流程设计上存在缺陷,导致用户在正常情况下不该完成的动作,可以通过篡改请求、调整步骤、伪造身份等方式完成。它不依赖于复杂的注入语法,也不需要多高深的二进制功底,核心是“想清楚业务应该怎么走,再反着想哪一步能跳、哪一步能改”。
举个例子,一个订单系统正常流程是:下单、支付、发货。如果支付成功后修改订单金额,退款时按修改后的金额退,那攻击者就能“花十块退一百”。这背后的根因,就是系统在生成订单后没有锁定金额快照,也没有在退款时重新校验原订单数据。这种问题,代码审计能发现,纯黑盒测试也能通过修改返回包里的金额字段试探出来。
我在实际测试中的体感是,逻辑漏洞的黄金场景都集中在用户核心路径上——注册、登录、找回密码、支付、下单、优惠券、积分、提现、修改资料、查看他人信息。凡是涉及身份、金额、数量、状态这几个关键字的操作,都值得花时间深挖。
1.2 与技术漏洞的本质区别
技术漏洞讲究的是“利用”,逻辑漏洞讲究的是“推导”。SQL注入你需要构造payload、判断闭合方式、绕WAF;逻辑漏洞你通常只需要一个正常的浏览器、一个抓包工具,把参数改一改、把步骤颠倒一下,就能发现问题。
从防御角度看,两者的区别更明显。技术漏洞可以用安全设备做通用防护,但逻辑漏洞是业务代码自己埋的雷,WAF看不见、扫描器扫不出。它需要开发者在设计阶段就充分考虑“恶意用户会怎么操作”这个前提,这对团队的安全意识要求很高。而大多数业务开发同学的习惯是先实现正常流程,很少去模拟异常操作,所以逻辑漏洞几乎不可能被彻底消灭。
这也是为什么SRC平台普遍把逻辑漏洞定为核心关注方向——危害不一定有RCE那么致命,但影响范围广、复现简单、修复成本高,厂商必须认真对待。
1.3 最容易出问题的功能场景
根据我这些年提交的漏洞记录和行业公开案例分析,出问题最频繁的功能场景排在前几名的是:
- 订单/支付流程:金额篡改、数量篡改、支付状态绕过、重复支付、退款逻辑缺陷。
- 用户身份体系:水平越权(查看/修改他人数据)、垂直越权(普通用户执行管理员操作)、会话固定、Token可预测。
- 验证码与短信接口:验证码不失效、验证码可爆破、返回包中直接携带验证码、短信轰炸。
- 密码找回流程:步骤可跳过、验证步骤与重置步骤分离、URL中含可猜测token。
- 优惠券/积分系统:并发领取、负数金额、重复使用、使用条件可绕过。
很多人问我,这些场景听起来都很简单,为什么厂商还会犯?因为业务迭代太快。产品今天上线一个活动,开发连夜写完就发,安全评审根本没有介入。这种“业务先行”的现实情况,恰好给了测试者充足的机会。
2. 高频逻辑漏洞类型逐一拆解
2.1 越权漏洞:用户A操作了用户B的数据
越权是逻辑漏洞里最常见的一类,分为水平越权和垂直越权两种。
水平越权指同级用户之间越权,典型场景是修改URL中的ID就能查看别人的订单、个人信息、地址、发票。比如一个订单详情接口是/api/order/detail?id=1001,用户A登录后把id改成1002,如果服务端没有校验这个订单是否属于当前用户,就直接返回了用户B的订单信息,这就是水平越权。
垂直越权指低权限用户执行高权限操作。典型场景是普通用户在登录状态下直接访问管理端接口,比如/admin/getAllUser,如果后端只依赖前端路由隐藏管理入口、没有做接口级权限校验,普通用户把请求路径改一下就能拉到全量用户数据。
挖掘越权漏洞的思路核心就一句话:找到带身份标识参数的接口,做交叉访问测试。具体来说,我会准备两个测试账号,一个记为A,一个记为B,然后用A的Cookie访问B的资源ID,再反过来用B的Cookie访问A的资源ID。两边都能通,那越权基本就坐实了。
要注意的是,现在很多厂商在详情接口上做了鉴权,但列表接口往往会漏——列表接口返回的每条记录里带的ID,换个用户能不能查看对应详情,很多系统没管。我提交过的好几个高危都是从列表接口切入的。
2.2 验证码机制:形同虚设的“安全防线”
验证码机制设计的初衷是区分人和机器、二次确认身份,但实际实现中翻车率极高。
短信验证码最常见的问题有五个:
- 验证码不绑定手机号,A手机收到的验证码填到B手机也能通过。
- 验证码不设置过期时间,24小时前收到的码还能用。
- 验证码位数太少且无次数限制,四位纯数字组合只有一万种,暴力枚举毫无压力。
- 验证码直接在响应包中返回,抓包就能看到明文。
- 验证码校验放在前端,后端接口根本不校验。
我印象最深的是一个密码重置接口,开发者把验证码校验的逻辑写在JavaScript里,后端直接信任前端传过来的verifyResult=true参数。我用抓包工具把参数一改,本地JS都不用动,密码重置流程就直接走完了。这种问题的根因是开发者对“前端可控、后端可信”这个原则彻底没有概念。
挖掘这类问题,重点抓两个点:一是看验证码校验发生在哪一步——是单纯前端弹窗确认,还是后端有真实校验;二是看验证码生成和校验的接口是否绑定会话或手机号。把这两个点确认清楚,十有八九能有收获。
2.3 支付与交易金额:一分钱买一台手机
支付逻辑漏洞,我愿称之为SRC提交里“最出彩”的一类。危害直观,商业模式一目了然,厂商也最愿意给高评分。
最常见的攻击手法是篡改金额字段。下单请求里通常会带totalPrice、payAmount、price这类参数,如果后端直接信任前端传的金额、不重新计算订单总额,那把金额改成0.01元提交,支付系统就会按0.01元扣款。原理很简单:后端以为前端传的金额就是订单金额,没有用自己的数据源重新计算。
另一种常见思路是修改商品数量或单价。比如一件商品单价100元,数量改成负数,总价反而变成负的,系统在计算优惠或退款时会按这个负数处理,结果就是用户不仅不用付钱,账户还多了钱。这种问题在促销系统里特别多,因为促销计算逻辑复杂,开发写条件判断时容易漏掉负数、零值这些边界。
我在测试这类漏洞时,习惯把请求里的数字字段全部列出来,逐一问自己一个问题:这个值如果改小、改大、改成0、改成负数,后端会不会发现?用这个笨办法,反而挖到过不少别人没注意的盲点。
2.4 密码找回流程:步骤跳转与Token泄露
密码找回是另一个逻辑漏洞高发区,典型原因是找回流程拆成了多个步骤,但每个步骤之间没有状态关联。
正常的找回流程应该是:验证手机号或邮箱、输入验证码、设置新密码。如果第二步的验证码校验和第三部的新密码设置是两个独立的接口,攻击者直接调用第三部接口,传自己的新密码,就跳过了验证码环节。这就是步骤跳转。
另一种情况是重置链接里的Token设计不安全。有的系统用用户ID加时间戳做个简单拼接,甚至直接就是一个可预测的递增数字,攻击者把Token改一改就能重置别人的密码。我在测试中遇到过一个系统,Token就是把用户ID做了个Base64编码,解码后原原本本就是123这种数字,等于裸奔。
测试密码找回流程,我建议用一套固定套路:走一遍正常流程,记录每个步骤的请求和状态;然后跳过第一步直接请求第二步;再试试第一步成功后,隔一段时间再执行第三步;最后看看URL和响应包里有没有泄露Token。这样一套下来,大部分问题都能覆盖到。
2.5 验证顺序与状态修改:先做什么和后做什么真的重要
除了上面几类,还有一批逻辑漏洞出在“业务状态机”上,典型的就是订单状态的跳转没有做到严格校验。
一个正常订单应该按“待支付—已支付—已发货—已完成”的顺序流转。但如果开发者没做好状态校验,用户就能把“已发货”的订单改成“待支付”,或者把“未支付”的订单直接标记成“已支付”。我在测试一个电商系统时发现,订单取消接口只校验了订单归属,没校验当前状态,导致我在“已发货”状态下取消订单,系统竟然把全额退款给了过来。
再比如活动系统,正常逻辑是活动开始后才能参与、活动结束后不能领取奖励。但有些实现只在前端做了时间判断,后端接口不校验活动时间窗口,测试时把系统时间改一下、或者直接构造一个活动结束后的请求,奖励照样能领。
这类问题从防御角度反而好修,核心就是在后端每个状态流转节点上增加前置状态校验,不满足条件直接返回异常。但从测试角度,需要你具备“业务流程建模”的思维,把每个操作的前置条件、后置结果都列出来,再逐个打破。
3. 从功能梳理到实战挖掘的正确思路
3.1 第一步:先给目标做“功能地图”
很多新人拿到目标就开始瞎点,点半天没什么收获。我的经验是反着来,先梳理清楚目标系统有哪些功能模块,再给每个模块标出涉及“身份、金额、状态、数量”的接口。
我会开一个文档,按模块分类记录:登录注册模块有哪些接口、订单模块有哪些接口、个人中心有哪些接口。每个接口标注请求方法、参数、是否需要登录、返回什么关键字段。这个过程看起来费时间,但逻辑漏洞的挖掘非常依赖对系统功能的整体认知——你不知道系统有哪些功能,就不知道哪些逻辑值得测。
举个例子,你花十分钟把接口列表整理完,发现购物车模块有一个“清空购物车”接口,传参是购物车ID,你就应该立刻想到:这个ID能不能改成别人的?改完能不能看到别人的购物车内容?一个高危越权漏洞可能就藏在这里。
3.2 第二步:请求与响应交互的细致观察
测逻辑漏洞,抓包工具是标配。我用得最多的是浏览器开发者工具配合抓包代理,拦截HTTPS流量看请求参数和响应内容。
看请求时重点盯三样东西:身份标识(Cookie、Token、UserID参数)、业务参数(金额、数量、状态码)、操作标识(action、type、step)。看响应时重点盯三样东西:接口返回的错误码、响应体里是否有敏感信息泄露、字段值是否能被前端参考。
有一次我在测一个资讯类App,看到评论接口返回的响应体里除了评论内容,还带了一个user_id字段,我没多想,顺手改成另一个数字,结果接口直接把那个用户的手机号返回来了。这就是典型的响应体敏感信息泄露加水平越权组合拳。
3.3 第三步:正向业务链路的异常推导
把正常的业务链路走通之后,我会在脑子里做一组反事实推导:如果这里多调用一次会怎样、如果不调用会怎样、如果换个顺序会怎样、如果并发调用会怎样。
这四种“会怎样”,对应了四类逻辑漏洞:重复操作导致的重复领取、缺失操作导致的校验绕过、乱序操作导致的状态异常、并发操作导致的资源竞争。我提交过一个并发领取优惠券的漏洞,就是用脚本同时发了20个领取请求,系统没有做幂等控制,用户一次性领了20张同面额优惠券,规则里明确限定只能领一张。
这种推导能力是可以训练的。平时看到一个功能,先别急着点,问自己“开发者默认用户不会做什么”,然后专门去干那件事。大多数逻辑漏洞,本质都是做了开发者默认“用户不会做”的操作。
3.4 第四步:守住授权测试的边界意识
挖掘逻辑漏洞这件事,一定要在授权范围内进行。SRC平台的授权边界通常写得很清楚,哪些域名属于测试范围、哪些操作不允许做(比如不允许拖库、不允许大规模遍历数据),都有明文规定。
我的原则是:只在你拥有合法测试权限的平台上挖洞,只使用最小必要的数据量做验证,发现敏感数据后立即停止并打码截图。这既是合规底线,也是保护自己的方式。
4. 修复建议与开发侧防线建设
4.1 服务端必须承担全部校验责任
修复逻辑漏洞的第一原则,是把所有关键校验放到服务端。前端传上来的任何参数都不可信,服务端必须基于自身存储的数据做二次校验。
具体到支付场景,服务端应该根据订单号从数据库重新计算应付金额,而不是直接信任前端传来的payAmount。如果校验结果与前端参数不一致,应该直接拒绝支付并告警。我在复盘一些高危漏洞时发现,很多问题的根因就是“前端传什么,后端收什么”,完全没有二次计算的环节。
4.2 统一鉴权与对象级权限检查
越权漏洞的修复方案,核心是建立统一的鉴权中间件,并且在每次访问具体资源时做对象级权限检查。对象级权限检查的意思是,在代码里明确“这个订单属于当前用户吗”,而不是笼统地通过登录状态就放行所有操作。
Java项目中常见的做法是在Controller层加一个当前用户的注入,Service层查数据时强制带上userId条件,而不是先查数据再判断归属。这样做的好处是,数据库查询层面就限定了数据范围,即使接口被猜测到,也拿不到别人的数据。
4.3 关键令牌的随机性与会话绑定
验证码、Token、重置链接这类关键凭据,必须使用高熵随机数生成,并且绑定到具体的会话或用户。具体来说:
- 验证码必须绑定手机号或邮箱、设置过期时间、限制错误尝试次数。
- 重置Token必须用加密安全的随机数,不能使用可预测的用户ID拼接。
- 重要操作(修改密码、绑定手机、提现)前必须让用户重新输入密码或验证码做二次确认。
- 登录后的会话标识要设置合理的过期时间,避免长期有效带来会话劫持风险。
4.4 把安全测试嵌入开发流程而不是最后补救
作为参与过不少安全建设的人,我最大的体会是:逻辑漏洞的修复成本,和发现时机几乎成正比。上线前发现,改代码就行;上线后用户发现,要紧急修复加公告;被攻击者利用后再发现,就是安全事故。
所以在开发流程里,我强烈建议在每个迭代结束后做一次“恶意用例评审”,让产品经理、开发、测试一起坐下来,列出本次迭代涉及的所有业务规则,然后逐条问:如果用户不按这个规则走,系统会怎样?这个问题,比任何安全扫描器都管用。
5. SRC漏洞报告与沟通经验分享
5.1 漏洞报告怎么写才更容易通过审核
很多同行技术能力不差,但报告写得一塌糊涂,导致被驳回或者降级处理。逻辑漏洞尤其讲究“讲清楚业务影响”,审核员如果看不懂你的攻击路径,很容易低估危害。
我写报告的习惯是六段式结构:漏洞标题、漏洞等级、漏洞地址、漏洞描述、复现步骤、影响说明。
标题要直接点出问题类型,比如“订单金额篡改导致0元购”就比“订单模块存在逻辑问题”清晰得多。复现步骤要逐条写明,每一步配上请求和响应的截图,关键参数用红框标注。影响说明要站在业务角度描述:攻击者能做到什么程度、影响哪些用户、是否涉及资金损失。
5.2 复现步骤与影响分析的进阶技巧
在写复现步骤时,我总会额外做两件事:一是给出最小化复现路径,把无关操作全部去掉,让审核员能一步到位看到问题;二是提供修复建议,哪怕只有一句话“建议在服务端重新校验订单金额”。
不要小看修复建议这一行字。我观察到的现象是,同一等级的漏洞,附带了清晰修复建议的报告,通过率和评级往往更高。原因很现实:审核员也是人,你帮他省了分析问题的时间,他对漏洞价值的判断自然会更高。
5.3 常见被驳回原因与现实思考
逻辑漏洞报告被驳回,最常见的三个原因:
- 复现不完整,审核员照着步骤走不通。
- 危害描述夸大,比如把一处鸡肋的逻辑瑕疵描述成“可导致任意用户数据泄露”,实际影响面没那么大。
- 超出SRC授权范围,测试对象不在收录范围里。
第三条特别值得说明。很多新人看到目标系统里有一个站外链接,顺手就测了,结果发现问题在厂商的兄弟公司域名上,直接属于超范围测试,不但漏洞不被收录,还可能被平台警告。我自己的经验是:动手测试前,先把SRC页面规则读三遍,把授权资产范围、禁止操作清单都记下来,宁可少测一个功能,也不要去碰规则之外的东西。
5.4 一点个人心得
做逻辑漏洞挖掘这几年,我越来越觉得它像是“业务侦探”的工作。你不需要背很多漏洞利用代码,但你需要对业务流程保持敏感,对每一个“好像不太对”的瞬间保持好奇。
踩过的坑越多,越会发现一个规律:几乎所有逻辑漏洞,都源于开发者对用户行为做了一个过于美好的假设。产品经理说“用户不会这么操作”,开发说“没人会抓包改参数”,于是那些“没人会做”的操作,就成了漏洞的藏身之处。
反过来说,挖掘逻辑漏洞的核心竞争力,恰恰是“站在开发者的反面想问题”。别人默认用户是善良的,你就默认用户是恶意的;别人觉得流程理所当然,你就想哪一步能跳过、哪个参数能篡改、哪条逻辑能反转。这个思维习惯一旦养成,你就会发现,逻辑漏洞根本挖不完——因为只要业务还在迭代,只要开发者还在替用户做假设,就一定会有新的问题冒出来。
最后分享一个实测有效的技巧:每次提交一个逻辑漏洞后,不要急着删除测试记录。把攻击的思路、参数改动的点位、后端校验缺失的原因写成一个短笔记,积累成自己的“逻辑漏洞模式库”。下次拿到新目标时,对照着这个库去检查,效率会高很多。我个人就是用这个办法,把很多原本需要现场摸索的功能点,变成了一进去就能定位的“送分题”。