01 Summary
정적 분석에서 PraisonAI의 issue-comment workflow가 외부 fork branch name을 unquoted shell command에 직접 보간함을 확인했습니다. 별도의
foxirain/langflow low-privilege canary workflow에서 non-collaborator의 @claude comment로 같은 sink를 재현해 Bash command execution과 $GITHUB_PATH 상태 변경을 관찰했습니다. PraisonAI production workflow 자체에서 payload를 실행하거나 credential에 접근하지는 않았습니다.구분 | 확인 내용 |
Component | PraisonAI GitHub Actions issue-comment workflow |
Attack Input | shell metacharacter가 포함된 external fork branch name |
Preconditions | untrusted actor가 대상 PR에서 workflow trigger comment를 만들 수 있어야 함 |
Sink | pr.data.head.ref를 git fetch ...:${{ pr_branch }} shell command에 직접 보간 |
Verified | Bash injection으로 /tmp/p0canary 생성 및 $GITHUB_PATH 변조 |
Impact | 동적 확인: low-privilege canary의 shell execution·다음 step PATH 영향 / 정적 확인: target workflow의 후속 privileged context 노출 가능성 |
Fix Principle | trusted-actor gate, branch 값을 env로 전달·quote, fixed local ref와 least privilege 사용 |
목차 · 섹션으로 이동
01 SummaryAttack ChainDisclosure Timeline02 Vulnerability취약점 개요03 Approach조사 대상04 Root Cause원인 분석05 Reproduction & Validation재현 및 검증06 Impact & Fix영향과 수정확인한 범위제외한 범위수정07 Turning PointsKey Turning PointsInvestigation LogRole & Credit08 Provenance분석 기준과 증거 보존09 Artifacts관련 파일2026-08-30 증거 보강 감사10 References11 Takeaways느낀 점
Attack Chain
검증 결론: non-collaborator가 제어한 branch metadata가 low-privilege canary job의 Bash에서 해석되어 canary path와
$GITHUB_PATH가 바뀌고, 다음 canary step의 gh resolution에 영향을 준 사실을 동적으로 확인했습니다. PraisonAI target job의 write 권한, id-token: write, GitHub App token을 소비하는 후속 Claude step은 workflow 정적 분석으로 확인했으며, privileged credential의 실제 사용·노출은 시험하지 않았습니다.Disclosure Timeline
Date | Event | Evidence |
기존 기록 없음 | Reported | DB Reported 및 본문에 날짜 기록 없음 |
기존 기록 없음 | Fixed | 본문에는 fixed version만 있고 날짜 기록 없음 |
기존 기록 없음 | Released | DB Released 및 본문에 날짜 기록 없음 |
2026-08-05 | Published | DB Published 및 본문 공개일 |
02 Vulnerability
취약점 개요
PraisonAI의
.github/workflows/claude.yml은 issue comment에 @claude가 들어오면 PR branch를 fetch한 뒤 GitHub App token을 사용하는 작업을 수행했습니다. fork의 branch 이름은 외부 contributor가 정할 수 있지만 Bash run: block 안에 quoting 없이 들어갔습니다. 이 target 구조는 정적 분석으로 확인했습니다.별도의
foxirain/langflow controlled run에서는 shell metacharacter가 포함된 branch 이름으로 PR을 만들고 댓글을 달자 Bash가 첫 git fetch ...:poc 뒤의 주입 command를 실행했습니다. 무해한 canary 파일을 만들고 $GITHUB_PATH 앞에 controlled directory를 추가해 다음 low-privilege canary step의 gh 해석이 바뀌는 것까지 확인했습니다.공격자가 제어하는 값 | fork PR의 branch name과 issue comment |
취약 sink | Bash run: block의 unquoted git fetch ...:<branch> |
Trigger 문제 | 댓글 작성자의 신뢰 여부 검사 없음 |
후속 권한 | 후속 Claude step에 GitHub App token·OAuth token 전달, job에 id-token: write 선언; 실제 credential·OIDC 사용은 미검증 |
03 Approach
조사 대상
CI workflow에서는
${{ }} 표현식이 shell script로 들어가는 위치를 모두 찾고, 값의 출처가 trusted repository인지 fork metadata인지 구분했습니다. YAML상 문자열이라는 사실은 Bash에서 안전한 인자가 된다는 뜻이 아닙니다.또한 injection step 자체의 기본 token만 보지 않고 뒤에 실행되는 credential-bearing steps를 확인했습니다.
$GITHUB_PATH와 workspace는 step 사이에 상태를 전달하므로 초기 무권한 step이 후속 도구 해석에 영향을 줄 수 있었습니다.외부 contributor가 만든 branch name이 실제 runner의 shell 문법으로 해석되고, 그 영향이 privileged 후속 step까지 지속되는가?
04 Root Cause
원인 분석
취약 명령은
git fetch origin pull/<number>/head:${{ steps.check_fork.outputs.pr_branch }} 형태였습니다. branch name이 shell word 안에 그대로 붙었지만 quote가 없어 command separator와 redirection 같은 metacharacter가 Bash 문법이 됐습니다.workflow trigger는
@claude 문자열을 확인했지만 commenter가 collaborator인지 확인하지 않았습니다. 따라서 branch를 만든 outside contributor가 자신의 PR에 댓글을 달아 취약 job을 시작할 수 있었습니다. PraisonAI target source에는 write job permissions와 id-token: write가 선언돼 있었고, 후속 Claude step은 GitHub App token과 OAuth token을 전달받았습니다. 이 credential의 실제 사용·노출은 시험하지 않았습니다.claude.yml · branch metadata가 shell source가 된 지점
run: | git fetch origin \ pull/${{ github.event.issue.number }}/head:\ ${{ steps.check_fork.outputs.pr_branch }} # expected data: branch name # actual interpreter: Bash source text
입력에서 영향까지의 경로
- 외부 contributor가 shell metacharacter가 든 fork branch를 만듭니다.
- 그 branch로 PR을 열고
@claude댓글을 답니다.
- workflow가 신뢰 actor 검사 없이 시작됩니다.
- branch name이 unquoted Bash command에 삽입됩니다.
- controlled run에서는 canary와
$GITHUB_PATH변경이 다음 low-privilege canary step에 남았습니다. 별도로 PraisonAI source에서 같은 상태 전달 경로 뒤에 credential-bearing Claude step이 있음을 확인했습니다.
05 Reproduction & Validation
재현 및 검증
foxirain/langflow의 통제된 read-only GitHub Actions run에서 외부 fork branch를 사용했습니다. payload는 secret을 읽거나 repository를 수정하지 않고 /tmp/p0canary 생성과 controlled directory를 $GITHUB_PATH에 추가하는 동작으로 제한했습니다. 이 run은 PraisonAI production workflow가 아닙니다.다음 low-privilege canary step에서
command -v gh가 /tmp/p0canary/gh를 가리키는지 확인했습니다. 이 동적 결과와 PraisonAI target source의 credential-bearing 후속 step을 분리해 분석함으로써, privileged credential을 실제로 사용하지 않고 잠재적 영향 경로를 확인했습니다.# outside fork branch name contains shell metacharacters # comment: @claude # observe controlled run 25624510066 and canary path
검사 | 입력·조건 | 관찰 | 의미 |
외부 Trigger | outside contributor의 @claude | job 시작 | trusted actor gate 없음 |
Canary | branch name payload | /tmp/p0canary 생성 | Bash command injection |
Step 지속성 | $GITHUB_PATH 변경 | 다음 step의 gh 경로 변경 | 후속 단계 영향 |
비파괴 범위 | token/secret 접근 안 함 | 외부 반출 없음 | 검증 payload 제한 |
실제 controlled workflow run의 핵심 관찰
run id: 25624510066 trigger actor: outside contributor branch-derived shell payload: executed canary: /tmp/p0canary created next step command resolution: gh=/tmp/p0canary/gh secrets exfiltrated: no
06 Impact & Fix
영향과 수정
Controlled canary는 외부 contributor가 제어한 branch name으로 임의 shell syntax를 실행하고,
$GITHUB_PATH를 통해 다음 low-privilege canary step의 gh resolution을 바꿀 수 있음을 확인했습니다. PraisonAI target job에는 GitHub App token을 사용하는 후속 Claude step과 id-token: write 선언이 있어 동일한 상태 조작이 privileged context에 영향을 줄 잠재적 chain이 존재했습니다. 다만 App token의 사용·탈취, OIDC token 발급, repository mutation은 재현하지 않았으므로 실제 영향은 credential availability, token scope, 후속 action·command 동작에 조건부입니다.확인한 범위
- controlled low-privilege GitHub Actions run의 shell command execution
- outside contributor의 self-trigger
$GITHUB_PATH를 통한 다음 step 영향
제외한 범위
- secret 값의 출력·외부 전송
- 실제 repository 변조
- LLM prompt injection
수정
PraisonAI 4.6.40에는 PR branch 값을
PR_BRANCH 환경변수로 전달하고 "${PR_BRANCH}"로 quote하는 수정이 적용되어 branch-name command injection이 차단되었습니다. Trusted-actor gate, 고정된 pr-${PR_NUMBER} local ref, job token·OIDC 권한 최소화는 추가 권고사항이며 4.6.40에는 적용되지 않았습니다.07 Turning Points
Key Turning Points
핵심 변화는 AI prompt 문제로 보던 초기 해석을 deterministic shell injection으로 교정하고, 비파괴 canary와 권한 경계를 분리한 것입니다.
Stage | Prior Belief | New Evidence | Revised Judgment | Trigger |
1. Vulnerability class | Claude workflow이므로 prompt injection일 수 있다고 보았습니다. | Branch name은 LLM 호출 전에 Bash source text로 해석됐습니다. | LLM 판단과 무관한 deterministic command injection으로 분류했습니다. | Bash sink가 모델 호출보다 먼저 실행된다는 확인이 계기였습니다. |
2. Proof boundary | Secret 탈취나 repository write가 더 강한 증거가 될 수 있었습니다. | Canary file과 PATH shadowing만으로 shell execution과 다음 step 영향을 확인했습니다. | Credential을 읽지 않고 비파괴 side effect에서 실험을 중단했습니다. | Controlled run의 안전한 검증 경계를 정한 것이 계기였습니다. |
3. Privilege boundary | Injection step의 권한만 보면 영향이 low privilege로 끝난다고 볼 수 있었습니다. | $GITHUB_PATH 상태가 다음 step에 남았고 target source에는 credential-bearing Claude step과 id-token: write가 있었습니다. | Low-privilege 동적 증거와 target privileged context의 정적 가능성을 분리했습니다. | Step 간 상태 지속성 확인이 계기였습니다. |
4. Fix scope | Branch quoting만으로 workflow 보안 문제가 모두 해결된다고 볼 수 있었습니다. | Quote는 shell injection을 막지만 untrusted actor trigger와 넓은 job 권한은 별도 문제로 남았습니다. | Quoting fix와 actor authorization·least privilege 권고를 분리했습니다. | 수정 범위를 다시 검토한 것이 계기였습니다. |
Investigation Log
전체 판단 변화 원문
판단 변화
Prompt Injection
처음에는 Claude가 포함된 workflow라 prompt injection으로 볼 여지가 있었습니다. 그러나 취약 동작은 모델 호출 전에 Bash가 branch 이름을 해석하면서 발생했습니다. LLM 판단과 무관한 deterministic command injection으로 분류했습니다.
Secret 탈취
실제 runner에서 검증하더라도 secret 출력이나 repository write는 필요하지 않았습니다. canary와 PATH shadowing만으로 arbitrary shell execution과 다음 step에 대한 영향이 입증돼 공격을 그 선에서 중단했습니다.
현재 Step 권한
controlled run의 주입 step은 read-only 권한이었습니다. 여기서는
$GITHUB_PATH와 파일이 다음 canary step까지 지속되는 사실을 동적으로 확인했습니다. 별도로 PraisonAI target source에는 GitHub App token과 OAuth token을 전달받는 후속 Claude step 및 id-token: write 선언이 있었습니다. OIDC token 발급이나 credential 사용은 재현하지 않았으며, workflow를 job 전체의 잠재적 상태 흐름으로 평가했습니다.Trigger와 Quote
branch 값을 quote하는 수정만으로 shell injection은 막을 수 있지만 누구나 privileged AI job을 시작하는 문제는 남습니다. 최종 방어 조건에 actor authorization과 최소 권한을 함께 포함했습니다.
Role & Credit
- Portfolio author: Taegu Ha
- Credit: Public Attribution
- Credit Note: 기존 기록 없음
- 검증은 PraisonAI target source의 정적 분석과 별도 controlled low-privilege canary run으로 구분했습니다.
08 Provenance
322da5d0e45b3e13e6487c4a8c9eb04124233f56 링크는 current main에서 삭제된 감사·보존 파일의 historical snapshot입니다. 그 밖의 current artifact 링크는 main을 유지합니다.분석 기준과 증거 보존
항목 | 최종 감사 결과 |
소스 기준 | PraisonAI checkout 4985415e61043a1734903f6a6e8ca85efdaede7c |
원본 검증 | external fork branch metadata, workflow interpolation, canary file·GITHUB_PATH side effect를 연결 |
GitHub 보존 상태 | Historical audit commit 64c54ec 당시 evidence 5개와 root manifest.json에 size·SHA-256을 고정했습니다. 현재 main은 evidence 4개이며 root manifest.json은 제거됐습니다. |
무결성 | 사례별 SHA256SUMS 검증 통과, root manifest의 모든 file hash와 실제 파일 일치, README 상대 링크 확인 완료 |
보존 경계·제약 | controlled run의 gh run view --log 텍스트 캡처는 보존; PraisonAI production credential·실제 secret·GitHub 원본 log ZIP·raw Codex session은 제외 |
감사 완료 | 2026-08-30 · controlled run log 추가, source/provenance와 전체 manifest 재검증 후 GitHub push 완료 |
이전 Summary 상세 · 원문 보존
외부 fork의 branch 이름이 따옴표 없이
git fetch 명령에 삽입됐고 누구나 @claude 댓글로 job을 시작할 수 있었습니다. 통제된 foxirain/langflow read-only canary run에서 shell execution과 $GITHUB_PATH 영향을 동적으로 확인했습니다. PraisonAI의 privileged workflow는 source snapshot으로 별도 분석했으며 target credential 접근이나 production 실행은 하지 않았습니다.Taegu Ha · PraisonAI · CWE-862 · CVSS 10.0
2026년 8월 5일 공개 · PraisonAI 4.6.40에서 수정 · 공식 기록
구분 | 확인 내용 |
프로젝트·컴포넌트 | PraisonAI GitHub Actions issue-comment workflow |
공격 입력 | shell metacharacter가 포함된 external fork branch name |
필요 조건 | untrusted actor가 대상 PR에서 workflow trigger comment를 만들 수 있어야 함 |
취약 지점 | pr.data.head.ref를 git fetch ...:${{ pr_branch }} shell command에 직접 보간 |
검증 결과 | Bash injection으로 /tmp/p0canary 생성 및 $GITHUB_PATH 변조 |
영향 | 동적 확인: low-privilege canary의 shell execution·다음 step PATH 영향 / 정적 확인: target workflow의 후속 privileged context 노출 가능성 |
수정 원칙 | trusted-actor gate, branch 값을 env로 전달·quote, fixed local ref와 least privilege 사용 |
09 Artifacts
관련 파일
2026-08-30 증거 보강 감사
공개 advisory·CVE 레코드와 함께 취약 workflow snapshot 및 controlled run 25624510066의 169줄
gh run view --log 텍스트 캡처를 독립 보존했습니다. 이 캡처는 PraisonAI production log나 GitHub 원본 log ZIP이 아니며, secret 값은 검증에서도 수집하지 않았습니다.- github-advisory.json — 실제 workflow run과 canary 검증이 포함된 공개 report
- cve-record.json — CVEProject 공개 레코드와 fix reference
- p0-claude-canary-25624510066.log —
foxirain/langflowread-only run의 전체gh run view --log텍스트 캡처; shell injection과 다음 step PATH persistence를 직접 기록
10 References
11 Takeaways
느낀 점
CI에서 metadata는 단순 데이터가 아니라 여러 interpreter를 차례로 통과하는 입력이었습니다. GitHub expression이 YAML을 지나 Bash source가 되는 경계를 그리자 취약점이 명확해졌습니다.
또 하나 남은 기준은 step 단위 권한만 보지 않는 것입니다. 파일과 environment file이 다음 step으로 이어지면 앞 단계의 작은 injection이 뒤 단계의 큰 권한을 빌릴 수 있습니다. 실제 피해를 만들지 않고도 이 연결을 canary로 보여줄 수 있었습니다.