巅峰极客2021的MedicalApp,光看题名就有一股"出题人想让你演一场医疗系统攻防"的味道。医疗应用现在是个大靶子,从挂号到病历到检查报告上传,随便挑一个功能点都是漏洞的天然温床。我当时拿到这题的第一反应不是急着到处点,而是先把攻防思路在脑子里理了一遍:这类题通常把权限边界藏在业务流程里,光扫目录扫不出花来。所以这篇就把我当时从信息收集到最终拿下Flag的完整链路拆开写,顺便聊聊MedicalApp这种题型背后通用的方法论,是想入坑CTF Web方向的朋友值得反复看的一篇。
1. 题面拆解与最初的指纹探测
1.1 从"MedicalApp"三个单词能读出什么
CTF题目命名其实很有讲究。MedicalApp这个命名方式属于"功能直白型"——直接告诉你这是一个医疗场景的Web应用。但"直白"不等于"简单",医疗应用天然包含几个极其耐打的业务模块:
- 患者注册、登录、个人信息维护
- 门诊挂号与预约排班
- 病历、诊断报告、检验报告的查看与下载
- 医生端/管理员端的高权限操作入口
- 检查图像或文档的上传通道
这种模块划分意味着什么?意味着题目可以从任意一条业务链上做手脚。出题人能埋SQL注入,能埋越权(IDOR),能埋文件上传绕过,也能在业务逻辑上搞事,比如验证码复用、越权改数据、水平提权。所以拿到这类名字的第一时间,我脑子里拉出了一张"医疗功能点VS常见漏洞"的对照表,后面所有操作都在按这张表做排除法。
1.2 打开题目的第一波探测动作
我习惯先看一眼HTTP响应头,这一步能省掉后面很多瞎猜。用浏览器开发者工具或者直接curl -I就能拿到基本信息,重点看Server头、Set-Cookie的特征、响应头里是否暴露框架名。如果Server显示的是nginx或者Apache,参考价值有限,但Cookie名字往往能暴露后端语言——比如PHPSESSID说明是PHP,JSESSIONID大概率是Java系,connect.sid就是Node.js的Express框架。此外响应头里的X-Powered-By也值得注意,很多题目环境忘了关它,直接告诉你框架版本。
再就是robots.txt、常见目录扫描、还有页面源码注释。CTF Web题里,源码注释是个被低估的信息源。开发者经常在注释里留调试信息、TODO清单甚至残留的账号格式。MedicalApp这种带真实业务场景的题目,页面上很可能有测试账号提示或者被注释掉的链接。我那次扫下来,发现应用根目录下静态资源路径有点不规范,部分接口路径不像REST风格,反而像是PHP的老式?module=xxx&action=xxx写法,这基本就是往PHP方向跑了。
1.3 确定技术栈后,攻击面开始收窄
当后端语言和框架能被大致判断出来后,后续的打法就完全不一样了。打个比方,如果是PHP,那重点想想文件包含、反序列化、上传绕过这条老链路;如果是Java,可能得关注Spring的SpEL注入或者Shiro的记住我功能反序列化;如果是Node.js,原型链污染和表达式注入就得放进备选池。
MedicalApp这题给我最明显的感觉是:登录注册模块做到了一半,有点草率。这里的草率不是说页面做得丑,而是好多接口看起来能用但状态返回异常。我当时心里就咯噔了一下——半成品功能往往是出题人故意留的口子,因为"没做完"的东西最容易藏逻辑漏洞。与其在完整的页面里找洞,不如在这些半吊子接口里花时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 医疗业务的功能面盘点:把攻击目标画成树
2.1 角色权限模型:先弄清楚谁是谁
任何Web题,特别是带业务场景的,第一步永远是理清角色。MedicalApp这种系统里至少有三类角色:
| 角色 | 常见权限 | 攻击价值 |
|---|---|---|
| 普通患者 | 注册、预约、查看本人报告 | 低权限入口,用于探测通用漏洞 |
| 医生/护士 | 门诊排班、查看患者病历 | 中权限目标,常隐藏越权点 |
| 管理员 | 用户管理、系统配置、日志查看 | 高权限目标,往往就在后台藏着Flag |
大多数情况下,我们的起点是患者角色,目标是管理员权限。中间往往还有医生角色作为"跳板"。这类题最喜欢的考法就是:你注册一个普通用户,然后通过各种姿势把自己变成医生,再从医生变成管理员。
这不是我瞎猜——医疗系统真实世界的权限模型就是这么设计的。出题人照搬业务模型来出题,天然就能把每一层的权限边界变成考点。所以拿到题后在纸上画出角色关系图,标记哪些功能点是跨角色的,这一步比直接开扫重要得多。
2.2 核心功能点逐一过筛
我的习惯是注册一个账号登录进去,把页面上所有能点的按钮都点一遍,同时开Burp Suite挂代理,把每个请求都记录下来。MedicalApp当时的页面结构大致是:首页展示医院介绍和科室列表,登录后跳转到个人中心,里面有预约记录、检查报告列表和个人资料编辑。
这个过程中我最关注三种接口:
一是查询类接口。报告列表、预约记录的接口一旦没有做水平越权校验,就可以通过修改ID来查看别人的数据。一般接口参数里带着数字ID或者UUID的,都值得手动改一改试试。二是下载类接口。报告下载经常是file.php?filename=xxx.jpg这种形式,改filename参数就能读任意文件,这也是经典考法。三是编辑类接口。个人资料编辑看似无害,但很多框架会自动绑定客户端提交的字段,如果提交一个role=admin或者其他额外参数,后端不问青红皂白就存进去了,这在Java的Spring MVC里是著名的Mass Assignment漏洞。
2.3 医疗服务流程里的状态机漏洞
比单点漏洞更隐蔽的是流程状态机漏洞。比如一个预约流程,前端正常走是"选择科室-选择医生-选时间-确认",但后端的接口可能没有对状态做严格校验。我可以直接跳过某个步骤,或者把同一个请求重放两次。曾经有题就是利用预约接口和取消预约接口的竞态条件,把一个预约反复取消再通过,搞出了并发刷数据的效果。
MedicalApp里如果我记得没错的话,它把挂号、缴费、查看报告拆成了好几个接口,但之间依赖关系校验得非常弱。这种"改得很弱"的设计放在CTF里就是故意送分的信号。所以我爬完一遍业务后,专门回去把预约和取消预约的接口用Turbo Intruder做了几次条件竞争测试,但这种东西灵不灵全看出题人,很多时候也可能完全没坑,终归要在有限时间里排出优先级。
3. 锁定关键漏洞:从信息泄露到权限边界
3.1 低垂果实:接口里的敏感信息回显
有些题的Flag不是藏得很深,而是藏在响应报文里你没注意的地方。我习惯在浏览功能时把响应包切成JSON或者Raw模式逐段看,尤其是那些报错信息。MedicalApp在若干接口里返回了完整的异常堆栈和SQL语句片段。这里多说一句:很多CTF选手看到报错就慌了,觉得环境是不是崩了,恰恰相反,报错信息是后端最诚实的接口文档。堆栈里有文件路径、类名、数据库表前缀,这些信息在后期的SQL注入或者文件读取阶段都能直接当作"地图"来用。
我当时在某个查询报告详情的接口里看到SQL报错片段,直接拼出了表名的大致结构。就这一点来说,MedicalApp的前期渗透难度其实不高,属于出题人故意在信息收集阶段送你一张地图。
3.2 认证绕过的尝试路径:从字典档到逻辑缺陷
Web题里绕认证的常规操作无非以下几种:弱口令倒着试(admin/admin、test/test123这种)、修改请求里的角色字段、对登录接口做SQL注入、利用验证码不校验的逻辑漏洞。
我在MedicalApp上先试了登录接口的SQL注入,布尔的、时间盲注的都跑了,没有明显反馈。这块要讲个经验:测试SQL注入不能只盯login这一个点,WHERE条件里带数值的接口(比如id=xxx)反而是更高频的注入点。后来我在报告查询接口的id参数上试了单引号,报错模式出现了变化,再一测发现是数字型的注入点,能直接union出数据库里的用户表。整个医疗App的表结构从information_schema里一拉就出来了,这一步基本锁定了后续路径。
3.3 水平越权的确认:别人的病历就在眼前
大概在把报告ID逐个递增的时候,我发现自己账号能看到编号相邻的其他报告。这个发现其实比SQL注入还要致命。说明后端完全没有"报告归属校验",只是通过一个连续的ID去拉数据。想深入体会"水平越权"这个词,你只需要知道:你只要把URL里的id数字加1,就可以看到不属于你的医疗记录。这几乎刻在了CTF Web题的经典考法清单上。
我当时的判断是:出题人故意在系统里留下一处没做鉴权的数据查询接口,用来体现"医疗数据只能被患者本人查看"这条业务规则的失守。这种漏洞在真实世界里就是医疗数据泄露事故的源头。有了这个洞,就算后面SQL注入被封死,我也能用它把整个系统的用户数据翻一遍,包括管理员的账号和密码哈希。
4. 利用链拼接:从普通患者到拿到Flag
4.1 先拿数据库内容,再找管理入口
通过SQL注入和越权接口这两条线,我拿到了数据库的用户表、医生表、管理员表。这里有个小经验:拿到表之后不要急着翻数据,先把每条数据的结构看清楚,尤其是密码字段的哈希格式。如果是MD5就直接彩虹表或者在线站点跑,如果是bcrypt就得掂量一下强度,专挑弱口令猜测。
管理员表里往往有独立的表或者字段标记用户角色,我就是在角色字段上看到了'admin'字样,然后顺着找到了管理后台的地址。管理后台路径在页面源代码里是没有入口的,一般只能通过扫描加上数据库信息反推出来。
4.2 后台功能的最终一击:文件上传与Getshell
进入后台后就到了最后一步。医疗后台里有很多功能性页面,比如医生信息上传头像、检查报告批量上传、文件附件管理。这类后台如果有文件上传功能,基本都存在"扩展名黑名单不严、内容校验缺失、存储路径可控"的问题。我当时的思路是:既然后端是PHP,那就试试上传一个内容为PHP代码、扩展名伪装成图片的文件,再用解析绕过或者路径拼接的方式让它执行。
这一步如果成功,直接就能通过Webshell执行系统命令,Flag要么在数据库里,要么就在Web根目录或者系统根目录下。MedicalApp把Flag藏在一个只有管理员能访问的配置页文件里,其实毫无意外——拿到后台管理权限就等于拿到了Flag的钥匙。上传PHP文件、连接Webshell、读配置文件,一气呵成。
4.3 为什么这套链路能走通:一层一层都是设计好的
整套利用链能走通,不是因为我技术多强,而是因为它完全踩在医疗应用的通病上:
- 信息泄露给了地图(报错堆栈、SQL片段);
- 水平越权放大了访问边界(拿别人的报告数据);
- SQL注入直接操作了权限模型(查到管理员账号);
- 后台弱校验给了代码执行能力(上传文件直接落地)。
这四个洞串在一起,每一层都是上一层的"助攻"。真实世界的医疗系统渗透测试也基本是这个套路——互联网边界打进去只是一小步,内网里一层一层打穿业务系统边界才是大头。
5. 复盘MedicalApp:这类题型给我们留下的几个沉淀
5.1 不要一上来就暴力扫目录,先理业务
很多新手打Web题的第一反应是跑目录扫描器。这个动作不是不行,但效率太低。MedicalApp这种业务型题,目录扫描得到的有效信息远远不如把业务流程完整走一遍来得实在。你注册一个账号,把功能点挨个点过去,接口列表自动就进了Burp的历史记录里。后续分析就不愁没有素材。说白了,目录扫描是找"隐藏页面"的,而业务逻辑漏洞全藏在"正常页面"里。
5.2 对数字ID的操作要形成肌肉记忆
看见URL里带数字ID,第一反应就是改。加法、减法、换成UUID、换成负数,全试一遍。水平越权在很多团队里都被划为"低危",但在CTF里它是扭转战局的关键一环。你能拿到别人的数据,往往就拿到了管理员凭证或者Flag存放位置。
5.3 报错信息是地图,不是噪音
新手怕报错,老手追着报错看。MedicalApp让我印象最深的就是:整个渗透过程中的几次关键转折,全是从报错信息里找到的线索。SQL语法错误透露了表结构,文件路径报错透露了Web根目录,配置错误透露了框架类型。以后做任何Web题,把Burp里的错误响应单独拉出来看一遍,往往会有意外收获。
5.4 上传点不要只看扩展名
文件上传绕过在CTF里早就是烂大街的考点了,但每年还是能考出新花来。核心思路就是:后端对文件内容、文件头、MIME类型、扩展名、存储路径的控制往往不是全面的。图片马加解析漏洞、双写扩展名、大小写绕过、空字节截断、.htaccess覆盖,这些都是前辈们踩出来的路。MedicalApp这种医疗场景给上传功能提供了天然的合理性——报告单、头像、检查影像,随便一个都能作文章。
打CTF打到一定程度你会发现,真正拉开差距的不全是技巧的多少,而是排查攻击面的体系化程度。看到一个题目,能不能快速想清楚"这个应用里有哪些角色、哪些功能、每个功能点能关联到哪些漏洞",比单纯会某个注入姿势重要得多。MedicalApp给的就是一个非常标准、非常典型的教学样本。如果你能把自己代入一个真实渗透测试工程师的角色——先从业务出发,再谈姿势,那么这类题无论怎么换皮,你都能快速抓住要害。
最后分享一个小经验:每次打完这种业务型Web题,我会把整条攻击路径画成一张简单的步骤图,然后把每一环对应的检测方法写下来。积累几道题之后,你再看新题目就会有"既视感",因为绝大多数CTF Web题的灵魂都是相通的——身份认证、越权、文件处理是你永远绕不开的三座大山。MedicalApp不过是换了个白大褂而已。
