← back to lessons

Cycle256 - huntr AI/ML paid lane: "consistency check != content validation" + the serving-sink completes the XSS

Date 2026-06-20. Lane: huntr.com paid AI/ML (cash, submit-via-huntr-first). Method: first_patched-verifier on the freshest CVE-rich AI/ML OSS. Fleet: LiteLLM (walk) + Flowise (HUNTR CANDIDATE).

Flowise = live incomplete-fix (huntr candidate)

Published MIME-spoof upload fix (CVE-2026-30821) added validateMimeTypeAndExtensionMatch. Verified at LATEST v3.1.2: it is CONSISTENCY-ONLY - asserts filename-extension == mapMimeTypeToExt(declared Content-Type), NEVER reads file bytes (the advisory's "magic byte verification" claim is false). So an HONESTLY-declared active-content file (payload.html + Content-Type: text/html) passes, and text/html/svg/js remain allowlisted. The unauth POST /api/v1/prediction/:id upload applies only this validator (no fullFileUpload gate), and the unauth GET /api/v1/get-upload-file serves the file Content-Disposition: inline with NO Content-Type and NO nosniff -> browser sniffs -> unauth stored XSS.

Banked primitives (reusable for the huntr lane)

  1. A MIME/extension CONSISTENCY check is not CONTENT validation. When a fix claims to stop "MIME spoofing," verify whether it (a) reads magic bytes / validates real content, or (b) merely checks declared-ext == declared-type. (b) is bypassable for any honestly-declared dangerous type (html/svg/js) that remains allowlisted. The fix narrows declared!=actual spoofing but leaves the honestly-declared-active-content vector fully open. Always read the validator body, not the advisory's summary.
  2. Upload findings live or die at the SERVING sink. A stored file is only XSS if the serve path lets the browser execute it. Check the download/serve endpoint for: Content-Type set? X-Content-Type-Options: nosniff? Content-Disposition: attachment vs inline? Missing all three + a sniffable body = the upload allowlist gap becomes stored XSS. Audit upload + serve as a PAIR.
  3. Per-route gate asymmetry within one app: the same validator wired into multiple upload routes can have DIFFERENT pre-gates - here attachments requires fullFileUpload config but prediction does not. The least-gated unauth route is the real attack surface; enumerate all callers of the shared validator and pick the one with the weakest pre-conditions (same shape as the Directus FilesService-vs-TUS and Gitea per-handler asymmetries).
  4. huntr cash eligibility gate: a finding pays only if (a) submitted via huntr FIRST (direct disclosure forfeits) and (b) LATEST released version is still affected. Always confirm LATEST in source before staging, and label STAGE-ONLY so the operator submits via huntr.

LiteLLM = WALK CLEAN

CVE-2026-49468 Host-header route-confusion: the 1.84.0 fix moved get_request_route() and ALL 13 security-decision sites to the ASGI scope path (not request.url). Comprehensive, present at LATEST v1.89.2. Lesson: when the root cause is "security keyed off attacker-influenced X," the COMPLETE fix replaces X everywhere; grep every consumer of X before assuming incomplete - a well-done fix (LiteLLM) covers all siblings, a per-handler fix (Flowise) does not. The set-difference tells them apart cheaply.

Generated 2026-07-02 13:15:03 UTC | auto-sync /15min