$npx -y skills add elementalsouls/Claude-BugHunter --skill hunt-exceptional-conditionsHunt mishandling of exceptional conditions — feed an endpoint malformed/unexpected input (wrong type, broken JSON, oversized field, null byte) and make it fail OPEN or leak internals: a verbose stack-trace / framework error page that discloses ORM internals, server file paths, li
| 1 | # HUNT-EXCEPTIONAL-CONDITIONS — Verbose Errors / Fail-Open (A10:2025) |
| 2 | |
| 3 | ## What actually pays |
| 4 | |
| 5 | Well-built apps catch errors and return a clean, generic message. A broken app, |
| 6 | when handed input it didn't expect, throws an unhandled exception and renders a |
| 7 | **developer error page** straight to the client — leaking the stack trace, the |
| 8 | ORM/query internals, server-side file paths, and framework/library versions. |
| 9 | That disclosure is the finding (and it arms SQLi/RCE/path attacks next). |
| 10 | |
| 11 | ## Recon |
| 12 | |
| 13 | Any endpoint that parses input is a candidate; the richest are: |
| 14 | |
| 15 | ``` |
| 16 | JSON APIs that expect typed fields: POST /api/* with {numbers, ids, enums} |
| 17 | Endpoints with numeric/id path or query params: /item/{id}, ?page=, ?quantity= |
| 18 | Search / filter / sort params |
| 19 | File or content-type sensitive uploads |
| 20 | ``` |
| 21 | |
| 22 | ## Attack — send what the code didn't anticipate |
| 23 | |
| 24 | Take a known-good request and break ONE assumption at a time: |
| 25 | |
| 26 | - **Wrong type:** a field the app expects to be a number/string is sent as an |
| 27 | array or object — `{"rating":"x","comment":[1,2,3]}`, `{"quantity":{}}`. |
| 28 | - **Malformed body:** truncated/!invalid JSON, an unterminated string, a stray |
| 29 | brace, a wrong/missing Content-Type. |
| 30 | - **Boundary/oversized:** a very long string, a huge/negative/overflow number. |
| 31 | - **Null byte / control chars** embedded in a value. |
| 32 | |
| 33 | ``` |
| 34 | POST /api/Feedbacks {"rating":"notanumber","comment":[1,2,3]} |
| 35 | GET /item/' OR /item/%00 (also exercises the error path) |
| 36 | ``` |
| 37 | |
| 38 | Watch the RESPONSE BODY, not just the status: a 500 (or even a 200/400) whose |
| 39 | body contains a stack trace or framework error page is the signal. |
| 40 | |
| 41 | ## What counts as a leak (the success signal) |
| 42 | |
| 43 | A finding is confirmed when the response body contains a cross-framework error-disclosure signature: |
| 44 | |
| 45 | - **Node/Express + Sequelize:** `SequelizeDatabaseError`, `node_modules/sequelize`, |
| 46 | a JS stack with internal paths. |
| 47 | - **PHP:** `<b>Warning</b> ... /var/www/.../file.php on line N`. |
| 48 | - **Python:** `Traceback (most recent call last)`, `werkzeug.exceptions`. |
| 49 | - **Java:** `at com.app.Foo(Foo.java:42)` stack frames. |
| 50 | - **.NET:** `Server Error in '/' Application`, a `[System.XxxException: ...]` YSOD. |
| 51 | |
| 52 | A clean JSON error (`{"error":"Invalid input"}`) with no internals is NOT a |
| 53 | finding — that's correct handling. Disclosure of internal structure is. |
| 54 | |
| 55 | ## Validation discipline |
| 56 | |
| 57 | - Capture the exact leaked artifact (path, ORM class, version, stack frame) — |
| 58 | that's the evidence. "It returned 500" alone is not disclosure. |
| 59 | - Note what the leak enables next (e.g. a disclosed SQL error → hunt-sqli; a |
| 60 | disclosed absolute path → hunt-lfi). |