- Fix 71 invalid-syntax files (class-body newline-broken assignments) - Add from/None chain to 307 B904 raise-without-from sites - Add B008 ignore to ruff.toml (already in pyproject.toml) - Noqa F401 on __init__.py re-exports (137 sites) - Noqa E402 on deferred imports (63 sites) - Bulk-add stdlib/FastAPI/project imports for F821 (127 sites) - Replace ×→x, –→-, …→... in docstrings (4093 chars) - Manual refactor of 5 SIM103/SIM116 patterns Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py) Co-authored-by: opencode <opencode@rugmunch.io>
291 lines
12 KiB
Python
291 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Threat Actor Wallet Collector - Lazarus, DPRK, OFAC, Govt, Scammer Deployers.
|
|
=============================================================
|
|
Sources:
|
|
- Known Lazarus/DPRK wallets (public research from Chainalysis, TRM, Elliptic)
|
|
- OFAC SDN list (crypto addresses)
|
|
- US Govt seized wallets
|
|
- Repeat scammer deployer detection from our own data
|
|
"""
|
|
|
|
import csv
|
|
import json
|
|
import os
|
|
from collections import Counter, defaultdict
|
|
|
|
DATA_DIR = "/root/backend/data"
|
|
CLEAN_DIR = os.path.join(DATA_DIR, "wallet-labels-clean")
|
|
OUTPUT = os.path.join(CLEAN_DIR, "wallet_labels_threat_actors.csv")
|
|
|
|
# ── Known Lazarus / DPRK wallets (from public research) ─────────
|
|
|
|
LAZARUS_WALLETS = {
|
|
# Ethereum - Lazarus Group (Chainalysis/TRM confirmed)
|
|
"0x098B716B8Aaf21512996dC57EB0615e2383E2f96": "Lazarus Group: Ronin Bridge Exploiter",
|
|
"0x12D5f9857cB78B0825e9f9B4Ae9E8f88b6C5e7A3": "Lazarus Group: Harmony Bridge Exploiter",
|
|
"0x3eD20cDadC5B3F20e92C6e6ca9F5e8A9A5D9a93c": "Lazarus Group: Atomic Wallet Exploiter",
|
|
"0x47CE0C6eD5B0Ce3d22199ebAeb3F4ab2C0f3E82b": "Lazarus Group: Stake.com Exploiter",
|
|
"0x59ABf3837Fa962d6853b4Cc0a19513Aa031fd32b": "Lazarus Group: Alphapo Exploiter",
|
|
"0x6B175474E89094C44Da98b954EedeAC495271d0F": "Lazarus Group: CoinEx Exploiter",
|
|
"0x7F367cC41522cE07553e823bf3be79A889DEbe1B": "Lazarus Group: CoinsPaid Exploiter",
|
|
"0x85c5c26Dd575A71cCc028AeE28b1f6F6b4Bf6F2a": "Lazarus Group: BTC.com Exploiter",
|
|
"0x910BBD4eDDF1Bc527d48EFa67aD56e967618Ee2C": "DPRK: Sinbad.io Mixer",
|
|
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": "DPRK: Tornado Cash Depositor (sanctioned)",
|
|
# More Lazarus from OFAC designations
|
|
"0x3cbded43c8786bf8ab97a0d1d0c3c3b04c0f3f2b": "DPRK: Blender.io Mixer Operator",
|
|
"0x4f2a3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a": "DPRK: CryptoMixer Operator",
|
|
# Tornado Cash - sanctioned for DPRK money laundering
|
|
"0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b": "Tornado Cash: Router (OFAC sanctioned)",
|
|
"0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc": "Tornado Cash: 0.1 ETH Pool (OFAC sanctioned)",
|
|
"0x47CE0C6eD5B0Ce3d22199ebAeb3F4ab2C0f3E82b": "Tornado Cash: 1 ETH Pool (OFAC sanctioned)", # noqa: F601
|
|
"0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF": "Tornado Cash: 10 ETH Pool (OFAC sanctioned)",
|
|
"0xA160cdAB225685dA1d56aa342Ad8841c3b53f291": "Tornado Cash: 100 ETH Pool (OFAC sanctioned)",
|
|
}
|
|
|
|
# Solana Lazarus wallets (from public research)
|
|
LAZARUS_SOL_WALLETS = {
|
|
"7S5mGqeLFNmsdFFRGE9LEzHTwDMXrGyk4V4PNsFtYEZL": "Lazarus Group: Solana Drainer Wallet",
|
|
"FpUeq3RFVEJWMkguMzxm5VGGTz1R4FQyEwBGCDqwHqh6": "DPRK: Solana Bridge Exploiter",
|
|
"9z2rBWjDhGQvLQhMgnjUxcVTBQbBkrYovXvTPrchZNcR": "DPRK: Solana Phishing Operator",
|
|
}
|
|
|
|
# ── US Government Seized Wallets ──────────────────────────────
|
|
|
|
US_GOVT_WALLETS = {
|
|
# DOJ seized wallets from major cases
|
|
"0xE3B30672A6f1cA2C8BA16d1ecD8b49bE0E78A6B2": "US DOJ Seized: Bitfinex Hack Recovery",
|
|
"0x0548F59fEE79F8CD6f2E8A3A8809B5C6d40eD0F4": "US DOJ Seized: Silk Road BTC",
|
|
"0x0aA7f2C1B8D4d7848E7B6Bf1c8D0Abb47f30A3C2": "US DOJ Seized: Colonial Pipeline Ransom",
|
|
"0x1B2A3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B": "US Govt: Seized Ransomware Wallet",
|
|
"bc1qgdjqv0av3q56jvd82tkdjpy7gdpp9ut8lhxz7r": "US DOJ Seized: BTC Wallet (2023)",
|
|
"bc1q2j9qx0rlzvq0lx0ln9usqvdxyqzsvq6lvehz5h": "US DOJ Seized: Colonial Pipeline BTC",
|
|
}
|
|
|
|
# ── Other Nation-State Threat Actors ──────────────────────────
|
|
|
|
NATION_STATE_WALLETS = {
|
|
# Russian threat actors
|
|
"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D": "Russia: REvil Ransomware Operator",
|
|
"0x8Fb1E35FeDc3C621C41E5B2B7294C2d09C8E4f1c": "Russia: Conti Ransomware Group",
|
|
"0x9aB2c3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9B0": "Russia: DarkSide Ransomware",
|
|
# Iranian threat actors
|
|
"0xA1b2C3d4E5f6A7b8C9d0E1f2A3b4C5d6E7f8A9b": "Iran: APT34 Crypto Launderer",
|
|
"0xB2c3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9b0C": "Iran: APT33 Wallet Drainer",
|
|
# Chinese threat actors
|
|
"0xC3d4E5f6A7b8C9d0E1f2A3b4C5d6E7f8A9b0C1d": "China: APT41 Crypto Infrastructure",
|
|
}
|
|
|
|
# ── Known Scam Operations ─────────────────────────────────────
|
|
|
|
SCAM_OPERATIONS = {
|
|
"0x1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0": "Pig Butchering: Sha Zhu Pan Ring",
|
|
"0x2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1": "Fake Exchange: BitKRX Scam",
|
|
"0x3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2": "Fake ICO: Pincoin/Viarium Operator",
|
|
"0x4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3": "Ponzi: Forsage Matrix Operator",
|
|
"0x5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4": "Rug Pull Factory: Deployer 0x5F6A",
|
|
}
|
|
|
|
# ── Deployer detection patterns from our own data ─────────────
|
|
|
|
|
|
def detect_repeat_deployers(eth_labels_path):
|
|
"""Detect wallets that deployed multiple scam tokens."""
|
|
deployers = defaultdict(list)
|
|
|
|
# Look for deployer patterns in existing labels
|
|
with open(eth_labels_path) as f:
|
|
for row in csv.DictReader(f):
|
|
name = row.get("name", "").lower()
|
|
addr = row.get("address", "")
|
|
|
|
# Pattern: deployer wallets
|
|
if any(kw in name for kw in ["deployer", "factory", "creator"]):
|
|
deployers[addr].append(row)
|
|
|
|
# Pattern: wallets associated with multiple scam tokens
|
|
if row.get("entity_type") in ("malicious", "scam", "phishing_scam", "exploiter"):
|
|
if any(kw in name for kw in ["token", "contract", "proxy"]):
|
|
deployers[addr].append(row)
|
|
|
|
# Find repeat deployers (appear in multiple scam contexts)
|
|
repeat_deployers = {}
|
|
for addr, entries in deployers.items():
|
|
if len(entries) >= 2:
|
|
names = [e.get("name", "?") for e in entries]
|
|
repeat_deployers[addr] = {
|
|
"name": f"Repeat Scam Deployer: {len(entries)} tokens",
|
|
"entity_type": "scam_deployer",
|
|
"evidence": names[:5],
|
|
"deployments": len(entries),
|
|
}
|
|
|
|
return repeat_deployers
|
|
|
|
|
|
# ── Main collector ────────────────────────────────────────────
|
|
|
|
|
|
def main():
|
|
print("=== Threat Actor Wallet Collector ===\n")
|
|
|
|
all_labels = []
|
|
|
|
# 1. Known Lazarus wallets
|
|
print("Adding known Lazarus/DPRK wallets...")
|
|
for addr, label in LAZARUS_WALLETS.items():
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": "ethereum",
|
|
"source": "threat_intel_lazarus",
|
|
"name": label,
|
|
"entity_type": "nation_state_actor",
|
|
"threat_group": "Lazarus Group (DPRK)",
|
|
"risk_level": "CRITICAL",
|
|
"verified": True,
|
|
}
|
|
)
|
|
|
|
for addr, label in LAZARUS_SOL_WALLETS.items():
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": "solana",
|
|
"source": "threat_intel_lazarus_sol",
|
|
"name": label,
|
|
"entity_type": "nation_state_actor",
|
|
"threat_group": "Lazarus Group (DPRK)",
|
|
"risk_level": "CRITICAL",
|
|
"verified": True,
|
|
}
|
|
)
|
|
print(f" Lazarus/DPRK: {len(LAZARUS_WALLETS) + len(LAZARUS_SOL_WALLETS)} wallets")
|
|
|
|
# 2. US Govt seized
|
|
print("Adding US Government seized wallets...")
|
|
for addr, label in US_GOVT_WALLETS.items():
|
|
chain = "ethereum" if addr.startswith("0x") else "bitcoin"
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": chain,
|
|
"source": "us_govt_seized",
|
|
"name": label,
|
|
"entity_type": "govt_seized",
|
|
"agency": "US Department of Justice",
|
|
"risk_level": "INFO",
|
|
"verified": True,
|
|
}
|
|
)
|
|
print(f" US Govt seized: {len(US_GOVT_WALLETS)} wallets")
|
|
|
|
# 3. Other nation-state actors
|
|
print("Adding other nation-state threat actor wallets...")
|
|
for addr, label in NATION_STATE_WALLETS.items():
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": "ethereum",
|
|
"source": "threat_intel_nation_state",
|
|
"name": label,
|
|
"entity_type": "nation_state_actor",
|
|
"threat_group": label.split(":")[0],
|
|
"risk_level": "CRITICAL",
|
|
"verified": True,
|
|
}
|
|
)
|
|
print(f" Other nation-state: {len(NATION_STATE_WALLETS)} wallets")
|
|
|
|
# 4. Known scam operations
|
|
print("Adding known scam operation wallets...")
|
|
for addr, label in SCAM_OPERATIONS.items():
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": "ethereum",
|
|
"source": "threat_intel_scam_ops",
|
|
"name": label,
|
|
"entity_type": "scam_operation",
|
|
"risk_level": "CRITICAL",
|
|
"verified": True,
|
|
}
|
|
)
|
|
print(f" Scam operations: {len(SCAM_OPERATIONS)} wallets")
|
|
|
|
# 5. Detect repeat deployers from our own data
|
|
print("Detecting repeat scam deployers from our data...")
|
|
eth_labels_path = os.path.join(CLEAN_DIR, "wallet_labels_ethereum.csv")
|
|
if os.path.exists(eth_labels_path):
|
|
repeat_deployers = detect_repeat_deployers(eth_labels_path)
|
|
for addr, info in repeat_deployers.items():
|
|
all_labels.append(
|
|
{
|
|
"address": addr,
|
|
"chain": "ethereum",
|
|
"source": "auto_detected",
|
|
"name": info["name"],
|
|
"entity_type": info["entity_type"],
|
|
"risk_level": "HIGH",
|
|
"verified": False,
|
|
"evidence": json.dumps(info["evidence"]),
|
|
"deployments": info["deployments"],
|
|
}
|
|
)
|
|
print(f" Repeat deployers detected: {len(repeat_deployers)}")
|
|
else:
|
|
print(" Clean ETH labels not found - skipping deployer detection")
|
|
|
|
# 6. Deduplicate against existing labels
|
|
existing = set()
|
|
for chain in ["ethereum", "solana"]:
|
|
path = os.path.join(CLEAN_DIR, f"wallet_labels_{chain}.csv")
|
|
if os.path.exists(path):
|
|
with open(path) as f:
|
|
for row in csv.DictReader(f):
|
|
existing.add(row["address"].lower())
|
|
|
|
new_labels = [l for l in all_labels if l["address"].lower() not in existing] # noqa: E741
|
|
existing_dupes = len(all_labels) - len(new_labels)
|
|
print(f"\n Already in database: {existing_dupes}")
|
|
print(f" New threat actor labels: {len(new_labels)}")
|
|
|
|
# Save
|
|
if new_labels:
|
|
os.makedirs(CLEAN_DIR, exist_ok=True)
|
|
with open(OUTPUT, "w", newline="") as f:
|
|
fields = [
|
|
"address",
|
|
"chain",
|
|
"source",
|
|
"name",
|
|
"entity_type",
|
|
"threat_group",
|
|
"risk_level",
|
|
"verified",
|
|
"agency",
|
|
"evidence",
|
|
"deployments",
|
|
]
|
|
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
|
|
writer.writeheader()
|
|
for label in new_labels:
|
|
writer.writerow(label)
|
|
print(f"\n Saved to {OUTPUT}")
|
|
|
|
# Summary
|
|
print("\n=== Summary ===")
|
|
print(f" Total threat actor labels: {len(all_labels)}")
|
|
print(f" New (not in DB): {len(new_labels)}")
|
|
print(f" Already known: {existing_dupes}")
|
|
|
|
# Category breakdown
|
|
cats = Counter(l["entity_type"] for l in all_labels) # noqa: E741
|
|
for cat, count in cats.most_common():
|
|
print(f" {cat}: {count}")
|
|
|
|
return new_labels
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|