Quick answer
Some bugs require no fuzzing and no chains: just reading what the app ships to the client. A productivity and notes platform injected a third-party CMS token into its HTML with read permissions over all of its marketing content — including download URLs for paid templates. A single authenticated HTTP request, zero bypasses, $2,000 bounty.
The window.CONFIG object
The affected application is built in React (wrapped in Electron for desktop). As in most SPAs, when the page loads the server injected a global config object into the HTML:
window.CONFIG = {
googleCaptchaSiteKey: "...",
facebookPixelId: "...",
contentful: {
spaceId: "XXXXXXXXXXXXXX",
ACCESS_TOKEN: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // this shouldn't be here
},
...
}
This object was fully visible in the page source. No authentication, no special tooling. Ctrl+U and it's right there.
Among the third-party keys traveling in this object was one for Contentful — the CMS the platform used to manage marketing content, including the template marketplace.
What an exposed Contentful API key allows
Contentful exposes a REST API. With spaceId + ACCESS_TOKEN you can query any content in the space with no restriction beyond the token itself:
window.CONFIG in the public HTML
↓
Extract spaceId + ACCESS_TOKEN
↓
GET /spaces/{SPACE_ID}/environments/master/content_types?access_token=TOKEN
↓
Enumerate every content type in the space
↓
Identify the "template" content type with price and url fields
↓
GET entries filtered by fields.price >= 100
↓
Full list of paid templates with a direct download URL
The exact requests
Step 1 — Enumerate available content types:
GET https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/content_types
?access_token={ACCESS_TOKEN}
The response returns every content type defined. Among them: person, category, template.
Step 2 — Filter templates priced ≥ $100:
GET https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries
?access_token={ACCESS_TOKEN}
&content_type=template
&fields.price[gte]=100
For each template the response included: title, category, price and a direct download URL, regardless of whether the template was paid.
No extra step. No exploit. Just reading the API response with credentials the application itself handed out.
Root cause
The Contentful token should be a backend-only key: the server uses it to build the content it serves to the client, but it should never reach the client directly. By including it in window.CONFIG, any user could run the same queries as the server, with no restrictions.
Build / Deploy
↓ ACCESS_TOKEN injected into window.CONFIG
Browser / client
↓ Public HTML accessible without authentication
↓ Token visible in any DevTools
Contentful API
↓ No origin or scope restriction
↓ Read access to the entire space
Where to hunt this pattern
window.CONFIG, window.__INITIAL_STATE__, window.__NEXT_DATA__, window.APP_CONFIG — these are the most common names. Any React, Vue or Next.js app that hydrates state on the client is a candidate.
What to look for inside:
- Third-party API keys: Contentful, Algolia, Sanity, Cloudinary, Firebase, Mapbox, Stripe (publishable vs secret key, watch out)...
- Tokens with broad read permissions over resources that shouldn't be public.
- Internal parameters that reveal data structure, IDs, or undocumented endpoints.
The flow is always the same: Ctrl+U or DevTools → search for CONFIG, TOKEN, API_KEY, SECRET → evaluate what each key found lets you do against the corresponding service's API.
Impact
Free download of any paid template in the marketplace. In this specific report, templates priced from $100 up to several hundred. Access without a paying account, without bypasses, with a single HTTP request authenticated with the credentials the app itself handed out.
Reported in May 2024. Fixed in under 24 hours. $2,000 bounty.
Related labs
Practice recon of client-side tokens and abuse of poorly restricted third-party APIs: Information Disclosure labs.
Practice this in a lab
Information Disclosure
Keep learning · free account
Save your progress, unlock advanced payloads and rank your flags.
Related articles
Information Disclosure — the 12 highest-paying patterns
API keys in HTML, debug endpoints, verbose errors, JS bundles with secrets, exposed .git, header leaks. How to find it and why it's fixed fast and paid.
IP leak via a chat GIF — Client-Side Request Forgery + Information Disclosure
A social network didn't validate the URL of a GIF sent in chat. Sending a GIF revealed the victim's IP, operating system, phone model and device ID.
URL shortener as a mass PII leak — extraction of ~300 people/hour
Low-entropy codes + no rate limiting + a ticket with no auth = enumeration of phone numbers, cards (BIN+last 4) and real customers' purchases.