Gh Actions - Context Script Injections

Reading time: 4 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

위험 이해

GitHub Actions는 단계가 실행되기 전에 ${{ ... }} 표현식을 렌더링합니다. 렌더된 값은 해당 단계의 프로그램(예: run 단계의 경우 셸 스크립트)에 붙여넣어집니다. run: 안에 신뢰할 수 없는 입력을 직접 인터폴레이션하면 공격자가 셸 프로그램의 일부를 제어하여 임의의 명령을 실행할 수 있습니다.

Docs: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions and contexts/functions: https://docs.github.com/en/actions/learn-github-actions/contexts

핵심 요점:

  • 렌더링은 실행 전에 발생합니다. 모든 표현식이 해석된 상태로 run 스크립트가 생성된 다음 셸에서 실행됩니다.
  • 많은 contexts는 트리거 이벤트(issues, PRs, comments, discussions, forks, stars 등)에 따라 사용자 제어 필드를 포함합니다. 신뢰할 수 없는 입력에 대한 참고는 다음을 보세요: https://securitylab.github.com/resources/github-actions-untrusted-input/
  • run: 내부의 셸 따옴표는 신뢰할 수 있는 방어책이 아닙니다. 인젝션은 템플릿 렌더링 단계에서 발생하기 때문입니다. 공격자는 조작된 입력을 통해 따옴표를 탈출하거나 연산자를 주입할 수 있습니다.

취약한 패턴 → RCE on runner

취약한 workflow (누군가 새 이슈를 열 때 트리거됨):

yaml
name: New Issue Created
on:
issues:
types: [opened]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: New issue
run: |
echo "New issue ${{ github.event.issue.title }} created"
- name: Add "new" label to issue
uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: new

공격자가 제목이 $(id)인 이슈를 열면, 렌더된 단계는 다음과 같이 됩니다:

sh
echo "New issue $(id) created"

command substitution은 runner에서 id를 실행합니다. 예시 출력:

New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created

따옴표로 감싸는 것으로는 안전해지지 않는 이유:

  • 표현식은 먼저 렌더링된 다음, 그 결과 스크립트가 실행됩니다. 만약 신뢰할 수 없는 값에 $(...), ;, "/', 또는 개행이 포함되어 있다면, 따옴표로 감싸더라도 프로그램 구조를 변경할 수 있습니다.

안전한 패턴 (shell variables via env)

올바른 완화 방법: 신뢰할 수 없는 입력을 환경 변수에 복사한 다음, run 스크립트에서 네이티브 shell 확장($VAR)을 사용하세요. 명령 내부에 ${{ ... }}로 다시 포함시키지 마세요.

yaml
# safe
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: New issue
env:
TITLE: ${{ github.event.issue.title }}
run: |
echo "New issue $TITLE created"

Notes:

  • run: 안에서 ${{ env.TITLE }} 사용을 피하세요. 이는 명령에 템플릿 렌더링을 다시 도입하여 동일한 injection 위험을 초래합니다.
  • untrusted inputs는 env: 매핑을 통해 전달하고 run:에서 $VAR로 참조하는 것이 바람직합니다.

Reader-triggerable surfaces (treat as untrusted)

읽는 사용자가 트리거할 수 있는 이벤트는 많습니다. public repositories에 대해 read 권한만 있는 계정도 여러 이벤트를 트리거할 수 있습니다. 이러한 이벤트로부터 유래한 contexts의 모든 필드는 달리 입증되지 않는 한 공격자에 의해 조작될 수 있다고 간주해야 합니다. 예시:

  • issues, issue_comment
  • discussion, discussion_comment (orgs는 discussions를 제한할 수 있음)
  • pull_request, pull_request_review, pull_request_review_comment
  • pull_request_target (오용 시 위험함, base repo 컨텍스트에서 실행됨)
  • fork (누구나 public repos를 fork할 수 있음)
  • watch (리포지토리에 star를 누르는 행위)
  • workflow_run/workflow_call 체인을 통한 간접적 경로

어떤 특정 필드가 공격자 제어인지 여부는 이벤트별로 다릅니다. GitHub Security Lab의 untrusted input 가이드를 참조하세요: https://securitylab.github.com/resources/github-actions-untrusted-input/

Practical tips

  • run: 안에서 expressions 사용을 최소화하세요. env: 매핑 + $VAR를 선호하세요.
  • 입력을 변환해야 한다면 shell에서 안전한 도구(printf %q, jq -r 등)를 사용해 변환하되, 항상 shell 변수에서 시작하세요.
  • 스크립트, 명령행 플래그, 파일 경로에 branch names, PR titles, usernames, labels, discussion titles, PR head refs 등을 보간할 때 각별히 주의하세요.
  • reusable workflows와 composite actions에도 동일한 패턴을 적용하세요: env로 매핑한 다음 $VAR로 참조합니다.

References

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기