背景
昨天,朋友突然找到我,说他的 EmbyServer 被人入侵了,内容库被胡乱删改,留下了一堆挑衅信息。
简单的问了下,了解到他的 EmbyServer 管理员密码是弱口令,而他最近在后台看到有一些恶意的尝试登录的行为,不过频率并不高。我以为是简单的弱密码碰撞导致的,并没有放在心上,只是让他删除所有配置,重新删库重装,并更改使用复杂一些的密码。
朋友很听劝,第一时间按照建议重装了系统,可是不到三小时,联系我说 EmbyServer 又沦陷了。这次入侵者不但删改内容库,还主动在 TG 上联系到了他,勒索 100U,嚣张程度可见一斑。
这时我开始意识到这次攻击是有备而来,短短三个小时,必不可能是碰撞撞出来的,肯定是哪里有了漏洞导致的。于是和朋友要了服务器帐号密码,开始溯源。
安全应急响应的一般流程
很多人没有遇到过类似的情况,不知道遇到这种事该怎么处理,这里简单讲解下我所了解的安全应急响应的一般流程,另外,因为我本身并不是做安全出身的,内容难免有错误、遗漏之处,欢迎各位指出。
遏制
第一时间要做的,永远是遏制,防止攻击者利用漏洞进一步破坏或提权。针对此次情况,首先要做的就是网络隔离
因为朋友的 EmbyServer 运行在 Docker,如果可以确定影响没有溢出到宿主机的情况下,可以考虑其他业务不停机,先断开 EmbyServer 的网络连接
保护现场
其次要做的,是尽可能停止对关键日志进行更改/删减操作的服务
分析
溯源和分析,找到发生问题的原因
最后: 清除、恢复和总结。
溯源过程
1. 确定影响范围
首先,第一时间登入服务器,在做好了网络隔离的情况下,开始分析系统痕迹。经过日志分析,基本可以排除服务器层面的侵入可能,结合 EmbyServer 的 Docker 配置,基本也可以排除从容器提权到宿主机的可能,最终锁定侵入范围仅在 EmbyServer 服务本身。
2. 调查服务环境
经过确认,服务器所用 EmbySever 为官方最新版本镜像,EmbyServer 使用了数个第三方插件以及围绕 EmbyServer 搭建了数个配套服务系统,均运行在单独的 Docker 中,服务之间互相隔离,并没有使用 Docker compose 统一编排。
首先,我倾向于认为不太可能是 EmbyServer 本身的问题,假如是一个已经公开过的基于最新版本的高危安全漏洞,现在肯定到处都是风声,我应该早就听到了。如果是个未公开发表的 0day,我相信发现者也不会拿这种高级别漏洞做这么 low 的事儿,而且整个勒索的过程也显得太不专业了点,怎么看都像个年纪不大的脚本小子。
不过本着宁可信其有,不可信其无的心态,还是简单的检索了下,确定 EmbyServer 近期没有高危漏洞公开的信息,至此进一步缩小调查范围,目前集中锁定在插件和周边服务。
3. 溯源
前两步其实在登入服务器之前心里就已经估摸个大概了,所以执行起来并没有花费多少时间,更多的是对想法的验证。接下来主要集中在对日志的分析上,整个 EmbyServer 日志打包下来有 80 多MB,一条一条看是不可能的,只能聚焦在敏感操作上,搜了下跟认证/用户服务/Token 相关的日志,有大几千条,即使过滤了 succeed/success 相关的,也剩个几百条。
全看是不可能全看的,尝试定位下事件发生的时间段。比较麻烦的是,朋友并不知道确切的事件发生时间点,只是其他用户告诉他, EmbyServer 的内容库被再次更改,并且他尝试登录自己的管理员账户时候发现登录不上去。
因为已经做好了网络隔离,不担心启用 EmbyServer 时被进一步进行破坏,所以直接后台重置密码,以管理员用户登录后台查看信息。
这一查,确实发现了一些有用的信息,这个帐号的用户策略设置和密码更改并不是朋友自己操作的。
定位到该段时间对应的详细日志,果然有所收货
2025-10-20 15:49:00.043 Info UserService-0HNGFIA83NGCV:00000001: http/1.1 POST http://xx.xx.xx.xx/Users/xxxxxxxxxxxxx/Password. Source Ip: 144.xx.xx.xx, UserAgent: python-requests/2.32.5
python-requests/2.32.5
,这个请求头说明对方是用 Python 脚本操作的,并不是通过后台面板登录进去操作的,搜索这个 144.xx.xx.xx IP的所有操作,基本可以还原整个过程。
15:47:54 - 用 Python 脚本,利用 EmbyServer Token 修改管理员密码
15:49:00 - 创建新的账户 pxxxx
15:49:00 - 为 pxxxxx 设置密码修改权限
15:49:24 - 删除原始管理原账户
4. 定位漏洞
知道了对方的攻击手法,下一个问题就是找到 Token 泄漏的原因。朋友刚重装的官方的 EmbyServer 容器,Token 都是新设的,有三条 Token,分别给了三个相关服务使用,同时 EmbyServer 本身也装了第三方插件。
第一时间的想法是确定哪个 Token 泄漏了
首先继续研究了下 EmbyServer 日志,发现其并没有进一步的详细请求说明,无法得知一个请求具体是使用了哪个 Token 来进行操作的。
又问了下朋友关于反向代理服务搭建的细节,没有获取到有用信息,也没有日志可以读取。
无法确定是哪个 Token 泄漏引起的,没办法,只能从插件和服务本身来手动来排查了
首先看了下第三方插件,功能主要是利用 ffmpeg 来读取媒体数据,用来加速刮削用的。插件作者,我多少也是听说过的,个人不太相信其会埋雷,另外我虽然没有用过这个插件,但是基于这个插件的功能分析,不太可能有需要用到 Token 的地方,所以其他人找到这个插件的漏洞然后获取 Token ,应该也是不太可能的。
剩下三个服务,一个是用来下载媒体的,一个是用来同步和生成 Strm 的,一个是用来管理 EmbyServer 用户的,每个都有获取 Token。
第一个是开源项目,使用人数多,影响范围广,不太可能是原因(因为有 0day 的话,影响范围会大很多,应该能听到风声)先放在后面。
后两个都是小范围内使用的收费项目,且朋友都使用了很长一段时间,两个都有可能。不过我听到的第一反应就是管理 EmbyServer 用户的更可能像是泄漏的原因,毕竟跟用户操作相关的服务是比较让人敏感的,决定首先排查这个。
5. 代码审计
进去EmbyServer 用户管理服务的容器,看了下源码,Python 写的,有一定的加密,但不多。代码风格写的也比较像 AI 生成的。(此时其实已经有感觉问题大概率出现在这个服务)
开始审计,另外说明下进行审计时候的排查思路(因为这个服务的功能和代码还是比较多的,毕竟是收费项目)
首先需要重点关照的是跟配置相关的路由,这是最有可能导致 Token 泄漏的地方
其次关注关于权限验证部分的路由
关于可能存在注入点和任意代码执行部分的路由
其他接口
关于1, 他这个项目写的还是比较特殊的,前端没有手动写入 EmbyServer Token 的地方,而是在安装的时候通过配置文件写入的,而且这个 Token 并没有写入环境变量,同时检查了他的获取配置的相关接口,并没有暴露 Token 的可能。
关于2, 权限验证部分的路由也没有太多问题,而且就算是管理员登录,也没有在前台能获取到的 Token 的地方。
关于3, 不存在这样的问题
只能去检查,使用 Token 和调用 Emby 相关服务的路由,在一个 emby-proxy
的路由下,发现了问题
@app.route('/emby-proxy/<path:subpath>')
def emby_proxy(subpath):
"""代理Emby图片请求,解决Docker中的跨域问题"""
try:
# 构建完整的Emby URL
url = f"{EMBY_SERVER}/emby/{subpath}"
headers = {
'X-Emby-Token': ADMIN_TOKEN,
'User-Agent': 'EmbyUserHub/3.1.0'
}
# 转发请求到Emby服务器
response = requests.get(
url,
headers=headers,
params=request.args,
stream=True,
timeout=10
)
# 如果请求失败,记录错误并返回错误响应
if response.status_code != 200:
app.logger.error(f"Emby代理请求失败: {url}, 状态码: {response.status_code}")
return f"图片加载失败: {response.status_code}", response.status_code
# 创建Flask响应对象
from flask import Response
proxy_response = Response(
response.iter_content(chunk_size=10*1024),
status=response.status_code,
content_type=response.headers.get('content-type', 'image/jpeg')
)
# 复制响应头
for key, value in response.headers.items():
if key.lower() not in ('content-length', 'transfer-encoding', 'content-encoding'):
proxy_response.headers[key] = value
return proxy_response
except Exception as e:
app.logger.error(f"Emby代理异常: {str(e)}")
return "图片加载失败", 500
接口本身是想要转发一些 Emby 的响应,问题是,他在转发响应的时候,是直接拿响应进行拼接的,并没有针对响应头和响应体的敏感信息进行过滤,这就导致了 Token 泄漏的可能。
拿普通用户登入服务前台进行验证,发现在普通用户界面,查看最新入库媒体海报和获取最新媒体信息的时候,响应体里直接暴露了 EmbyServer Token,就此可以确定漏洞所在!
总结
整个漏洞利用的过程是:
朋友搭建了一个收费的「EmbyServer用户管理」项目,可以方便快捷的进行用户管理
「EmbyServer用户管理」项目新建的普通用户既可以登录 EmbyServer,也可以在「EmbyServer用户管理」项目的用户端查看最新入库的媒体资源,但是这一过程中会暴露 EmbyServer 的 Token
不法用户使用 Token 创建 EmbyServer 管理员账户,恶意窜改媒体库
因全部服务互相隔离,且 EmbyServer 用 Docker 容器搭建,不法用户无法造成进一步损失(又因为使用 Strm 的缘故,连原始资源也无法删除)
修复,已告知朋友漏洞原因和修复方案,截止目前,服务器并没有再次受到入侵。
后记
脚本小子在敲诈我朋友的时候并没有做好自己的匿踪防护,使用的 144.xx.xx.xx 的跳板竟然是他自己的常用服务器。这下好了,可以友好的反向调查一下了,目前已经查到了域名,以及服务器上面跑得服务列表。
更新: 已经定位到QQ了,后续就不展开讲了。
10.22 再更新:
本来已经完结了的,朋友说服务器又被黑了,依然是 Token 改密码、删用户、改 EmbyServer 标题三件套。
比较纳闷,又问了下朋友,说之前告诉我的使用 Token 的服务只是其中一部分,还有一些没有告诉我的服务也在使用 Token - -#,简单溯源了下,这次的手法跟上次一次,甚至连使用的 IP 都没变,漏 Token 的地方可能不止一处。不过破坏力依然感人,除了改改 EmbyServer 标题,啥也没干。
又了解到一个细节,朋友说重装后又把 Token 给了除「EmbyUser管理」之外的所有服务,且没有修改相应服务的密码,不排除周边服务存在弱口令的可能性。
考虑到破坏力实在有限(连删 Strm 副本都做不到,只能改改网站标题)。且了解完全后,朋友的整个 EmbyServer 周边服务非常庞杂(甚至有弹幕相关的),一个个服务的去审查,时间成本上有点划不来。简单粗暴点,先直接禁用 Token,后续看看情况是否值得进一步分析。
另外这台服务器,整个服务器上面的容器编排、网络隔离、日志记录、备份容灾等多少都有些可以优化和改善的地方。(搞笑的是,这个 144.xx 的服务器,也没做好,暴露到外网很多不必要的服务)。
后续如果有朋友感兴趣的话,可以留言,到时候考虑结合本次案例出一篇关于服务搭建、容器编排、日常维护的文章。当然,在运维方面,我也只是个刚刚入门的新手,如果有说的不对或者有纰漏的地方,欢迎各为大佬指正,一起学习交流。
评论