客厅主机风扇狂转,后台正在替黑客挖矿
客厅角落的一台静音主机,散热风扇突然通宵狂转。主机平时只用来串流 4K 高清电影,日常负载极低。设备主人登录后台查看,处理器占用率已连日保持在 100%。
两个未知的后台程序正在昼夜消耗算力。一个伪装成网络代理 myawesomeproxy,另一个是加密货币挖矿程序 mylovelyminer。主人并未下载来源不明的安装包,管理后台也设有独立强密码。
问题出在多媒体服务软件 Plex Media Server 上。安全研究人员披露了一条编号为 plex2shell 的远程代码执行链(CVE-2026-96652、-96654、-96655、-96656)。该风险覆盖 1.43.2 及更早版本。公网攻击者无需密码,发送一段特制请求就能接管整台设备。
图:蜜罐记录中攻击者通过自动化脚本拉取恶意载荷的完整过程。来源:BoredHackerBlog。
借用未校验的回调地址,诱导服务器攻击自身
很多本地服务都有一个惯性假设:本机发出的请求默认安全。为了省去内部通信的核验开销,Plex 大幅放宽了本地请求的拦截规则。这种信任设计成了攻击链的突破口。外部黑客只要诱导服务器攻击自己,内部防线便瞬间瓦解。
破绽藏在播放器时间线接口 /player/timeline 中,该接口负责接收播放器的进度上报。请求按要求需提供 X-Plex-Token 请求头,但服务端代码只调用了 hasParam() 方法。该方法只检测请求头是否存在而不验证有效性,填入无意义的垃圾字符便能放行。
通过门槛后,服务端会向播放器地址回传事件订阅通知。底层代码使用 boost::format("%1%://%2%:%3%") 拼装回调链接。协议字段 protocol 未做任何格式检查与字符过滤。
攻击者将包含回环地址的完整 URL 塞入该参数。Plex 随即向自身的内部管理端口发送了一条 HTTP POST 请求。在网络连接层面,请求源 IP 变成了真实的 127.0.0.1,攻击者就此拿到了「受信任的本地身份」。
字符串前缀判断失误,管理员凭据拱手相送
伪装成本地请求后,攻击者进入了内部插件管理接口 /system/agents/*。该接口检测到来源是回环地址后,直接跳过凭据校验。服务端脚本 agentservice.py 内部原本为 searchOne 函数编写了白名单。
然而函数上方挂载了 @lock 互斥锁装饰器。装饰器没有传递底层 inner() 函数的返回值。拦截规则触发提前退出时,返回状态被装饰器直接丢弃,拦截机制失去效力。
同时,请求参数 identifier 未做字符过滤。传入问号可以开启查询参数,传入井号则能截断默认的 /messaging/... 路径。第二处缺陷就此成立。
攻击者随后瞄准了全局管理员权限,而 Plex 插件框架正好内置了一项便利设计。只要出站请求指向以 127.0.0.1 开头的地址,系统便会自动附带管理员专属的 local-* 凭证。框架采用无界字符串前缀匹配而未解析主机名边界,导致指向 127.0.0.1.evil.com 的请求也能通过检验。
攻击者利用动态域名服务 sslip.io 完成凭证窃取,构造出域名 127.0.0.1.10-0-0-9.sslip.io。该域名既满足前缀规则,又会在公网解析时将流量导向攻击者的服务器 10.0.0.9。Plex 判定规则通过后主动送出管理员 Token,第三处缺陷就此触发。
图:安全研究员捕获的外部恶意 IP 发起漏洞探测与利用的实时日志。来源:BoredHackerBlog。
恶意代码伪装成视频画面,借转码器写入系统目录
掌握管理员 Token 后,攻击者拥有了修改服务端偏好设置的权力。Plex 依靠内置转码器实现跨设备画面适配。底层 x264 编码组件包含调试参数。攻击者注入了调试指令:TranscoderH264Options=dump-yuv=<路径>:qp=0。
dump-yuv 参数会调用底层 fopen(path, "w"),将解码后的视频原始帧写入指定路径。量化参数 qp 设为 0 时启用无损模式,画面像素不会发生偏离。攻击者提前编写了一个 ELF 格式的动态链接库。该动态库仅包含一个系统初始化构造函数。
攻击者把编译出来的二进制字节,逐点填入一段 MP4 视频第一帧的亮度平面中。外观正常的无损 H.264 载体视频就此成型。
攻击者随后用管理员凭证发出转码请求,把 path 参数指向自己托管的远程 item.xml 清单。Plex 主动拉取那段载体视频并启动转码。解码器解出第一帧的瞬间,调试功能触发。隐藏在画面像素中的恶意动态库,被转码引擎亲手写入了系统底层的 Codecs 目录。
底层组件缺少签名校验,无名动态库直接夺取权限
恶意文件写入磁盘只是第一步,让系统加载它才是最终目的。Plex 自带的底层组件 libavutil.so.58 在转码器启动时,都会调用 avpriv_load_new_libs 函数。该函数负责扫描 Codecs 目录下的所有文件。只要文件名以 .so 结尾,函数便会调用 dlopen(path, RTLD_NOW)。
这一加载逻辑省略了关键安全步骤。它不核对标准前缀,不检查执行权限位。代码缺少数字签名校验机制。即使动态库文件的权限仅为普通读取,依然会被载入内存空间。
根据动态链接规范,dlopen 载入动态库时会优先执行 DT_INIT_ARRAY 定义的构造函数,无需导出任何有效符号。构造函数内部直接调用 fork() 与 execve() 派生子进程,迅速建立反向连接通道。攻击者凭此直接拿到守护进程所属的 uid=1000(plex) 权限,整条链走到终点。
大模型压低漏洞挖掘门槛,家用边缘设备首当其冲
整套利用链的时间线十分紧凑。漏洞发现者于 2026 年 7 月 27 日向官方报告。Plex 团队在 8 月 4 日确认并支付了赏金。9 月 23 日官方分配了 4 个 CVE 编号,并在 10 月 16 日正式公开细节与补丁。
漏洞细节公开后,在野攻击迅速出现。蜜罐捕获到了来自 45.194.92.222、91.92.40.202 和 176.65.139.228 的自动化探测流量。攻击者从恶意地址 217.60.194.116 下发恶意程序。
攻击样本包括哈希为 5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441 的容器镜像,以及用 UPX 打包的二进制文件 68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7。脱壳后的核心文件哈希为 b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49。攻击者利用家庭设备昼夜开机的特性,悄悄挂载代理并挖掘加密货币。
这套攻击链的挖掘方式同样引人关注。发现者自述采用了大模型辅助流程。整个工作分为侦察、排查、验证与链式组合四步,并用独立智能体专门证伪每条线索。
在这个工作流中,人工分析师负责把控架构决策与利用优先级。AI 工具将繁杂的代码排查门槛压低,串联多段缺陷的成本大幅下降。承受第一波冲击的,正是缺少专业防护的家庭边缘设备。
这条链条里没有复杂的内存越界。未校验的请求头,被装饰器吞掉的拦截指令,无界的前缀匹配,未受限制的库加载。每一处都是各自孤立的代码粗心。
Plex 对「来自本机的请求就可信」的设计假设,让这些粗糙的失误咬合为直通核心的后门。省事的免鉴权设计,最终变成了失守的最快通道。面对已经开始的在野自动化扫描,升级到已修复的新版软件,是阻断设备沦为矿机的当务之急。
参考链接:
- zmain.info 的 plex2shell 报告
- BoredHackerBlog 的野外利用分析
- GitHub code-zm/plex2shell
- GitHub BoredHackerBlog/plex2shell_pcap
- HN 讨论 (item?id=50152321)