CVE-2024-39877:Apache Airflow 任意代码执行

news2024/9/25 7:24:59

Apache Airflow 是一个开源平台,用于以编程方式编写、调度和监控工作流。虽然它提供了管理复杂工作流的强大功能,但它也存在安全漏洞。一个值得注意的漏洞 CVE-2024-39877 是 DAG(有向无环图)代码执行漏洞。这允许经过身份验证的 DAG 作者以一种可以在调度程序上下文中执行任意代码的方式制作 doc_md 参数,而根据 Airflow 安全模型,这是被禁止的。

补丁差异

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从GitHub上修补漏洞的 pull request 中,我们可以看到 DAG 代码执行漏洞源于对 doc_md 参数的不当处理,这允许攻击者在调度器上下文中注入并执行任意代码。Airflow 的 DAG 中的 doc_md 参数允许包含 Markdown 文档。但是,由于清理不当,由于使用 Jinja2 来呈现此参数的内容,因此可以注入可执行任意 Python 代码的 Jinja2 模板。由于 Airflow 调度器会处理此参数,因此任何注入的代码都将在调度器上下文中运行。通过将 doc_md 参数内的数据视为原始数据,可以修补此漏洞。

测试实验室

\1. 我们将在 Docker 上构建实验室。首先,我们需要拉取易受攻击的镜像:

airflow % docker pull apache/airflow:2.4.0

2.然后,下载 Docker Compose 文件:

airflow % curl-LfO’https://airflow.apache.org/docs/apache-airflow/2.4.0/docker-compose.yaml’

\3. 创建 logs、dags、plugins 和 config 文件夹以及 .env 文件:

airflow % mkdir -p ./dags ./logs ./plugins ./config && echo -e “AIRFLOW_UID=$(id -u)” > .env

4.检查创建的目录和文件:

airflow % ls

配置 dags docker-compose.yaml 日志插件

\5. 启动气流:

airflow % sudo docker compose up airflow-init

6.现在,运行 Airflow:

气流 %s udo docker compose up

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们发现它在端口 8080 上工作。用户名和密码是 airflow:airflow。

分析

现在,为了重现该漏洞,我们需要创建一个 DAG。

什么是 DAG?

有向无环图 (DAG) 是一种有向边且无环的有限图。在 Apache Airflow 中,DAG 是您要运行的所有任务的集合,以反映其关系和依赖关系的方式组织。

  • **有向:**图中的每个边都有一个方向,从一个节点(任务)到另一个节点(任务)。
  • **非循环:**图中没有循环,这意味着您不能从一项任务开始并沿着有向边回到同一项任务。
  • **图:**节点(任务)和边(任务之间的依赖关系)的集合。

Apache Airflow 中的 DAG

在 Apache Airflow 中,DAG 是用 Python 脚本定义的,它指定了任务之间的关系和依赖关系。以下是一些关键组件:

  • **任务:**单个工作单元,可以是运行 shell 命令、调用 API 或运行机器学习模型等任何内容。
  • **依赖关系:**任务之间的关系,指定哪些任务需要先完成,其他任务才能开始。
  • **调度:**定义 DAG 运行的时间和频率。

DAG 示例

以下 DAG 包含一个 doc_md 参数。此参数允许您使用 Markdown 记录您的 DAG。当您查看 DAG 详细信息时,文档将显示在 Airflow Web 界面中。

from datetime import datetime
from airflow import DAG
from airflow.operators.empty import EmptyOperator
default_args = {
    'owner': 'airflow',
    'start_date': datetime(2023, 1, 1),
    'retries': 1
}
# Define the DAG
dag = DAG(
    'example_dag_with_doc_md',
    default_args=default_args,
    description='An example DAG with doc_md',
    schedule='@daily',
    doc_md="""
    # Example DAG
    This is an example DAG that demonstrates the use of the `doc_md` parameter to add documentation.
    ## Description
    This DAG has two dummy tasks: `start` and `end`.
    ## Tasks
    - `start`: This is the starting task.
    - `end`: This is the ending task.
    ## Dependencies
    The `end` task depends on the `start` task.
    """
)
# Define the tasks
start = EmptyOperator(
    task_id='start',
    dag=dag
)

end = EmptyOperator(
    task_id='end',
    dag=dag
)
# Set the task dependencies
start >> end
  • **doc_md:**此参数用于将 Markdown 文档添加到 DAG。doc_md 字符串中的内容以 Markdown 编写,当您查看 DAG 详细信息时,它将呈现在 Airflow 网页界面中。
  • **EmptyOperator:**这是一个不执行任何操作的简单运算符。它在这里用于创建占位符任务。

现在,让我们尝试一下 DAG

保存 DAG 文件

将上述代码保存为 Python 文件(例如,example_dag_with_doc_md.py)放在 Airflow DAGs 文件夹(在我们的 Docker 设置中为 /opt/airflow/dags/)中。

触发 DAG

转到 Airflow 网页界面并触发名为 example_dag_with_doc_md 的 DAG。

查看文档

在 Airflow 网页界面点击 DAG 查看其详细信息。您将在 Doc 选项卡中看到渲染后的 Markdown 文档。

这里究竟发生了什么?

让我们看一下漏洞代码中的 def get_doc_md(self, doc_md: str | None) -> str | None: 函数,看看它如何从 doc_md 解析 Markdown 内容:

def get_doc_md(self, doc_md: str | None) -> str | None:
    if doc_md is None:
        return doc_md
    env = self.get_template_env(force_sandboxed=True)
    if not doc_md.endswith(".md"):
        template = jinja2.Template(doc_md)
    else:
        try:
            template = env.get_template(doc_md)
        except jinja2.exceptions.TemplateNotFound:
            return f"""
            # Templating Error!
            Not able to find the template file: `{doc_md}`.
            """
    return template.render()

get_doc_md 方法旨在处理 doc_md 参数,允许 DAG 作者将 Markdown 文档嵌入其 DAG 中。下面是其工作原理的详细说明:

1.检查doc_md是否为None:

如果 doc_md 为 None,该函数会提前返回。

2.初始化Jinja2环境:

它使用 self.get_template_env(force_sandboxed=True) 初始化启用沙盒的 Jinja2 环境。

3.处理doc_md内容:

  • 如果 doc_md 不以 .md 结尾,它会使用 template = jinja2.Template(doc_md) 直接从 doc_md 字符串创建 Jinja2 模板。此步骤非常危险,因为它允许将 doc_md 中提供的任何字符串视为 Jinja2 模板,而无需进行任何清理。如果攻击者可以操纵此内容,他们就可以轻松地将恶意 Jinja2 表达式甚至任意 Python 代码注入模板。
  • 如果 doc_md 以 .md 结尾,该方法将尝试使用 env.get_template(doc_md) 从环境中加载模板。如果未找到模板文件,它将返回模板错误消息。然而,这部分不如直接创建模板那么重要。

4.渲染模板:

最后一步 template.render() 执行渲染的模板,即注入的代码在这里执行。

所以,该漏洞是注入攻击(服务器端模板注入,SSTI)的经典示例。

开发

让我们看看如何利用此漏洞:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

攻击场景

详细步骤

1.发送恶意doc_md有效负载:

攻击者通过doc_md参数向Web服务器发送恶意负载。

2. 将有效载荷转发至气流:

Web 服务器将此有效负载转发给 Airflow 应用程序,然后该应用程序调用 get_doc_md 方法。

3.调用get_doc_md方法:

该方法检查 doc_md 参数是否为 None 并继续初始化 Jinja2 环境。

4.创建Jinja2模板:

接下来,它会使用doc_md内容创建一个Jinja2模板并渲染该模板。在渲染过程中,doc_md参数中嵌入的恶意代码会被操作系统执行。

5.执行注入的代码:

操作系统执行命令并将输出返回给 Airflow 应用程序。

6.发送回复:

最后,Airflow 将渲染的模板输出发送回 Web 服务器,然后 Web 服务器将包括命令输出在内的响应返回给攻击者。

注入代码示例

为了证明这一点,让我们注入代码来转储可用的类:

doc_md="""
    {{ ''.__class__.__mro__[1].__subclasses__() }}
    """

Jinja2 模板代码中的 {{”.class.mro[1].subclasses()}} 利用 Python 的自省功能列出对象类的所有子类,从而有效地揭示当前 Python 环境中加载的所有类。其工作原理如下:

  • ”.class 检索空字符串的类,即 str。
  • 访问此类上的 .mro 可提供方法解析顺序 (MRO),这是一个包含 str 类本身及其基类(包括对象)的元组。
  • 表达式 .mro[1] 从这个元组中选择对象类。
  • 最后,.subclasses() 列出了 object 类的所有已知子类,使我们能够枚举运行时可用的类。这可用于识别有用的类(如 os.system),以便在操作系统上执行命令并实现代码执行。

在这里插入图片描述

更新 DAG 后,我们注入的表达式被渲染并转储所有可用的类。

在这里插入图片描述

在这里,我们可以看到像 subprocess.Popen 这样的有用类可用于执行命令。利用取决于环境和类的可用性。

结论

在本次分析中,我们发现了 CVE-2024-39877 漏洞,该漏洞允许经过身份验证的 DAG 作者利用 doc_md 参数在调度程序上下文中执行任意代码,从而违反 Airflow 的安全模型。该漏洞源于对 doc_md 参数的不当处理和清理,该参数使用 Jinja2 模板呈现。这一疏忽允许攻击者注入可以执行 Python 代码的恶意 Jinja2 表达式。

存在漏洞的代码中的 get_doc_md 方法会初始化 Jinja2 环境,如果 doc_md 字符串不是以 .md 结尾,则直接根据该字符串创建模板,从而在未经过充分清理的情况下呈现模板。攻击者可以通过注入有效载荷来利用此过程,利用 Python 的自省功能枚举可用类并执行命令,从而入侵系统。

为了缓解这种情况,补丁确保正确处理 doc_md 作为原始数据,从而防止执行任意代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

游戏ttf字体瘦身脚本

游戏中通常会用到某种特定字体,而某些字体动则10M,对某些游戏(尤其是小游戏)来讲是无法忍受的,此文章主要讲述上个项目中制作的字体裁剪脚本 工具git地址 配置信息(config.json) { // 文本内容(可能为多语言表导出的内容)"txtFile&qu…

常用API(三)

对于常见API的学习,主要学习了关于时间和日期的传统和新增APi 目录 1.Math 2.System 3.Runtime 4.日期和时间 (1)JDK8前传统时间API [1] Date [2] SimpledateFormat [3]Calendar (2)JDK8后新增时间API [1]代替…

JeecgBoot低代码平台简单记录

BasicModal弹窗 Usage 由于弹窗内代码一般作为单文件组件存在,也推荐这样做,所以示例都为单文件组件形式 注意v-bind"$attrs"记得写,用于将弹窗组件的attribute传入BasicModal组件 attribute:是属性的意思,…

嵌入式学习之线程和同步互斥机制

一. 线程的概念 进程的上下文切换: a.上下文:运行一个进程所需要的所有资源。 b.上下文切换:替换原有内容,从访问进程A的所有资源切换到访问进程B的所有资源,是一个耗时操作。 c.为了提高系统性能,引入了一…

HCIP | 实验一

实验内容 配置 1.合理规划IP地址,启用OSPF单区域 R1 R2 R3 R4 R5 R6 2.R1-R2之间启用PPP的pap单向认证 R1 R2 3.R2-R3之间启用PPP的chap双向认证 R2 R3 4.R3-R5-R6之间使用MGRE,R3为hub端,R5,R6为spoke端,要求MGRE…

DS1302实时时钟(51单片机)

一、DS1302时钟 1.DS1302时钟介绍 2.芯片使用 使用芯片时首先要通过数据手册知道芯片功能,根据芯片功能应用。 3.实现DS1302功能 通过对配置寄存器使用DS1302的读写功能 二、实现DS1302读写 1.模块化编程框架 首先对DS1302端口重新定义(换端口名字…

汽车电子推拉力测试机测试流程的详细步骤

在现代汽车技术迅猛发展的今天,汽车电子产品的可靠性已成为确保车辆性能和乘客安全的关键因素。标准下的键合线剪切试验,作为评估这些产品中关键连接点强度的一项测试,扮演着至关重要的角色。本文旨在深入探讨这一测试的重要性、实施流程及其…

Java学习Day20

Vue学习 nodejs的安装与环境配置 1.直接去官网下载合适版本的nodejs( https://nodejs.org/zh-cn/download/prebuilt-installer) 2.解压下载的安装包,将文件路径配置到系统变量的path中,然后确认后退出。可以使用终端来查看安装的nodejs版本。使用winR…

智能合约的未来:解析Web3在智能合约领域的创新

随着区块链技术的不断发展,智能合约已成为Web3生态系统中的核心组成部分。智能合约通过在区块链上自动执行合约条款,推动了去中心化应用(DApp)的广泛应用。它们的核心优势在于去中心化、透明和不可篡改,这使得合同执行…

uniapp切换同一个子组件时,钩子函数只进了一次

给子组件添加不同的 “key” 值,当 key 值改变时,Vue 会认为这是一个不同的组件,并重新创建它 props: ["L1Id"],// 方式1: 使用keycomputed: {// 切换子组件时,发现created、mounted等钩子函数只会进一次,或者用 keykey(){this.ref…

RAG私域问答场景超级详细方案(第一期方案)[1]:工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块)

RAG私域问答场景整体夏详细方案(第一期方案):工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块) 大模型性能的跳阶式增长给文本摘要、信息检索、信息抽取、语义问答等自然语言处理任务带来了卓越的性能提升。同时,LangChain 作为一种基于 LLM 的框架,能够快速…

【autoware】安装ros2 密匙gpg报错,443连接失败

443连接问题解决方法: 访问该网站 https://ping.chinaz.com 输入raw.githubusercontent.com可以得到解析出来的IP sudo vim /etc/hosts

Unity射击游戏开发教程:(31)制造一定追踪行为的敌人

在本文中,我们将介绍如何在两种敌人行为之间切换。本文是前两篇文章的延续,分别介绍了敌人躲避玩家射击以及敌人不断旋转并向玩家射击的情况。我只是介绍如何在这两种行为之间进行转换。 这种新的敌人行为的目标: 当不开火时,敌人可以躲避玩家的射击。射击时,敌人无法躲避…

lvs实战项目-dr模式实现

一、环境准备 主机名IP地址router eth0:172.25.254.100 eth1:192.168.0.100 clienteth0:172.25.254.200lvseth1:192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…

使用FFmpeg实现摄像头RTMP实时推流

在当今的数字时代,视频直播已成为连接人与人之间的重要桥梁,广泛应用于在线教育、远程会议、娱乐直播等多个领域。随着技术的不断进步,人们对于直播的实时性、稳定性和高质量需求日益增加。为了实现高效的视频直播,选择合适的工具和协议至关重要。 RTMP(Real-Time Messagi…

Kotlin OpenCV 视频分析和对象跟踪60 MIL 对象跟踪

Kotlin OpenCV 视频分析和对象跟踪60 MIL 对象跟踪 1 OpenCV 对象跟踪算法2 Kotlin 引入依赖3 OpenCV 下载4 Kotlin OpenCV MIL 对象跟踪 1 OpenCV 对象跟踪算法 算法算法特点1 BOOSTING Tracker基于 AdaBoost 算法。适合于简单的对象跟踪任务。算法较老,在复杂场景…

LLVM理论篇之编译器结构

1、概述 编译器完成源程序到目标程序的翻译工作,这是一个复杂的整体过程。从概念上讲,一个编译程序的整体过程可以分为3个阶段,每个阶段将程序的一种语言表示形式转换成另一种语言表示形式,并且各个阶段在逻辑上是紧密相连的。典…

【C++】STL | list (链表)详解及重要函数的实现

目录 前言 总代码 ListNode类框架的建立 (成员与模板) list类的框架 普通构造 与 empty_init(适合不同类型构造函数的小函数) list的迭代器 引子 operator、operator--(前置与后置) operator 与 operator! operator* 与 …

基于主从Reactor模式的高性能服务器

服务器性能 百万并发:2核2G 2M Linux云服务器,2线程100并发请求,持续1000s,达到百万连接处理且0错误 高并发HTTP服务器项目:性能与功能性测试汇总-CSDN博客(测试详细信息) 主要通信逻辑分析 初…

专利申请后,如何支付年费?

专利申请后,如何支付年费?