我把“待审批”改成了“已通过”:审批流状态参数篡改实战,一张请假条打穿整个OA
原创
逍遥
逍遥
昆仑AI安全实验室
2026年8月16日 09:53
广东
在小说阅读器读本章
去阅读
上个月测一套企业OA系统,目标是一个大型集团的内部办公平台。这系统界面做得挺唬人,审批流、会签、驳回、转交一应俱全,看着比银行还严。结果我在一个请假申请的功能里,抓了个包,把请求体里的\"status\":\"pending\"改成了\"status\":\"approved\",放行。页面刷新后,我的请假申请状态直接变成了“已通过”,部门经理和HR的名字赫然挂在审批记录里——而我连部门经理是谁都还没搞清楚。
就这样,一张伪造的请假条,让我摸到了整个审批流的命门。今天把这套审批流绕过的完整思路、技术细节、挖掘方法和防御方案全部公开。
一、审批流的本质:一道谁都能改的“状态门”
审批流的本质,就是一个状态机。一个标准的多级审批流程,它的状态流转大致是这样的:
待提交 → 待审批 → 已通过/已驳回 → 已归档
每一步流转,都应该有一个服务端控制的动作来触发:员工提交申请,系统把状态从“待提交”改为“待审批”;经理点击“同意”,系统校验经理的权限和角色,再把状态从“待审批”改为“已通过”。
问题出在哪?问题出在很多系统的状态流转不是由服务端动作触发的,而是由前端传给后端的参数直接控制的。
比如一个典型的请假申请接口,请求体可能是这样的:
{ \"leave_id\": \"20260816001\", \"applicant\": \"zhangsan\", \"start_date\": \"2026-08-20\", \"end_date\": \"2026-08-22\", \"status\": \"pending\", \"approver\": null, \"approve_time\": null}
如果前端在提交时可以直接传\"status\":\"approved\",而服务端直接把这个字段写进数据库,那这个审批流就等于没有。更微妙的是,很多系统在“提交”和“审批”两个动作之间,使用了同一个接口,只是请求体不同。攻击者可以用提交接口传一个“已通过”的状态,直接绕过所有人的审批。
二、实战:从一个请假单到整个审批系统的陷落
以下是一个脱敏的真实案例。
目标系统:某制造业集团的内部OA,使用Spring Boot + Vue开发,审批流覆盖请假、报销、采购、合同等十几个业务场景。
第一步:侦察审批接口
我注册了一个普通员工账号,进入请假申请页面,正常填写并提交了一张请假单。Burp Suite抓到了提交请求:
POST /api/v1/leave/apply HTTP/1.1Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...Content-Type: application/json
{ \"leaveType\": \"annual\", \"startDate\": \"2026-08-20\", \"endDate\": \"2026-08-22\", \"reason\": \"personal\", \"status\": \"pending\"}
响应返回{\"code\":200,\"data\":{\"leaveId\":\"LEAVE20260816001\"}}。
第二步:测试状态参数是否可控
我重新提交了一次,把请求体中的\"status\"改为\"approved\",并在请求体中添加了审批人字段:
{ \"leaveType\": \"annual\", \"startDate\": \"2026-08-20\", \"endDate\": \"2026-08-22\", \"reason\": \"personal\", \"status\": \"approved\", \"approver\": \"wangwu\", \"approveTime\": \"2026-08-16 15:30:00\"}
后端返回200,并且我的请假单详情页直接显示“已通过”,审批人是wangwu(他的身份是部门经理)。系统没有校验我的权限,也没有校验approver是否真的做了审批操作。
第三步:测试其他业务场景
既然请假单能绕过,报销单、采购单、合同审批能不能?我逐一测试:
- 报销单:把
\"status\":\"pending\"改成\"status\":\"approved\",报销单直接进入财务打款状态。 - 采购单:同样手法,采购申请直接变成“已批准”,金额20万的设备采购单直接生效。
- 合同审批:把状态改成
\"approved\",合同直接进入盖章流程。
全部成功。这个审批系统就是一个筛子,所有业务场景的状态参数都能被前端直接控制。
第四步:获取管理员权限
我继续深入,发现了一个更危险的问题。在审批流里,有一个“管理员待办”接口GET /api/v1/admin/todo/list,它返回所有待审批的高权限单据。而审批接口POST /api/v1/approval/decide的逻辑是:
{ \"flow_id\": \"FLOW20260816001\", \"decision\": \"approved\", \"comment\": \"同意\"}
这个接口同样不校验当前用户是否拥有审批权限。我直接对一张采购单的审批流调用了这个接口,传了\"decision\":\"approved\"。采购单通过。我又尝试对一个“系统管理员权限申请”的审批单进行操作,同样通过。这意味着我可以自己批准自己的任何申请,包括提权申请。
最后我甚至发现,审批流中的“审批记录”字段也完全由前端控制。我可以伪造审批人、审批时间、审批意见。整个审批系统不仅被绕过,还被完全操控。
三、审批流绕过的四种典型技术形态
在2026年的业务系统里,审批流绕过已衍生出多种形态,我总结为四种最常见的技术模式:
形态一:状态参数直接篡改。 这是最基础的情况,就像上面的案例。前端请求体里直接包含status或state字段,服务端不做校验直接入库。测试时重点看提交接口和更新接口。
形态二:审批动作接口缺少权限校验。 这种情况里,状态流转有独立的接口(比如/approve、/decide),但接口不校验当前用户是否具有审批权限。攻击者可以自己批准自己的单子,或者批准别人的单子。
形态三:工作流引擎配置不当。 在Flowable、Activiti等BPM引擎驱动的系统里,审批流的节点跳转是由引擎控制的。但如果开发者在后端代码里手动调用了runtimeService.setVariable()来设置状态变量,而变量名和前端传的参数同名,就可能被覆盖。
形态四:状态枚举值与数据库实际值不一致。 前端展示的状态和数据库存储的状态不一致的情况也很常见。比如前端显示“已通过”,数据库里是1;前端显示“待审批”,数据库里是0。攻击者可以构造一个前端不存在的状态值(比如99或-1),导致系统进入未定义状态,某些情况下会触发“默认通过”的逻辑。
四、挖掘审批流漏洞的系统化方法
如果你想在SRC里挖这类漏洞,下面是我的完整流程:
第一步:枚举所有审批场景。 请假、报销、采购、合同、用章、权限申请……每一个场景的接口都要摸清楚。
第二步:抓包分析状态流转。 对每个审批流程,记录从提交到审批到归档的所有请求。重点看:状态字段是谁传的?审批接口的权限校验在哪一层?
第三步:逐一测试状态篡改。 对每个状态字段,尝试修改为其他状态值。比如提交时改成approved,审批时改成rejected,归档时改成pending。观察系统是否接受非法流转。
第四步:测试审批接口的水平越权。 用普通员工账号调用审批接口,尝试审批自己或他人的单据。重点关注那些“审批动作独立接口”的场景。
第五步:组合利用。 审批流绕过本身可能只是中危,但和其他漏洞组合就能升危。比如:
- 审批绕过 + 报销金额篡改 = 直接骗钱
- 审批绕过 + 采购单金额篡改 = 伪造采购单骗货
- 审批绕过 + 文件上传 = 上传WebShell
- 审批绕过 + 管理员权限申请 = 提权
第六步:检查状态值是否可预测。 如果系统使用数字枚举状态(0/1/2/3),尝试传负数、超大数、或者不在枚举范围内的值,看系统如何处理。
五、防御方案
服务端强制状态机。 状态流转必须由服务端动作触发,不能由客户端直接设置。具体实现方式:
- 前端只传业务数据(请假类型、日期、原因),不传状态字段。
- 服务端根据当前单据状态和操作者权限,计算下一个状态。
- 状态流转逻辑用枚举控制,不能接受任意的字符串或数字。
审批接口严格鉴权。 每个审批动作必须校验:
- 当前用户是否是审批流程中的合法审批人。
- 当前用户是否拥有该审批节点的审批权限(角色、部门、层级)。
- 单据当前状态是否允许该审批动作。
审批记录服务端生成。 审批人、审批时间、审批意见这些字段必须由服务端从会话中获取,不能由前端传入。
异常状态告警。 监控审批流中的非法状态流转,比如跳级审批、未经审批直接通过、状态回退等,发现异常立即告警。
工作流引擎安全配置。 使用BPM引擎时,确保引擎的权限模型与业务权限模型一致。不要在代码里硬编码状态变量赋值,尽量使用引擎提供的审批API。
六、写在最后
审批流是企业的核心控制机制,它决定了谁能花公司的钱、谁能签公司的合同、谁能进公司的门。一旦审批流被绕过,攻击者就不是在“入侵系统”了,而是在“成为系统的一部分”——他以合法用户的身份,用系统的正常功能,做出违背企业规则的操作。这种攻击最可怕的地方在于,它不会触发任何安全告警,因为一切都是“合规”的。
下次测业务系统,别只盯着SQL注入和XSS。找到那个“提交申请”的按钮,看看申请单的状态字段是不是你能控制的。如果是,那你离公司的钱袋子就不远了。
严正声明
本文所述技术仅用于合法授权的安全测试。所有案例均已脱敏处理,仅保留技术原理供学习参考。利用审批流漏洞进行未授权操作属于违法行为,与作者无关。请在SRC平台授权范围内进行测试,遵守法律法规。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论