excavate: add AIApplicationExtractor for LLM endpoints, SDKs, and leaked AI provider keys#3235
Conversation
|
Hi, thanks for the PR! This looks really cool, I think we definitely want this. Just a couple notes:
|
…ked AI provider keys Adds a passive excavate submodule (AIApplicationExtractor) that fingerprints AI/LLM application surface (provider API endpoints, embedded client SDKs for LangChain / LlamaIndex / OpenAI) and flags leaked AI provider API keys (OpenAI, Anthropic, Google Gemini) from HTTP_RESPONSE bodies and JavaScript, emitting TECHNOLOGY and FINDING events. Reuses excavate's existing YARA pipeline; no new dependency. Detected secrets are redacted before emission. Includes two ModuleTestBase tests (positive + negative) mirroring the existing extractor test conventions. Signed-off-by: Devam Shah <devamshah91@gmail.com>
ea6db5f to
5c4c47e
Compare
|
Thanks @liquidsec — glad it's a fit! Retargeted to The Validated on the Totally fine if this lands in |
|
Had a chance to do a cursory review, there are a few things: The redactions:BBOT doesn't have any other cases where credentials harvested from a target are redacted in output. There are some instances where we redact our own api key secrets etc. But the philosophy here is, this is an offensive tool, many users will want to go on to actually use the harvested secrets. They'd also still be in output.json, so the potential usecase is definitely narrow at best and it adds complexity. The URL is also in the output, and re-fetch is trivial anyway. Missing Severity And Confidence in yara metadata:The custom severity and confidence fields get passed on to the eventual finding. In this case, we hope the regexes are good enough we can call them "confirmed". The severity we could debate about. However, in the absence of a severity it will be emitted as informational which is probably not what we want. RegexI suspect the provider endpoint stuff (looking for "api.openai.com") but not be specific enough, and i believe would just fire on any mentions of them Im not totally sure that the api key patterns wouldn't occasionally false positive but im not sure Process() overrideunderstand why you did this, but i think rather than do that override i'd almost rather split it into two separate excavate signatures, and then do some modifications to the base to support technology events. I'd need to think a little deeper into it first, but I can definitely help with that part. overallThe only other thing that makes me pause a little bit with this is the potential overlap with the trufflehog module. I'd want to do some more testing to see if trufflehog would pick this up... There's a larger question here which is the degree with which we want to outsource secret detection to trufflehog vs do it ourselves. I think what im leaning towards right now, pending actually getting my hands dirty w/this and testing, is leaving the API key detection to trufflehog and handle the detection of actual LLM endpoints with an excavate submodule. |
Summary
Adds a passive
excavatesubmodule (AIApplicationExtractor) that fingerprints AI/LLM application surface — provider API endpoints, embedded client SDKs (LangChain / LlamaIndex / OpenAI) — and flags leaked AI provider API keys (OpenAI, Anthropic, Google Gemini) directly fromHTTP_RESPONSEbodies and JavaScript, emittingTECHNOLOGYandFINDINGevents.Problem / motivation
BBOT already excels at passively surfacing attack surface from HTTP responses, but it has no native recognition of the AI/LLM layer that now ships in most modern web front-ends. Two concrete gaps:
api.openai.com/v1/chat/completions,api.anthropic.com,generativelanguage.googleapis.com, Azure OpenAI deployments, and SSE chat streams, plus@langchain/*/llama_index/openaiSDK markers. These are high-value pivots for an assessor (prompt-injection sinks, server-side proxy endpoints, model/billing abuse) yet are invisible to current modules.sk-…,sk-proj-…), Anthropic (sk-ant-api03-…), and Google (AIza…) keys are regularly committed into front-end JS and inline<script>blobs. A leaked inference key is a direct financial-loss and data-exfiltration primitive.This reuses excavate's existing YARA pipeline (excavate already ships ~140 YARA references), so there is no new dependency or architectural change.
Change
AIApplicationExtractor(ExcavateRule)inbbot/modules/internal/excavate.py, placed alongside the other detection extractors (FunctionalityExtractor,SerializationExtractor,ErrorExtractor).ai_provider_endpoint— OpenAI / Anthropic / Google Generative AI / Azure OpenAI hosts +/v1/chat/completions.ai_chat_streaming— SSE chat stream, gated on bothtext/event-streamand an LLM-shapeddata: {... "delta"/"choices" ...}frame to avoid matching generic event streams.ai_client_library— LangChain (@langchain/…), LlamaIndex (llama[_-]?index), and OpenAI SDK import/instantiation markers.ai_provider_apikey— anchored, length-bounded key formats for OpenAI, OpenAI project, Anthropic, and Google keys.process()maps each matched YARA string identifier to either aTECHNOLOGYevent (endpoint/SDK fingerprint, requires a host) or aFINDINGevent (leaked key). Secrets are redacted (prefix…suffix) before they enter scan output rather than echoed in full.bbot/test/test_step_2/module_tests/test_module_excavate.pyfollowing the existingTestExcavateCSP/TestExcavateSerialization*conventions: a positive case (all techs + all three providers' keys) and a negative case (benign AI marketing copy + a Stripesk_live_key + "blockchain language" decoy) asserting zero false positives and that full secrets are never emitted.produced_eventson the parent module is intentionally left unchanged, consistent with the existing extractors (SerializationExtractor/CSPExtractoralready emitFINDING/DNS_NAMEwithout enumerating them there).Security rationale
sk-/sk-ant-/AIzakeys in client-delivered JS are a textbook instance; detecting them passively closes a common, high-impact reconnaissance gap.\b-anchored and length-bounded, provider key namespaces are mutually exclusive (hyphenatedsk-ant-/sk-proj-cannot match the 48-char legacy OpenAI pattern), the SSE rule requires two independent signals, and detected secrets are redacted before emission.Testing / validation
Validated against a fresh clone with
yara-python4.5.2 (the version BBOT pins) and the fullbbot/testharness on Python 3.12:bbot/testpytest run:TECHNOLOGYevents (OpenAI/Anthropic/Gemini API, chat-completion endpoint, LangChain, OpenAI SDK) andFINDINGevents for the three leaked provider keys, and that full secrets are never echoed. The negative case asserts zero AIFINDING/TECHNOLOGYon benign content (Stripesk_live_, AI marketing prose, "blockchain language model").ruff check→ "All checks passed!";ruff format --check→ clean (line-length 119).The submodule follows the same shape as the existing
SerializationExtractor/FunctionalityExtractorextractors, so it carries no standalone doc file (module docs are generated from metadata).