FREE
Part of the AI/LLM Hacking Course — 90 Days
The most vulnerable endpoint wasn’t the chatbot. It was the internal document summariser — the one endpoint that accepted uploaded files and had no rate limiting, no authentication requirement for the API path itself (only for the frontend UI that called it), and a system prompt loaded from a configuration file that the frontend team had embedded with the production database read credentials because “the AI needs to look up customer context.” That endpoint wasn’t in the scope document because the client didn’t know it was AI-powered. Reconnaissance is how you find what the client didn’t think to tell you about.
🎯 What You’ll Master in Day 20
⏱️ Day 20 · 3 exercises · Kali Terminal + Think Like Hacker + Kali Terminal
✅ Prerequisites
- Day 17 — Burp Suite for LLM Testing
— the Burp proxy setup from Day 17 is the primary tool for passive traffic analysis in Day 20
- Basic familiarity with JavaScript bundle analysis — searching minified JS for API route strings
- Python with requests and BeautifulSoup installed — used in Exercise 1’s automated endpoint detection
📋 LLM API Reconnaissance — Day 20 Contents
Days 16 through 19 assumed you already knew which AI endpoints to test. Day 20 covers how you actually find them. Day 21 uses the endpoint inventory produced here to test authentication bypass patterns specific to AI APIs — gaps that emerge because AI endpoints are often added to applications that already have authentication infrastructure, and the integration isn’t always as careful as the original implementation.
Passive Discovery via Traffic Analysis
The most reliable way to find AI endpoints is the same as the most reliable way to find any API endpoint: browse the application with your proxy running and watch what requests it makes. The difference for AI specifically is knowing what patterns to look for. AI API calls have characteristic signatures in traffic that make them identifiable even before you’ve confirmed the endpoint’s purpose.
In Burp’s HTTP history, filter for requests to the domains you’d expect: api.openai.com, api.anthropic.com, generativelanguage.googleapis.com (Gemini), bedrock-runtime.*.amazonaws.com (AWS Bedrock), api.together.ai, api.cohere.ai. For self-hosted or proxied deployments, filter for POST requests with JSON bodies containing “messages” arrays or “prompt” fields. Filter for streaming responses with chunked transfer encoding — LLMs usually stream their output. Filter for unusually variable response lengths for similar input sizes — dead giveaway of generative output rather than deterministic API responses.
⏱️ 20 minutes · Kali Linux · Python · Burp Suite
This exercise builds a Python tool that parses a Burp HTTP history export and automatically identifies all AI-related requests — producing an endpoint inventory before you’ve manually reviewed a single request.
Browse any web application with AI features (or your test setup from Day 17).
Proxy → HTTP history → Right-click any request → Save all items
Save as: ai_recon_capture.xml
Step 2: cd ~/ai-security-course && source venv/bin/activate
pip install beautifulsoup4 lxml
nano day20_ai_recon.py
Step 3: Build the XML parser and endpoint detector:
from bs4 import BeautifulSoup
import base64, json, re, os
AI_HOST_PATTERNS = [
“openai.com”, “anthropic.com”, “googleapis.com”,
“amazonaws.com/bedrock”, “together.ai”, “cohere.ai”, “mistral.ai”
]
AI_BODY_PATTERNS = [‘”messages”‘, ‘”prompt”:’, ‘”model”:’, ‘”choices”‘, ‘”completion”‘]
def parse_burp_xml(filepath):
with open(filepath, “rb”) as f:
soup = BeautifulSoup(f, “lxml-xml”)
items = []
for item in soup.find_all(“item”):
host = item.find(“host”)
url = item.find(“url”)
req = item.find(“request”)
resp = item.find(“response”)
if not (host and url): continue
req_body = “”
if req and req.get(“base64”) == “true”:
req_body = base64.b64decode(req.text).decode(“utf-8″, errors=”ignore”)
resp_body = “”
if resp and resp.get(“base64”) == “true”:
resp_body = base64.b64decode(resp.text).decode(“utf-8″, errors=”ignore”)[:2000]
host_str = host.text if host.text else “”
is_ai_host = any(p in host_str for p in AI_HOST_PATTERNS)
body_score = sum(1 for p in AI_BODY_PATTERNS if p in req_body + resp_body)
if is_ai_host or body_score >= 2:
items.append({
“host”: host_str,
“url”: url.text if url.text else “”,
“ai_host”: is_ai_host,
“body_score”: body_score,
“req_snippet”: req_body[:200],
})
return items
Step 4: Run the parser and display findings:
items = parse_burp_xml(“ai_recon_capture.xml”)
print(f”AI endpoints found: {len(items)}”)
for item in sorted(items, key=lambda x: -x[“body_score”]):
print(f” [{item[‘body_score’]}] {item[‘url’][:80]}”)
Step 5: Extend with endpoint deduplication:
unique_paths = list(set(i[“url”].split(“?”)[0] for i in items))
print(f”\nUnique AI endpoint paths: {len(unique_paths)}”)
for path in sorted(unique_paths):
print(f” {path}”)
📸 Screenshot your tool output showing discovered AI endpoints. Share in #day20-recon on X.
AI Backend Fingerprinting
Knowing which model powers an endpoint changes your testing strategy. GPT-4o and Claude Sonnet have robust safety training — Tier 3 injection techniques are more likely to be needed. A Llama fine-tune without additional safety RLHF might respond to Tier 1 techniques. An older GPT-3.5 deployment might expose LLM10 vulnerabilities that newer models’ token limits don’t. Fingerprinting before testing routes you to the right payload families from the start.
Five fingerprinting signals in order of reliability. First: response headers — some deployments include model names in custom headers like X-Model-ID, X-AI-Provider, or similar. Second: error responses — trigger an error by sending a malformed request and read the error message format; each provider has distinctive error schemas. Third: response timing with length correlation — measure how response time scales with output token count; different models have different per-token generation speeds. Fourth: model-specific behaviour probes — certain prompts produce distinctive outputs on specific models. Fifth: JavaScript constants — frontend code often includes model names in configuration objects.
JavaScript Bundle Analysis for Undocumented Endpoints
Frontend JavaScript is almost always the most accurate documentation for what API endpoints exist. Developers write the frontend against the backend. If there’s a /api/summarise endpoint, there’s JavaScript somewhere that calls it. That JavaScript doesn’t disappear when the endpoint becomes undocumented — it just becomes harder to find.
The standard approach: download the minified JavaScript bundle, run it through a formatter (prettier, js-beautify), and search for API path strings and fetch/axios/XMLHttpRequest calls. Regex patterns for AI-specific strings — “chat”, “completion”, “prompt”, “llm”, “ai”, “model” — surface the most relevant calls. For larger codebases, linkfinder or similar tools automate the extraction and produce a clean endpoint list without requiring manual JS review.
⏱️ 20 minutes · No tools needed
Reconnaissance workflow design determines what you find and how quickly. This exercise designs the complete recon workflow for a new engagement from arrival to finished scope document.
“Test the AI-powered features of our SaaS application.
Scope: *.targetapp.com. AI features documented: one chatbot on /chat.”
You have 6 hours total. Your goal: find ALL AI surfaces, not just /chat.
QUESTION 1 — Phase 1: Passive recon (30 minutes).
What do you do in the first 30 minutes?
List every recon action in order.
What specific Burp configurations do you set before touching the application?
QUESTION 2 — JS analysis strategy.
The application has 12 JavaScript files, total 2.3MB minified.
You have 20 minutes for JS analysis.
What tools and commands do you run?
What output do you produce?
QUESTION 3 — Fingerprinting each discovered endpoint.
After passive recon, you have 6 endpoints in your HTTP history
that show AI indicators. You have 15 minutes to fingerprint all 6.
What’s your fingerprinting sequence for maximum coverage?
QUESTION 4 — Prioritisation for testing.
You found: one public chatbot, one admin-only report generator,
one unauthenticated document summariser, one internal analytics AI,
one customer sentiment analyser, one product recommendation engine.
Rank these by attack surface priority (1=highest priority to test).
Justify your ranking — what makes the unauthenticated one so interesting?
QUESTION 5 — Scope document structure.
You need to produce a one-page scope document for the engagement
before starting injection testing. What sections does it contain?
What format helps you during the actual testing phase?
📸 Share your recon workflow design in #day20-recon on X.
Authentication and Access Control Assessment per Endpoint
AI endpoints added to existing applications inherit whatever access control model the developer remembered to apply. That’s often less rigorous than the application’s main authentication infrastructure, because AI features are frequently added quickly, by frontend teams who expect the backend to handle security, when the backend team assumed the frontend would add auth headers. The gap between those assumptions is where unauthenticated AI endpoints live.
Test each discovered endpoint without authentication first. Not as a bypass attempt — as a baseline check. Remove the Authorization header, the session cookie, or whatever credential the normal request includes, and send the request. If it returns a 200 and a valid AI response, you have an unauthenticated AI endpoint. That’s a High finding on its own, before any injection testing.
Building the Prioritised Test Scope Document
The scope document is your test plan. It should fit on one page and contain exactly the information you need during testing — nothing more. Endpoint URL, fingerprinted backend model, authentication status (authenticated/unauthenticated/unknown), RAG integration (yes/no/unknown), agent tool access (yes/no/unknown), and test priority ranking. That’s it. The detail goes in the report later. During testing, you need to know which endpoint to test next and why.
⏱️ 20 minutes · Kali Linux · Python · Burp running
This exercise combines the endpoint discovery tool from Exercise 1 with fingerprinting probes to produce a complete one-command AI recon tool — endpoint discovery, fingerprinting, and scope document generation in a single run.
nano day20_full_recon.py
Step 2: Import the parse_burp_xml function from day20_ai_recon.py
(or copy it into this file)
Step 3: Add fingerprinting function:
import requests, time
from urllib.parse import urlparse
def fingerprint_endpoint(url, session_cookie=None):
headers = {}
if session_cookie:
headers[“Cookie”] = session_cookie
results = {}
# Auth check: try without credentials
try:
r = requests.post(url, json={“message”:”test”}, timeout=5)
results[“auth_required”] = r.status_code == 401 or r.status_code == 403
results[“status_without_auth”] = r.status_code
except: results[“auth_required”] = “unknown”
# Header fingerprint
try:
r = requests.post(url, json={“message”:”test”}, headers=headers, timeout=5)
ai_headers = {k:v for k,v in r.headers.items()
if any(w in k.lower() for w in [“model”,”ai”,”provider”,”inference”])}
results[“ai_headers”] = ai_headers
results[“streaming”] = “chunked” in r.headers.get(“transfer-encoding”,””).lower()
except: pass
# Error fingerprint
try:
r = requests.post(url, json={}, headers=headers, timeout=5)
if “openai” in r.text.lower(): results[“provider”] = “OpenAI”
elif “anthropic” in r.text.lower(): results[“provider”] = “Anthropic”
elif “google” in r.text.lower(): results[“provider”] = “Google”
else: results[“provider”] = “Unknown”
except: pass
return results
Step 4: Generate scope document:
def generate_scope_doc(endpoints, fingerprints):
print(“\n” + “=”*60)
print(“AI ENDPOINT SCOPE DOCUMENT”)
print(“=”*60)
for ep in sorted(endpoints, key=lambda x: -x[“body_score”]):
url = ep[“url”]
fp = fingerprints.get(url, {})
auth = “UNAUTHENTICATED” if fp.get(“auth_required”) == False else “auth required”
provider = fp.get(“provider”, “Unknown”)
print(f”\n[Priority: {ep[‘body_score’]}]”)
print(f” URL: {url[:70]}”)
print(f” Provider: {provider}”)
print(f” Auth: {auth}”)
print(f” Stream: {fp.get(‘streaming’, ‘unknown’)}”)
if auth == “UNAUTHENTICATED”:
print(f” ⚠ UNAUTHENTICATED AI ENDPOINT — test immediately”)
Step 5: Run the complete recon pipeline:
endpoints = parse_burp_xml(“ai_recon_capture.xml”)
fingerprints = {ep[“url”]: fingerprint_endpoint(ep[“url”]) for ep in endpoints[:5]}
generate_scope_doc(endpoints, fingerprints)
📸 Screenshot your scope document output showing fingerprinted endpoints. Share in #day20-recon on X. Tag #day20complete
📋 LLM API Reconnaissance — Day 20 Reference Card
✅ Day 20 Complete — LLM API Reconnaissance
Passive endpoint discovery via traffic analysis, AI backend fingerprinting using five signals, JavaScript bundle analysis for undocumented endpoints, authentication gap testing per endpoint, data flow mapping, and the one-command recon tool that produces a prioritised scope document. Phase 2 of the course — Days 16 through 20 — is complete. Days 21 onward cover advanced exploitation: authentication bypass patterns in AI APIs, prompt injection at scale, RAG attack chains, and the full professional AI red team report format.
🧠 Day 20 Check
LLM API Reconnaissance FAQ
How do you find undocumented AI endpoints?
How do you fingerprint which AI model powers an endpoint?
Why does AI API reconnaissance matter before injection testing?
What are common signs that an endpoint uses an LLM backend?
📚 Further Reading
- Day 21 — LLM Authentication Bypass — Using the endpoint inventory from Day 20 to test authentication bypass patterns specific to AI APIs — gaps that emerge from inconsistent integration of auth layers.
- Day 17 — Burp Suite for LLM Testing — The Burp proxy configuration and workflow that powers the passive discovery phase of Day 20’s reconnaissance methodology.
- AI/LLM Hacking Course Hub — The complete 90-day course overview — Phase 1 (Days 1-15), Phase 2 (Days 16-20), and the advanced phases ahead.
- LinkFinder — JavaScript Endpoint Extractor — The open-source tool for extracting API endpoint references from JavaScript bundles — essential for finding undocumented AI endpoints in frontend code.

