SRC众测实战:从业务逻辑漏洞到IDOR敏感信息泄露的完整挖掘链
1. 项目概述:一次典型的SRC众测实战复盘
最近在参与某次众测活动时,遇到了一个挺有意思的案例。目标是一个典型的Web应用,表面上看注册流程做得挺“严谨”,有图形验证码、手机号绑定、甚至还有邀请码机制。但越是看起来固若金汤的地方,往往越藏着意想不到的缝隙。这次的任务,就是从一个看似正常的注册入口开始,最终不仅绕过了限制成功注册了本不该存在的用户,还顺藤摸瓜发现了一处可能导致敏感信息泄露的接口。整个过程没有用到什么高精尖的0day,更多的是对业务逻辑的细致观察、对请求响应的反复揣摩,以及那么一点“如果我是开发者,我可能会犯什么错”的换位思考。如果你也对SRC漏洞挖掘、业务逻辑漏洞实战感兴趣,或者想了解如何从普通功能点切入深挖,那这次经历或许能给你一些启发。
2. 核心思路拆解:从“限制”本身寻找突破口
2.1 目标分析与攻击面梳理
接到目标后,我并没有急于上手测试。第一步永远是信息收集和攻击面梳理。这是一个提供在线服务的平台,注册是核心入口之一。前期的侦察发现,其注册页面包含以下几个关键控制点:
- 图形验证码:用于防止机器批量注册。
- 手机短信验证码:绑定实名信息,确保用户唯一性。
- 邀请码字段(非必填):暗示可能存在特殊的内部注册渠道。
- 用户角色/类型选择:注册时需选择“普通用户”或“企业用户”。
我的初步判断是,这套组合拳的目的很明确:精准控制用户来源和类型,防止无关人员或恶意用户注册。那么,我的测试思路也就围绕“如何成为系统未预期的用户”展开。重点不在于爆破验证码(那是体力活),而在于理解其业务规则和权限模型。例如,那个“邀请码”字段,就是第一个值得玩味的地方。它是真的只是个摆设,还是通往某个特权世界的钥匙?
2.2 绕过限制的通用性思考框架
在Web安全测试中,“绕过限制”通常不是指正面攻克加密算法,而是寻找逻辑上的不一致或缺失。对于注册场景,我们可以建立一个简单的思考框架:
- 客户端限制 vs 服务端校验:页面上有下拉框只能选A和B,是否意味着服务端也只接受A和B?如果我直接发包传一个C呢?
- 多步骤流程的完整性校验:注册分三步,第一步验证手机号,第二步填写资料,第三步完成。系统是否在每一步都重新验证了前序步骤的令牌(token)或状态?我能否跳过第二步直接访问第三步的接口?
- 参数可控性与边界处理:那些传入后端的参数,哪些是用户完全可控的?比如用户ID、角色字段。当传入一个负数、一个超长字符串、一个系统不存在的枚举值时,后端是报错、忽略,还是产生非预期行为?
- 平行权限测试:如果注册普通用户走的是流程A,注册企业用户走的是流程B。那么,用流程A的请求,尝试带上流程B的参数,会发生什么?系统是否能清晰地分辨请求的“意图”?
带着这些疑问,我开始了具体的测试。
3. 实操过程:步步为营的漏洞挖掘
3.1 第一步:常规流程探查与请求分析
首先,我使用一个合法的手机号,完全按照正常流程注册了一个“普通用户”。这个过程必须做,而且要仔细做。我使用Burp Suite作为代理,捕获了整个注册流程的HTTP请求序列。
关键发现如下:
- 请求1:获取图形验证码。
GET /api/captcha/image?t=<timestamp>。这里注意验证码是否与session或token绑定,后续提交时是否校验。 - 请求2:发送短信验证码。
POST /api/sms/send, 参数为phone=13800138000&captcha=abcd。这里观察响应,除了成功/失败,是否返回了其他信息,如用户是否存在、邀请码状态等。 - 请求3:提交注册。
POST /api/user/register。这是核心请求,捕获到的参数类似:
响应成功,返回了用户ID、token等信息。{ "phone": "13800138000", "smsCode": "123456", "password": "Passw0rd!", "userType": "normal", "inviteCode": "", "nickname": "testuser" }
初步分析:流程看似完整。但我的注意力立刻被userType: "normal"和inviteCode: ""吸引了。这是两个明确的可控输入点。
3.2 第二步:关键参数篡改与模糊测试
接下来,就是针对核心注册请求进行篡改测试。我使用Burp的Repeater模块,对刚才捕获的请求进行重放和修改。
测试1:用户类型(userType)篡改将"userType": "normal"依次修改为:
"enterprise"(页面上存在的另一个选项)"admin"、"superuser"、"manager"(猜测可能存在的管理角色)"null"、""(空值)"normal'--"(尝试SQL注入)
结果:当修改为"userType": "enterprise"时,请求竟然成功了!系统返回了注册成功的响应,并且用户信息里显示"role": "enterprise"。这意味着,前端虽然对普通用户隐藏了企业用户的选项,但后端接口并未对用户传入的userType值进行严格校验,只要是一个它“认识”的类型(比如在数据库枚举值里存在),它就照单全收。
注意:这里成功的前提是,其他参数(尤其是短信验证码)必须有效。这说明后端对业务连续性校验(手机号-验证码绑定)是有的,但对业务逻辑校验(谁有资格注册为企业用户)是缺失的。
测试2:邀请码(inviteCode)探测虽然注册普通用户时邀请码为空,但系统设计了此字段,必然有用。我尝试:
- 常见弱口令:
000000,111111,123456,invite。 - 构造规律值:根据已注册的用户ID(比如10001),尝试
"inviteCode": "10001"或"REF10001"。 - 暴力枚举(谨慎):如果邀请码是数字,写一个简单的脚本,在较低速率下尝试一个较小范围(如1000-2000)。务必注意众测规则,避免对系统造成压力。
结果:在尝试到"inviteCode": "888888"时,注册请求的响应出现了变化!虽然依旧是注册成功,但返回的用户权限对象中,多出了一个"vipLevel": 1的字段。而普通注册的用户没有这个字段。这说明,邀请码是一个“特权码”,可以开通额外的服务或身份。后端逻辑可能是:有有效邀请码 -> 赋予VIP身份;无邀请码 -> 普通身份。但这里同样没有校验“谁”有资格使用这个邀请码。
3.3 第三步:组合漏洞利用与敏感信息泄露发现
至此,我已经可以绕过限制注册一个具有“企业”角色或“VIP”身份的用户了。但这还不够,我需要验证这些“非正常”身份是否带来了新的安全风险。
我以新注册的“企业用户”身份登录系统。登录后,开始遍历前端功能点和抓取API请求。在查看“企业资料”或“我的项目”相关页面时,Burp捕获到了一个关键的API请求:GET /api/enterprise/projects?page=1&size=10
这个请求返回了一个项目列表。仔细查看其中一个项目的详细数据,发现了一个问题:
{ "projectId": "PROJ2024001", "projectName": "内部测试项目A", "creatorId": "10086", "creatorName": "张经理", "**memberList**": [ {"userId": "10010", "userName": "李四", "phone": "138****1111", "email": "li@example.com"}, {"userId": "10011", "userName": "王五", "phone": "139****2222", "email": "wang@example.com"} ], "attachmentList": [ {"fileId": "FILE001", "fileName": "设计方案.pdf", "**downloadUrl**": "/api/file/download/FILE001?token=eyJhbGciOiJ..."} ] }漏洞点出现了:
- 敏感信息过度暴露:
memberList中直接返回了其他成员的手机号(虽然打了码,但有时可能不打码)和邮箱。这是典型的敏感信息泄露。作为企业用户,我可能有权看到项目成员,但不应直接获取他们的个人联系信息。 - 文件下载权限控制缺失(更严重的发现):
downloadUrl中的链接指向一个文件下载接口,并附带了一个token。我尝试直接访问这个URL(/api/file/download/FILE001?token=eyJhbGciOiJ...),竟然成功下载了“设计方案.pdf”文件。然后,我尝试修改fileId参数,比如改成FILE002,再次访问,同样成功下载了另一个不属于我项目的文件!
漏洞原理分析:这里存在一个**不安全的直接对象引用(IDOR)**漏洞。文件下载接口/api/file/download/{fileId}虽然使用了token,但这个token很可能只是一个会话标识或通用令牌,并未在服务端严格校验当前登录用户是否有权限下载fileId对应的文件。系统错误地认为:“只要你有有效的登录token,并且知道文件的ID,你就可以下载它”。这导致了越权访问和敏感文件泄露。
4. 漏洞原理与深度解析
4.1 业务逻辑漏洞:信任边界混淆
本次发现的注册绕过,核心是业务逻辑漏洞。系统在设计时,混淆了“身份验证”、“权限校验”和“业务规则校验”的边界。
- 身份验证:证明你是你(密码、短信验证码)。这部分目标系统做得不错。
- 权限校验:决定你能做什么(RBAC模型)。这部分开始出现裂缝,例如文件下载接口的缺失。
- 业务规则校验:在特定业务流程中,附加的条件(如:只有市场部员工才能提交报销;只有获得邀请码的用户才能注册为VIP)。这部分校验被严重依赖前端,后端几乎缺失。
开发者可能认为:“前端下拉框只有‘普通’和‘企业’,用户只能选这两个,所以后端拿到的一定是这两个值之一。” 这种将安全依赖于客户端不可控输入的想法,是逻辑漏洞的温床。攻击者根本不用浏览器,直接伪造HTTP请求,规则就被轻易绕过。
4.2 敏感信息泄露:权限最小化原则的失效
敏感信息泄露(如成员手机号、邮箱)和IDOR漏洞,共同违反了信息安全中的权限最小化原则和访问控制原则。
- 权限最小化:接口应只返回完成当前操作所必需的最少信息。列表页显示成员姓名即可,详情页再根据权限决定是否展示联系方式。一股脑全返回,是偷懒也是风险。
- 访问控制:每个访问受保护资源的请求,都必须经过授权检查。在文件下载的例子中,服务端代码缺失了关键一步:
这个校验必须放在服务端,基于当前会话用户(// 错误示例:缺失权限校验 File file = fileService.getById(fileId); return download(file); // 正确示例:增加权限校验 File file = fileService.getById(fileId); Project project = projectService.getByFileId(fileId); // 检查当前登录用户是否属于该项目 if (!project.getMembers().contains(currentUser)) { throw new AccessDeniedException("无权访问此文件"); } return download(file);currentUser)和要访问的资源(file或project)进行关联性判断。
4.3 漏洞链的形成:从入口点到核心资产
单独看,每个漏洞可能危害等级不同:
- 绕过注册限制:中危。可能造成垃圾账号、占用资源。
- 敏感信息泄露:中危/高危。侵犯用户隐私,可能为社工攻击提供素材。
- 文件越权下载(IDOR):高危。直接导致核心业务数据(设计文档、合同、报告)泄露。
但当它们形成链条时,危害急剧放大:
- 攻击者通过逻辑漏洞,绕过限制注册了一个高权限(企业/VIP)账号。
- 利用这个账号,访问到本不应访问的功能模块(企业项目列表)。
- 在该模块中,发现敏感信息泄露和存在IDOR漏洞的接口。
- 通过IDOR漏洞,直接窃取系统内的敏感文件。
这个链条清晰地展示了:一个不显眼的入口点漏洞(注册逻辑缺陷),如何成为攻击者打入系统内部、获取高价值资产的跳板。
5. 修复建议与安全开发思考
5.1 针对性修复方案
注册逻辑修复:
- 服务端强校验:在后端注册接口,对
userType、inviteCode等参数进行白名单校验。不仅校验值是否合法,更要校验当前请求上下文(如:注册IP、来源、是否经过特定前置流程)是否有权使用该值。 - 业务状态机:将注册流程状态化。每一步完成后,在服务端Session或数据库中标记状态。下一步请求必须验证上一步状态已完成,防止跳步或乱序提交。
- 邀请码机制:邀请码应具备唯一性、时效性、绑定关系(如绑定给某个发起人)。使用后立即失效,并在使用校验时,核对发起人与使用者的关系(如果需要)。
- 服务端强校验:在后端注册接口,对
敏感信息与IDOR漏洞修复:
- 接口数据脱敏:列表类接口严格遵守最小化原则。个人联系方式、身份证号等敏感字段,除非必要,否则一律脱敏(如
138****1111)或不返回。 - 强制访问控制:在所有涉及资源ID(
fileId,projectId,userId)的增删改查接口中,加入权限校验层。可以使用Spring Security、Shiro等框架的注解,或在业务逻辑代码中显式编写校验逻辑。核心原则:永远不要相信客户端传来的资源ID,服务端必须二次确认权限。 - 使用不可预测的标识符:避免使用自增ID(1,2,3...)作为资源标识符暴露给前端。可以使用UUID、雪花算法ID等,增加攻击者猜测的难度。
- 接口数据脱敏:列表类接口严格遵守最小化原则。个人联系方式、身份证号等敏感字段,除非必要,否则一律脱敏(如
5.2 对开发与测试的启示
- 对开发者而言:必须树立“服务端是唯一可信方”的理念。所有来自客户端的输入都是不可信的,所有业务规则必须在服务端得到最终执行和校验。设计API时,要像审问者一样思考:“调用这个接口,我需要知道他的所有信息吗?我如何确保他只能做他被允许的事?”
- 对测试者而言:挖掘这类漏洞,需要具备“业务视角”。不要只盯着SQL注入、XSS这些传统漏洞。多问几个“为什么”:为什么这个字段在这里?为什么这个流程要这样设计?如果我不按它的来,它会怎么样?工具(Burp)帮你看到流量,但思考帮你看到漏洞。重点关注:
- 所有传入参数:特别是那些代表状态、身份、权限的枚举值。
- 所有步骤流程:尝试跳过、回退、并发提交。
- 所有返回数据:仔细查看每一个JSON字段,思考哪些信息是不应该给我的。
6. 常见问题与排查技巧实录
在挖掘和验证这类漏洞时,经常会遇到一些困惑和障碍。这里分享几个实战中的技巧:
问题1:修改参数后,请求返回“参数错误”或“验证失败”,如何判断是前端还是后端校验?
- 技巧:使用Burp Repeater,完全重放浏览器捕获的原始请求(包括所有Header,尤其是Cookie、Token、Content-Type)。如果原始请求能成功,你只修改了Body里的一个参数就失败,那基本是后端校验。如果请求根本无法从Repeater发出(比如缺少某个前端生成的加密参数),那可能是前端校验或复杂签名。此时可以尝试用浏览器调试工具,跟踪这个参数是如何生成的。
问题2:发现了疑似越权的接口(如/api/admin/listAllUsers),但直接访问返回403,怎么办?
- 技巧:403不代表没漏洞,只代表你当前的身份没权限。尝试:
- 降级访问:把
/admin/路径改成/user/,或者把listAllUsers改成listMyUsers,看看是否能访问到本应属于自己的数据,这可能是水平越权。 - 参数污染:在原有正常请求的参数基础上,额外添加管理员接口的参数,看看是否会执行部分管理功能。
- 关注Referer和Origin:有些接口会校验请求来源。在Repeater中尝试添加或修改
Referer: https://target.com/admin/这样的Header。
- 降级访问:把
问题3:如何高效地测试IDOR漏洞?
- 技巧:不要盲目爆破。先收集资源ID。
- 从自身数据入手:登录后,查看“我的项目”、“我的订单”,记下属于你的资源ID(如 projectId: 1001, orderId: 2001)。
- 进行加减遍历:尝试访问
projectId=1000和projectId=1002。如果系统使用自增ID,这很可能发现属于其他用户的资源。 - 观察ID格式:如果是UUID(如
550e8400-e29b-41d4-a716-446655440000),爆破难度极大,但可以检查系统在分享功能中生成的URL是否使用了可预测的短ID或弱hash。 - 使用Burp的“Compare”功能:分别抓取查看自己资源A和(猜测的)他人资源B的响应包,用Compare功能高亮差异,能快速发现数据层面的不同。
问题4:在众测中,如何避免测试行为被误判为攻击?
- 核心原则:最小化、慢速化、可解释化。
- 最小化:只修改必要的参数进行验证。例如,证明IDOR时,下载1-2个非所属文件即可,不要批量下载。
- 慢速化:避免使用自动化工具进行高频请求。手动操作,间隔时间放长。
- 可解释化:在提交报告时,清晰说明测试步骤、使用的测试账号(可标注为
test_vul_1)、请求和响应截图。让安全运营人员能清晰复现,理解这是安全测试而非恶意攻击。
这次众测经历再次证明,坚固的系统往往不是被复杂的攻击技术攻破,而是败给了自身逻辑上的疏忽。安全是一个整体,任何一个环节的信任缺失或校验遗漏,都可能被串联起来,形成致命的突破口。对于防御方,需要在每一个业务决策点都加入安全思考;对于攻击方(白帽子),则需要用系统的、怀疑的眼光去审视每一个交互细节。挖洞的过程,其实就是与开发者进行一场跨越时空的思维博弈,乐趣与挑战,皆在于此。