Spent a full day hunting across six targets. Found what looked like a CVSS 9.8 critical chain on a corporate travel platform. Wrote a full report. Got a 9/10 from my AI second-opinion validator. Then asked one question that killed the entire finding. Final score: zero submissions, zero dollars, three lessons worth more than a payout.
The Critical That Wasn't
A corporate travel portal loaded a third-party app serving a public JavaScript config file containing AWS Cognito User Pool ID and Client ID, along with 40+ other environment variables including payment vault IDs, analytics keys, and enterprise org mappings for Fortune 500 companies.
I registered a new account on the production Cognito pool, confirmed it via email, authenticated via CUSTOM_AUTH with a one-time code, and obtained full Bearer tokens with 24-hour expiry. The app's API rejected the token at the application layer because the user wasn't provisioned internally. But I had a valid Cognito identity in their production pool.
Wrote the report. Ran it through Ollama for second opinion. Score: 9/10. Verdict: Fix and Acknowledge.
Then I asked: is this registration actually an open one?
Checked the Cognito hosted UI. The signup page was right there. Branded. Intentional. Self-registration is the product's onboarding flow. The entire vulnerability was standard SPA architecture working exactly as designed.
The Lesson: OAuth Client IDs Are Not Secrets
Cognito Client IDs, Auth0 Client IDs, Firebase config objects, Okta Client IDs, Clerk publishable keys, Supabase anon keys: all designed to be public. They live in frontend JavaScript because that is where they are supposed to be. Before reporting any exposed auth credentials in JS, check if the provider's hosted UI offers the same functionality. If it does, it is by design. Takes thirty seconds. Saves hours of wasted report writing.
The SSRF I Couldn't Prove
On a major travel platform, found an internal image rendering service with its full OpenAPI spec exposed. The spec revealed an endpoint that takes an imageUrl parameter and fetches it server-side. Classic SSRF architecture.
Brute-forced valid template IDs via the batch processing endpoint. Found three valid ones. Set up a webhook callback. Tried every combination. Never got a callback. The render endpoint validates parameters against the template schema before fetching the URL. Without matching the exact template keys, the request dies at validation.
The lesson: the architecture supports SSRF and I can trigger SSRF are completely different statements. The second one pays. The first gets marked N/A. No callback proof, no report.
The Browser Wall
Every target I tried required browser-based interaction for authentication. WAF blocking curl on hotels. Cloudflare blocking POST on e-commerce. Captcha on social media registration. SPA catch-alls on AI platforms returning the same HTML shell for every path. CLI-only recon finds infrastructure issues. Infrastructure issues rarely pay. The real bugs live behind login walls.
Three Rules Added to My Playbook
- Check by-design before by-accident. If the auth provider's hosted UI offers the same functionality, stop. It is not a vulnerability.
- No callback, no SSRF report. Architectural SSRF potential is not exploitable SSRF. Set up a callback listener first. If nothing comes back, kill it.
- Browser first, CLI second. Start every session by connecting browser automation. CLI recon is preparation, not hunting.
Final Thought
Zero-dollar days are not wasted days if you learn something you will never repeat. The Cognito lesson alone saved me from adding another N/A to an already painful record. Ask the uncomfortable question before you submit. The answer might hurt, but not as much as another Informative from a triager.
No comments yet