针对 CVE-2026-30950 的概念验证演示,该漏洞是 AutoGPT 平台中的一个经过身份验证的 IDOR(缺少授权),允许任何登录用户通过单个 PATCH 请求重新分配(从而劫持)任何其他用户的聊天会话,且无需事先访问该会话。
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L)autogpt-platform-backend >= 0.6.36聊天会话 API 公开了一条路由,允许用户将自己的账户附加到会话记录。该路由受 JWT 身份验证保护,但不检查调用者当前是否拥有正在修改的会话——只检查他们是某个经过身份验证的用户。直接使用受害者的 session_id 和攻击者的 JWT 进行调用会将会话的所有权转移到攻击者。
该漏洞分为三层,每一层都建立了绕过机制:
# autogpt_platform/backend/backend/api/features/chat/routes.py:753-776
@router.patch(
"/sessions/{session_id}/assign-user",
dependencies=[Security(auth.requires_user)],
status_code=200,
)
async def session_assign_user(
session_id: str,
user_id: Annotated[str, Security(auth.get_user_id)],
) -> dict:
await chat_service.assign_user_to_session(session_id, user_id)
return {"status": "ok"}
该路由接受任何经过身份验证的调用者。它传递给服务:
# autogpt_platform/backend/backend/copilot/service.py:291-303
async def assign_user_to_session(session_id: str, user_id: str) -> ChatSessionInfo:
session = await get_chat_session(session_id, None) # ← user_id=None
if not session:
raise NotFoundError(f"Session {session_id} not found")
session.user_id = user_id
session = await upsert_chat_session(session)
return session
服务故意将 user_id=None 传递给数据访问器,而不是转发调用者的用户 ID。然后数据访问器将 None 视为管理员模式并跳过所有权过滤器:
# autogpt_platform/backend/backend/copilot/model.py:355-366
session = await _get_session_from_cache(session_id)
if session:
if user_id is not None and session.user_id != user_id:
logger.warning(f"Session {session_id} user id mismatch")
return None
return session
当 user_id is None 时,条件判断短路,从不执行不匹配检查——任何会话都会返回给任何调用者。然后服务用调用者的 ID 覆盖 session.user_id 并将结果缓存在 Redis 中,因此原始所有者随后的查找会被刚刚绕过的相同不匹配检查拒绝。
任何运行 autogpt-platform-backend >= 0.6.36 且 < 0.6.51 并启用了聊天(copilot)功能的 AutoGPT 平台实例都可能是可被利用的。该漏洞需要:
每个被劫持的会话的影响会随着劫持数量的增加而升级:
CVSS 评分(C:H / I:N / A:L)反映了每个会话的影响范围:对劫持的会话具有高机密性影响,对预先存在的消息内容没有完整性破坏,对合法所有者有可用性影响(锁定)。
此仓库包含一个 POC,涵盖直接触发:攻击者使用其 JWT 针对已知 session_id 发出的单个 PATCH 请求。设置脚本在新引导的 AutoGPT 堆栈中创建两个用户(攻击者 + 受害者),并在声明就绪之前验证前提条件。
setup/ — Docker Compose 环境。
setup.sh 在最后一个易受攻击的标签(autogpt-platform-beta-v0.6.50)处克隆 AutoGPT 到 setup/AutoGPT-src/,从上游的 docker-compose.yml 中启动最少的服务(rest_server、copilot_executor、database_manager、migrate,以及它们的传递依赖:Postgres、Redis、RabbitMQ、Supabase Kong + GoTrue),创建两个测试用户,并验证易受攻击的端点是否已路由。teardown.sh 关闭堆栈并清除卷。pocs/session_hijack.py — 自包含的利用程序。以受害者和攻击者身份登录,以受害者身份创建会话,确认攻击者在利用前没有读取权限,发送单个 PATCH 请求,然后证明所有权已转移且受害者被锁定。
前提条件:Docker、Git、Python 3.10+ 和 uv。
cd setup
./setup.sh
第一次运行会在易受攻击的标签处克隆 AutoGPT 并构建后端镜像(约 3-5 分钟)。后续运行会很快。设置完成后,它会打印出可供粘贴的 POC 调用命令。
运行 POC:
uv run --no-project --with requests \
pocs/session_hijack.py \
--api-url http://localhost:58006 \
--auth-url http://localhost:58000 \
--attacker-email [email protected] \
--attacker-password 'Attacker123!' \
--victim-email [email protected] \
--victim-password 'Victim123!'
为什么使用高端口? 上游的 compose 文件硬编码了容器名称(
supabase-db、rabbitmq等)并发布了默认端口(5432、8000 等)。为了与用户可能已在运行的其他 Supabase / Postgres / RabbitMQ 堆栈共存,setup/docker-compose.override.yml重命名了容器,将网络隔离到一个唯一的项目名称下(autogpt-cve-2026-30950),并将两个外部暴露的端口提升到 58000 范围内。其他内部服务仅绑定到项目的 Docker 网络。
预期输出以以下内容结尾:
[PASS] AUTHENTICATED SESSION HIJACK CONFIRMED
关闭:
cd setup
./teardown.sh
克隆到 setup/AutoGPT-src/ 中的 AutoGPT 源代码在多次关闭之间会保留,因此重新运行时无需重新下载。要完全清除,请删除 setup/AutoGPT-src/。