#!/bin/bash # ═══════════════════════════════════════════════════════════════ # LLM Key Hunter — 全网泄露 LLM API Key 搜索 & 验证工具 # ═══════════════════════════════════════════════════════════════ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PATTERNS_FILE="${SCRIPT_DIR}/patterns.conf" RESULTS_DIR="${SCRIPT_DIR}/results" GH_TOKEN="${GH_TOKEN:-}" LIMIT=1000 CONCURRENCY=10 VERIFIED_ONLY=false RAW_MODE=false DEEP_SCAN=false TARGET_ORG="" TARGET_USER="" REPORT_DIR="${RESULTS_DIR}" # ── 颜色 ────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' log_info() { echo -e "${CYAN}[*]${NC} $1"; } log_found() { echo -e "${GREEN}[+]${NC} $1"; } log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } log_err() { echo -e "${RED}[-]${NC} $1"; } # ── 用法 ────────────────────────────────────────────────── usage() { cat < [options] ${YELLOW}模式:${NC} broad GitHub 全局广搜 (gh search code) scan 深度扫描指定 org/user 的所有 repo (trufflehog) verify 验证候选 key 是否 live gist 扫描公开 GitHub Gist full 完整 pipeline: broad → verify → report ${YELLOW}选项:${NC} -t, --token GitHub PAT (或设 GH_TOKEN 环境变量) -o, --org 目标 org (scan 模式) -u, --user 目标 user (scan 模式) -l, --limit 每次搜索结果上限 (默认 1000) -c, --concurrency 并发数 (默认 10) -v, --verified 只输出验证通过的 key -r, --report 报告输出路径 (默认 ./results/) -p, --patterns 自定义 patterns.conf 路径 --raw 跳过验证,输出所有正则匹配 --deep 启用 trufflehog 深度 history 扫描 ${YELLOW}示例:${NC} $0 broad -t ghp_xxx -v $0 scan -o target-org -t ghp_xxx --deep $0 full -t ghp_xxx -v -r ./reports/ $0 gist -t ghp_xxx EOF exit 1 } # ── 参数解析 ────────────────────────────────────────────── parse_args() { while [[ $# -gt 0 ]]; do case "$1" in -t|--token) GH_TOKEN="$2"; shift 2 ;; -o|--org) TARGET_ORG="$2"; shift 2 ;; -u|--user) TARGET_USER="$2"; shift 2 ;; -l|--limit) LIMIT="$2"; shift 2 ;; -c|--concurrency) CONCURRENCY="$2"; shift 2 ;; -v|--verified) VERIFIED_ONLY=true; shift ;; -r|--report) REPORT_DIR="$2"; shift 2 ;; -p|--patterns) PATTERNS_FILE="$2"; shift 2 ;; --raw) RAW_MODE=true; shift ;; --deep) DEEP_SCAN=true; shift ;; *) shift ;; esac done } # ── 依赖检查 ────────────────────────────────────────────── check_deps() { local missing=() command -v gh &>/dev/null || missing+=("gh") command -v curl &>/dev/null || missing+=("curl") command -v jq &>/dev/null || missing+=("jq") command -v grep &>/dev/null || missing+=("grep") if [[ ${#missing[@]} -gt 0 ]]; then log_err "缺少依赖: ${missing[*]}" exit 1 fi if [[ -n "$GH_TOKEN" ]]; then export GH_TOKEN gh auth status &>/dev/null || { echo "$GH_TOKEN" | gh auth login --with-token &>/dev/null || true } fi mkdir -p "$REPORT_DIR" } # ── 加载正则模式 ────────────────────────────────────────── load_patterns() { if [[ ! -f "$PATTERNS_FILE" ]]; then log_err "patterns 文件不存在: $PATTERNS_FILE" exit 1 fi grep -v '^#' "$PATTERNS_FILE" | grep -v '^$' } # ── 构建搜索查询 ────────────────────────────────────────── get_search_queries() { cat <<'QUERIES' sk-proj- extension:env sk-proj- extension:py sk-proj- extension:js sk-proj- extension:ts sk-ant- extension:env sk-ant- extension:py sk-ant- extension:js OPENAI_API_KEY sk- extension:env OPENAI_API_KEY sk- extension:sh ANTHROPIC_API_KEY sk-ant extension:py GOOGLE_API_KEY AIza extension:env GEMINI_API_KEY AIza extension:py HUGGINGFACE_TOKEN hf_ extension:py HUGGINGFACE_API_TOKEN hf_ extension:env GROQ_API_KEY gsk_ extension:env GROQ_API_KEY gsk_ extension:py REPLICATE_API_TOKEN r8_ extension:env openai.api_key sk- extension:ipynb anthropic_api_key sk-ant extension:ipynb api_key=sk- extension:cfg api_key=sk-ant extension:ini sk-ant- extension:jupyter OPENAI_API_KEY extension:ipynb ANTHROPIC_API_KEY extension:ipynb GOOGLE_API_KEY extension:ipynb sk-proj- path:.env sk-ant- path:.env ARK_API_KEY extension:env ARK_API_KEY extension:py VOLC_API_KEY extension:env volces.com ARK_API_KEY extension:py ZHIPUAI_API_KEY extension:env ZHIPUAI_API_KEY extension:py GLM_API_KEY extension:env bigmodel.cn extension:py DASHSCOPE_API_KEY extension:env DASHSCOPE_API_KEY extension:py dashscope.aliyuncs.com extension:py MOONSHOT_API_KEY extension:env MOONSHOT_API_KEY extension:py api.moonshot.cn extension:py MINIMAX_API_KEY extension:env MINIMAX_API_KEY extension:py api.minimaxi.com extension:py HUNYUAN_API_KEY extension:env hunyuan.tencentcloudapi.com extension:py QIANFAN_API_KEY extension:env QIANFAN_API_KEY extension:py qianfan.baidubce.com extension:py SPARK_API_KEY extension:env SPARK_API_KEY extension:py spark-api.xf-yun.com extension:py maas-coding-api.xf-yun.com extension:py maas-coding-api.cn-huabei-1.xf-yun.com extension:py ASTRON_API_KEY extension:env CODING_PLAN_API_KEY extension:env astron-code-latest extension:py maas.xfyun.cn extension:py LINGYIWANWU_API_KEY extension:env api.lingyiwanwu.com extension:py STEPFUN_API_KEY extension:env api.stepfun.com extension:py SILICONFLOW_API_KEY extension:env api.siliconflow.cn extension:py ARK_API_KEY extension:ipynb ZHIPUAI_API_KEY extension:ipynb DASHSCOPE_API_KEY extension:ipynb MOONSHOT_API_KEY extension:ipynb MINIMAX_API_KEY extension:ipynb CSDN_API_KEY extension:env CSDN_API_KEY extension:py CSDN_CODING_PLAN_KEY extension:env CSDN_CODING_PLAN_KEY extension:py CSDN_TOKEN extension:env STARMAP_API_KEY extension:env ai.csdn.net extension:py ai.csdn.net extension:js ai.csdn.net extension:ts ai.csdn.net extension:json ai.csdn.net extension:yaml ai.csdn.net extension:toml glm_for_coding extension:py glm_for_coding extension:js glm_for_coding extension:ts csdn.net/api/model extension:py csdn codingplan extension:env csdn codingplan extension:py HUAWEI_API_KEY extension:env HUAWEICLOUD_API_KEY extension:env CODEARTS_API_KEY extension:env huaweicloud codearts extension:py MIMO_API_KEY extension:env XIAOMI_API_KEY extension:env MIMO_KEY extension:env api.xiaomimimo.com extension:py INFINI_API_KEY extension:env INFINIAI_API_KEY extension:env cloud.infini-ai.com extension:py JD_API_KEY extension:env JDCLOUD_API_KEY extension:env jdcloud.com coding extension:py MTHREADS_API_KEY extension:env code.mthreads.com extension:py KWAIKAT_API_KEY extension:env KUAISHOU_API_KEY extension:env STREAMLAKE_API_KEY extension:env streamlake.com extension:py UCLOUD_API_KEY extension:env COMPSHARE_API_KEY extension:env compshare.cn extension:py ANOMALY_API_KEY extension:env OPENCODE_GO_KEY extension:env opencode.ai extension:py UNICOM_API_KEY extension:env CUCLOUD_API_KEY extension:env YUANJING_API_KEY extension:env cucloud.cn extension:py SCNET_API_KEY extension:env SCNET_API_KEY extension:py scnet.cn extension:api_key extension:env scnet.cn extension:py sk-sp- extension:env sk-sp- extension:py sk-tp- extension:env sk-tp- extension:py coding.dashscope.aliyuncs.com extension:py coding.dashscope.aliyuncs.com extension:env ALIBABA_CODING_PLAN_KEY extension:env ALIBABA_CODING_PLAN_KEY extension:py ZYLOO_API_KEY extension:env ZYLOO_API_KEY extension:py ZYLOO_KEY extension:env ZYLOO_KEY extension:py zyloo.io extension:py zyloo.io extension:env sk-zy- extension:env sk-zy- extension:py fe_oa_ extension:env fe_oa_ extension:py fe_oa_ extension:js fe_oa_ extension:ts fe_oa_ extension:json fe_oa_ extension:yaml fe_oa_ extension:cfg fe_oa_ extension:sh fe_oa_ extension:ipynb freemodel.dev extension:env freemodel.dev extension:py freemodel.dev extension:js freemodel.dev extension:ts freemodel.dev extension:json freemodel.dev extension:yaml FREEMODEL_API_KEY extension:env FREEMODEL_API_KEY extension:py FREEMODEL_KEY extension:env FREEMODEL_KEY extension:py cc.freemodel.dev extension:py api.freemodel.dev extension:py vip-sg.freemodel.dev extension:py work.freemodel.dev extension:py QUERIES } # ── GitHub 全局广搜 (Python 引擎) ───────────────────────── mode_broad() { log_info "启动 Python 搜索引擎 (github_search.py)..." local candidates_file="${REPORT_DIR}/candidates.json" local search_args=() search_args+=("--output" "$candidates_file") # Pass tokens if [[ -n "$GH_TOKEN" ]]; then search_args+=("--token" "$GH_TOKEN") fi # Support multi-token via GH_TOKENS env var (colon-separated) if [[ -n "${GH_TOKENS:-}" ]]; then IFS=':' read -ra TOKEN_ARRAY <<< "$GH_TOKENS" for t in "${TOKEN_ARRAY[@]}"; do search_args+=("--token" "$t") done fi # Gitee token if [[ -n "${GITEE_TOKEN:-}" ]]; then search_args+=("--gitee-token" "$GITEE_TOKEN") fi # Flags [[ "$RAW_MODE" == "true" ]] && search_args+=("--no-commits" "--no-topics") python3 "${SCRIPT_DIR}/github_search.py" "${search_args[@]}" || { log_err "Python 搜索引擎失败,回退到 bash 广搜..." _mode_broad_bash return } local final_count final_count=$(jq 'length' "$candidates_file" 2>/dev/null || echo 0) log_found "广搜完成: $final_count 个唯一候选文件" echo "$candidates_file" } # ── Bash fallback 广搜 (原版) ───────────────────────────── _mode_broad_bash() { log_info "Bash 广搜 (fallback)..." local candidates_file="${REPORT_DIR}/candidates.json" echo '[]' > "$candidates_file" local queries queries=$(get_search_queries) local total=0 while IFS= read -r query; do [[ -z "$query" || "$query" =~ ^# ]] && continue log_info "搜索: $query" local encoded_query encoded_query=$(echo "$query" | sed 's/ /+/g') local results results=$(gh api "search/code?q=${encoded_query}&per_page=${LIMIT}" \ --jq '[.items[] | {repository: {full_name: .repository.full_name}, path: .path, url: .html_url}]' 2>/dev/null || echo '[]') local count count=$(echo "$results" | jq 'length' 2>/dev/null | tr -dc '0-9') count=${count:-0} if [[ "$count" -gt 0 ]]; then log_found "找到 $count 个候选文件" jq -s '.[0] + .[1]' "$candidates_file" <(echo "$results") > "${candidates_file}.tmp" 2>/dev/null || true mv "${candidates_file}.tmp" "$candidates_file" 2>/dev/null || true total=$((total + count)) fi sleep 7 done <<< "$queries" jq 'unique_by(.url)' "$candidates_file" > "${candidates_file}.dedup" 2>/dev/null || true mv "${candidates_file}.dedup" "$candidates_file" 2>/dev/null || true local final_count final_count=$(jq 'length' "$candidates_file" 2>/dev/null || echo 0) log_found "广搜完成: $final_count 个唯一候选文件" echo "$candidates_file" } # ── 从候选文件提取 key ──────────────────────────────────── extract_keys() { local candidates_file="$1" local keys_file="${REPORT_DIR}/extracted_keys.txt" > "$keys_file" local patterns patterns=$(load_patterns) log_info "从候选文件提取 key..." jq -r '.[].url' "$candidates_file" 2>/dev/null | while read -r url; do [[ -z "$url" ]] && continue local raw_url raw_url=$(echo "$url" | sed 's|github.com|raw.githubusercontent.com|;s|/blob/|/|') local content content=$(curl -sL --max-time 10 "$raw_url" 2>/dev/null) || continue [[ -z "$content" ]] && continue while IFS='|' read -r provider regex desc; do [[ -z "$regex" ]] && continue echo "$content" | grep -oE "$regex" 2>/dev/null | while read -r key; do echo "${provider}|${key}|${url}|${desc}" done done <<< "$patterns" done | sort -u > "$keys_file" local key_count key_count=$(wc -l < "$keys_file" | tr -d ' ') log_found "提取完成: $key_count 个候选 key" echo "$keys_file" } # ── 主入口 ──────────────────────────────────────────────── main() { [[ $# -lt 1 ]] && usage local mode="$1" shift parse_args "$@" check_deps case "$mode" in broad) mode_broad ;; full) log_info "=== 完整 Pipeline 启动 ===" local candidates candidates=$(mode_broad) local keys keys=$(extract_keys "$candidates") if [[ "$RAW_MODE" == "false" ]]; then bash "${SCRIPT_DIR}/verify.sh" "$keys" "$REPORT_DIR" "$VERIFIED_ONLY" else log_info "Raw 模式,跳过验证" cat "$keys" fi ;; scan) bash "${SCRIPT_DIR}/scan.sh" \ --org "$TARGET_ORG" \ --user "$TARGET_USER" \ --token "$GH_TOKEN" \ --concurrency "$CONCURRENCY" \ --deep "$DEEP_SCAN" \ --report "$REPORT_DIR" ;; verify) local keys="${REPORT_DIR}/extracted_keys.txt" [[ ! -f "$keys" ]] && { log_err "无候选 key 文件,先运行 broad 或 full"; exit 1; } bash "${SCRIPT_DIR}/verify.sh" "$keys" "$REPORT_DIR" "$VERIFIED_ONLY" ;; gist) bash "${SCRIPT_DIR}/gist.sh" --token "$GH_TOKEN" --report "$REPORT_DIR" ;; *) usage ;; esac } main "$@"