Download tools/exploit_tool.py from lablab-ai-amd-developer-hackathon/Threat_Hunter: direct link, hf CLI and curl.
- Browser
- Download file 14.6 kB
-
https://huggingface.co/spaces/lablab-ai-amd-developer-hackathon/Threat_Hunter/resolve/main/tools/exploit_tool.py
- Command line
-
hf download hf://spaces/lablab-ai-amd-developer-hackathon/Threat_Hunter/tools/exploit_tool.py
-
curl -L -o exploit_tool.py https://huggingface.co/spaces/lablab-ai-amd-developer-hackathon/Threat_Hunter/resolve/main/tools/exploit_tool.py
14.6 kB
| # tools/exploit_tool.py | |
| # ๅ่ฝ๏ผGitHub Exploit / PoC ๆๅฐ Tool | |
| # Harness ๆฏๆฑ๏ผGraceful Degradation๏ผ้็ด็ๅธ๏ผ+ Observability๏ผๅๅญๅๆฅ่ช๏ผ | |
| # ๆๆ่ ๏ผๆๅก C๏ผAnalyst Agent Pipeline๏ผ | |
| # | |
| # ไฝฟ็จๆนๅผ๏ผ | |
| # from tools.exploit_tool import search_exploits | |
| # | |
| # ๆถๆงๅฎไฝ๏ผ | |
| # Analyst Agent ็ใ็ฌฌไบ้ปๆใโ ๆๅฐ CVE ็ๅ ฌ้ Exploit/PoC | |
| # ๆๅ ฌ้ exploit = ๆปๆ้ๆชปๆฅตไฝ = ้ขจ้ชๆๆจ HIGH | |
| import json | |
| import os | |
| import time | |
| import hashlib | |
| import logging | |
| from datetime import datetime, timezone | |
| import requests | |
| logger = logging.getLogger("ThreatHunter") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๅธธๆธ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| GITHUB_API_BASE = "https://api.github.com/search/repositories" | |
| RESULTS_PER_PAGE = 10 # ้ๅถ็ตๆๆธ้๏ผ้ฟๅ context ้้ท | |
| REQUEST_TIMEOUT = 20 # ็ง | |
| # Rate limit ๆงๅถ | |
| # GitHub ็ก่ช่ญ 10 req/min๏ผๆ่ช่ญ 30 req/min | |
| RATE_LIMIT_WITH_TOKEN = 2.0 # ๆ Token: 30 req/min โ 2s ้้๏ผไฟๅฎ๏ผ | |
| RATE_LIMIT_WITHOUT_TOKEN = 6.0 # ็ก Token: 10 req/min โ 6s ้้๏ผไฟๅฎ๏ผ | |
| MAX_RETRIES = 2 | |
| # ้ข็ทๅฟซๅ | |
| CACHE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data") | |
| CACHE_TTL = 3600 * 24 # 24 ๅฐๆ้ๆ | |
| # ไธๆฌก่ซๆฑๆ้๏ผๆจก็ต็ด rate limiter๏ผ | |
| _last_request_time = 0.0 | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ่ผๅฉๅฝๅผ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def _get_github_token() -> str: | |
| """ๅๅพ GitHub Token๏ผๅชๅ ๅพ config๏ผๅ้ธ็ฐๅข่ฎๆธ๏ผ""" | |
| try: | |
| from config import GITHUB_TOKEN | |
| if GITHUB_TOKEN: | |
| return GITHUB_TOKEN | |
| except ImportError: | |
| pass | |
| return os.getenv("GITHUB_TOKEN", "") | |
| def _get_cache_path(cve_id: str) -> str: | |
| """ๅๅพ้ข็ทๅฟซๅๆชๆก่ทฏๅพ""" | |
| safe_name = hashlib.md5(cve_id.encode()).hexdigest()[:12] | |
| return os.path.join(CACHE_DIR, f"exploit_cache_{cve_id}_{safe_name}.json") | |
| def _read_cache(cve_id: str) -> dict | None: | |
| """่ฎๅ้ข็ทๅฟซๅ๏ผ้ๆๆไธๅญๅจๅๅณ None""" | |
| cache_path = _get_cache_path(cve_id) | |
| try: | |
| if os.path.exists(cache_path): | |
| with open(cache_path, "r", encoding="utf-8") as f: | |
| cached = json.load(f) | |
| cached_time = cached.get("_cached_at", 0) | |
| if time.time() - cached_time < CACHE_TTL: | |
| logger.info("[OK] Exploit cache hit: %s", cve_id) | |
| return cached | |
| else: | |
| logger.info("[INFO] Exploit cache expired: %s", cve_id) | |
| except (json.JSONDecodeError, IOError) as e: | |
| logger.warning("[WARN] Exploit cache read failed: %s", e) | |
| return None | |
| def _write_cache(cve_id: str, data: dict) -> None: | |
| """ๅฏซๅ ฅ้ข็ทๅฟซๅ""" | |
| try: | |
| os.makedirs(CACHE_DIR, exist_ok=True) | |
| cache_path = _get_cache_path(cve_id) | |
| data["_cached_at"] = time.time() | |
| with open(cache_path, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False, indent=2) | |
| except (IOError, PermissionError) as e: | |
| logger.warning("[WARN] Exploit cache write failed: %s", e) | |
| def _rate_limit() -> None: | |
| """Rate limiter โ ็ขบไฟไธ่ถ ้ GitHub API ้้""" | |
| global _last_request_time | |
| token = _get_github_token() | |
| interval = RATE_LIMIT_WITH_TOKEN if token else RATE_LIMIT_WITHOUT_TOKEN | |
| elapsed = time.time() - _last_request_time | |
| if elapsed < interval: | |
| wait = interval - elapsed | |
| logger.info("[WAIT] GitHub rate limit: waiting %.1fs", wait) | |
| time.sleep(wait) | |
| _last_request_time = time.time() | |
| def _classify_exploit_type(name: str, description: str) -> str: | |
| """ | |
| ๆ นๆ repo ๅ็จฑๅๆ่ฟฐๅคๆท exploit ้กๅใ | |
| ๅ้ก่ฆๅ๏ผ | |
| - ๅซ scanner/detect/check โ scanner | |
| - ๅซ weaponized/exploit-db/metasploit โ weaponized | |
| - ๅ ถไป๏ผpoc/proof/test/demo ็ญ๏ผโ poc | |
| """ | |
| text = f"{name} {description}".lower() | |
| # ๆๆๅจ / ๅตๆธฌๅทฅๅ ท | |
| scanner_keywords = ["scanner", "detect", "check", "scan", "checker", "finder"] | |
| for kw in scanner_keywords: | |
| if kw in text: | |
| return "scanner" | |
| # ๆญฆๅจๅ exploit | |
| weaponized_keywords = ["weaponized", "exploit-db", "metasploit", "payload", "shellcode", "reverse shell"] | |
| for kw in weaponized_keywords: | |
| if kw in text: | |
| return "weaponized" | |
| # ้ ่จญ็บ PoC | |
| return "poc" | |
| def _determine_risk_indicator(exploit_count: int, api_available: bool) -> str: | |
| """ | |
| ๆ นๆ exploit ๆธ้ๅ API ๅฏ็จๆงๅคๅฎ้ขจ้ชๆๆจใ | |
| ้่ผฏ๏ผ | |
| exploit_count > 0 โ "HIGH"๏ผๆๅ ฌ้ exploit = ้ซ้ขจ้ช๏ผ | |
| exploit_count == 0 ไธ API ๅฏ็จ โ "LOW"๏ผ็ขบ่ชๆฒๆๅ ฌ้ exploit๏ผ | |
| API ไธๅฏ็จ โ "UNKNOWN (API limited)" | |
| """ | |
| if not api_available: | |
| return "UNKNOWN (API limited)" | |
| if exploit_count > 0: | |
| return "HIGH" | |
| return "LOW" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๆ ธๅฟๆฅ่ฉข้่ผฏ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def _query_github_api(cve_id: str) -> dict | None: | |
| """ | |
| ๅผๅซ GitHub Search API๏ผๅๅณๅๅง JSON response dictใ | |
| ๅคฑๆๅๅณ Noneใ | |
| ๅ ๅซ๏ผ | |
| - Rate limiting | |
| - ้่ฉฆๆฉๅถ๏ผๆๅค MAX_RETRIES ๆฌก๏ผ | |
| - Timeout ่็ | |
| """ | |
| token = _get_github_token() | |
| headers = { | |
| "Accept": "application/vnd.github.v3+json", | |
| } | |
| if token: | |
| headers["Authorization"] = f"token {token}" | |
| # ๆๅฐ็ญ็ฅ๏ผ็จ CVE ID ไฝ็บ้้ตๅญ + exploit ้ๅฎ | |
| params = { | |
| "q": f"{cve_id} exploit", | |
| "sort": "stars", | |
| "order": "desc", | |
| "per_page": RESULTS_PER_PAGE, | |
| } | |
| for attempt in range(1, MAX_RETRIES + 1): | |
| _rate_limit() | |
| try: | |
| logger.info("[QUERY] GitHub API search: %s (attempt %d)", cve_id, attempt) | |
| response = requests.get( | |
| GITHUB_API_BASE, | |
| params=params, | |
| headers=headers, | |
| timeout=REQUEST_TIMEOUT, | |
| ) | |
| if response.status_code == 200: | |
| return response.json() | |
| if response.status_code in (403, 429): | |
| # Rate limit ่ขซ่งธ็ผ | |
| retry_after = response.headers.get("Retry-After", "60") | |
| logger.warning( | |
| "[WARN] GitHub API %d (rate limited), Retry-After: %ss", | |
| response.status_code, retry_after | |
| ) | |
| time.sleep(min(int(retry_after), 30)) # ๆๅค็ญ 30 ็ง | |
| continue | |
| if response.status_code >= 500: | |
| logger.warning("[WARN] GitHub API %d (server error)", response.status_code) | |
| time.sleep(2) | |
| continue | |
| # ๅ ถไป้ฏ่ชค็ขผ | |
| logger.warning("[WARN] GitHub API returned %d: %s", response.status_code, response.text[:200]) | |
| return None | |
| except requests.exceptions.Timeout: | |
| logger.warning("[WARN] GitHub API timeout (%ds)", REQUEST_TIMEOUT) | |
| continue | |
| except requests.exceptions.ConnectionError: | |
| logger.warning("[WARN] GitHub API connection failed (network issue)") | |
| continue | |
| except requests.exceptions.RequestException as e: | |
| logger.warning("[WARN] GitHub API request error: %s", e) | |
| return None | |
| return None # ๆๆ้่ฉฆ้ฝๅคฑๆ | |
| def _parse_github_response(raw: dict, cve_id: str) -> dict: | |
| """ | |
| ๅฐ GitHub Search API ๅๅง response ่ฝๆ็บ Tool ่ผธๅบๆ ผๅผใ | |
| ่ฝๆ mapping๏ผ | |
| response.items[].full_name โ repo_name | |
| response.items[].html_url โ url | |
| response.items[].stargazers_count โ stars | |
| response.items[].language โ language | |
| response.items[].updated_at โ last_updated | |
| response.items[].description โ description | |
| """ | |
| exploits = [] | |
| items = raw.get("items", []) | |
| for repo in items: | |
| repo_name = repo.get("full_name", "") | |
| description = repo.get("description", "") or "" | |
| exploit_type = _classify_exploit_type(repo_name, description) | |
| # ๆๅๆๅพๆดๆฐๆฅๆ๏ผๅชๅๆฅๆ้จๅ๏ผ | |
| updated_at = repo.get("updated_at", "") | |
| if "T" in updated_at: | |
| updated_at = updated_at.split("T")[0] | |
| exploits.append({ | |
| "repo_name": repo_name, | |
| "url": repo.get("html_url", ""), | |
| "stars": repo.get("stargazers_count", 0), | |
| "language": repo.get("language", "") or "Unknown", | |
| "last_updated": updated_at, | |
| "description": description[:300], # ๆชๆท้้ทๆ่ฟฐ | |
| "type": exploit_type, | |
| }) | |
| exploit_count = len(exploits) | |
| risk_indicator = _determine_risk_indicator(exploit_count, api_available=True) | |
| return { | |
| "cve_id": cve_id, | |
| "source": "GitHub API", | |
| "exploit_count": exploit_count, | |
| "exploits": exploits, | |
| "risk_indicator": risk_indicator, | |
| } | |
| def _search_exploits_impl(cve_id: str) -> str: | |
| """ | |
| search_exploits ็ๆ ธๅฟๅฏฆไฝ๏ผ่ CrewAI @tool ่งฃ่ฆ๏ผๆนไพฟๅฎๅ ๆธฌ่ฉฆ๏ผใ | |
| ้็ด็ๅธ๏ผ | |
| 1. ๆฅ GitHub Search API โ ๆๅๅๅฟซๅ | |
| 2. API ๅคฑๆ๏ผ403/429 rate limit๏ผโ ่ฎ้ข็ทๅฟซๅ | |
| 3. ๅฟซๅไนๆฒๆ โ ๅๅณ exploit_count: 0, risk_indicator: "UNKNOWN" | |
| 4. ไปปไฝๆช้ ๆ้ฏ่ชค โ ๅๅณๅฎๅ จ็้ ่จญ็ตๆ๏ผ็ตไธ crash๏ผ | |
| """ | |
| try: | |
| # ๆธ ็ CVE ID๏ผๅป็ฉบ็ฝใ็ตฑไธๅคงๅฏซ๏ผ | |
| cve_id = cve_id.strip().upper() | |
| if not cve_id: | |
| logger.warning("[WARN] Exploit Tool received empty CVE ID input") | |
| return json.dumps({ | |
| "cve_id": "", | |
| "source": "GitHub API (error)", | |
| "exploit_count": 0, | |
| "exploits": [], | |
| "risk_indicator": "UNKNOWN", | |
| "error": "No CVE ID provided", | |
| }, ensure_ascii=False, indent=2) | |
| logger.info("[QUERY] Exploit search: %s", cve_id) | |
| # ๅ่ฉฆ GitHub API ๆฅ่ฉข | |
| raw = _query_github_api(cve_id) | |
| if raw is not None: | |
| result = _parse_github_response(raw, cve_id) | |
| # ๅฏซๅ ฅๅฟซๅไพ้ข็ทไฝฟ็จ | |
| _write_cache(cve_id, result) | |
| logger.info( | |
| "[OK] Exploit search success: %s -> %d exploit(s), risk_indicator=%s", | |
| cve_id, result['exploit_count'], result['risk_indicator'] | |
| ) | |
| return json.dumps(result, ensure_ascii=False, indent=2) | |
| # API ๅคฑๆ โ ๅ่ฉฆ่ฎๅฟซๅ | |
| cached = _read_cache(cve_id) | |
| if cached: | |
| cached.pop("_cached_at", None) | |
| cached["source"] = "GitHub API (cache)" | |
| cached["error"] = f"GitHub API unavailable, using cached data for '{cve_id}'" | |
| logger.info("[OK] Exploit using cache: %s", cve_id) | |
| return json.dumps(cached, ensure_ascii=False, indent=2) | |
| # ๅฎๅ จๆฒๆ่ณๆ | |
| empty_result = { | |
| "cve_id": cve_id, | |
| "source": "GitHub API (unavailable)", | |
| "exploit_count": 0, | |
| "exploits": [], | |
| "risk_indicator": "UNKNOWN (API limited)", | |
| "error": f"GitHub API unavailable and no cache for '{cve_id}'", | |
| } | |
| logger.info("[INFO] Exploit no data for: %s", cve_id) | |
| return json.dumps(empty_result, ensure_ascii=False, indent=2) | |
| except Exception as e: | |
| # ๆๅพไธ้้ฒ็ท๏ผไปปไฝๆช้ ๆ้ฏ่ชค้ฝไธ่ฝ่ฎ Agent crash | |
| logger.error("[FAIL] Exploit Tool unexpected error: %s", e, exc_info=True) | |
| error_result = { | |
| "cve_id": cve_id if 'cve_id' in dir() else "", | |
| "source": "GitHub API (error)", | |
| "exploit_count": 0, | |
| "exploits": [], | |
| "risk_indicator": "UNKNOWN", | |
| "error": f"Unexpected error: {str(e)}", | |
| } | |
| return json.dumps(error_result, ensure_ascii=False, indent=2) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # CrewAI @tool ๅ ่ฃ๏ผAgent ๅผๅซ็จ๏ผ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # โ ๏ธ ้่ฆ๏ผไฝฟ็จใๅปถ้ฒ่ผๅ ฅใๆจกๅผ๏ผLazyToolLoader๏ผ | |
| # ๅๅ ๏ผ้ฟๅ ๅจ import ้ๆฎตๅฐฑ่งธ็ผ CrewAI ็ tool ่จปๅ | |
| def _create_tool(): | |
| """ๅปถ้ฒๅปบ็ซ CrewAI Tool๏ผๅ ๅจ Agent ๅฏฆ้ไฝฟ็จๆๆ import""" | |
| from crewai.tools import tool | |
| def search_exploits(cve_id: str) -> str: | |
| """ๆๅฐๆๅฎ CVE ็ๅ ฌ้ Exploit ๅ PoC ็จๅผ็ขผ๏ผ้้ GitHub API๏ผใ | |
| ่ผธๅ ฅๅฎไธ CVE ID๏ผๅฆ "CVE-2021-44228"๏ผ๏ผๅๅณๅ ฌ้ exploit ็ๆธ้ใ้ฃ็ตใๆๆธ็ญ่ณ่จใ | |
| ๆๅ ฌ้ exploit = ๆปๆ้ๆชปๆฅตไฝ = ้ขจ้ชๆๆจ HIGHใ""" | |
| return _search_exploits_impl(cve_id) | |
| return search_exploits | |
| # โโ ๅปถ้ฒ่ผๅ ฅๆฉๅถ๏ผ่ nvd_tool.py ็ธๅๆจกๅผ๏ผโโโโโโโโโโโโโโโโโโ | |
| class _LazyToolLoader: | |
| def __init__(self): | |
| self._tool = None | |
| def _load(self): | |
| if self._tool is None: | |
| self._tool = _create_tool() | |
| def search_exploits(self): | |
| self._load() | |
| return self._tool | |
| _loader = _LazyToolLoader() | |
| def __getattr__(name): | |
| """ๆจก็ตๅฑค็ด __getattr__๏ผๆฏๆด from tools.exploit_tool import search_exploits""" | |
| if name == "search_exploits": | |
| return _loader.search_exploits | |
| raise AttributeError(f"module 'tools.exploit_tool' has no attribute {name!r}") | |