May 24, 2026
A Secret Key in Plain Sight: How I Earned My First $200 Finding a Hidden API Leak
A JavaScript file. One exposed key. And months of patience that finally paid off β hereβs the full story of my first paid bug bounty.
By Theankitsaini16
7 min read
Introduction
This is the story of my first paid bug bounty β a $200 reward from VFS Global, one of the world's largest visa and consular outsourcing companies. The vulnerability was an API key exposed inside a publicly accessible JavaScript file on their web application onevasco.com. While it may sound simple on the surface, the real impact came from proving how that single exposed key could disrupt critical business services β and that story took six months to tell.
The bug was found through a structured recon methodology, not by luck. This write-up walks through every step, from initial scope mapping to final disclosure, so you can replicate this approach on your own programs.
About the Program
VFS Global runs a public bug bounty program listed on YesWeHack β one of the leading European bug bounty platforms. YesWeHack connects security researchers with organizations that want their systems tested by the community, with rewards based on the severity and impact of confirmed findings.
The scope included onevasco.com, the company's global visa application portal. With millions of users processing sensitive travel documents through this platform, any vulnerability affecting its infrastructure carries real-world weight.
"When choosing targets, I always prefer programs with broad scope. More subdomains = more attack surface = more chances to find something nobody else checked."
Recon Methodology: How I Found It
Finding this bug wasn't about getting lucky. It was the result of following a repeatable methodology I've built over time. Here's exactly what I did, step by step.
Step 1 Saving the In-Scope Domains
The first thing I do on any engagement is record all domains explicitly listed in scope. I save them to a local file so every subsequent tool has a clean, consistent input. This prevents scope creep and keeps my recon targeted.
Saved all in-scope domains to a file
touch domains.txt
Step 2 Subdomain Enumeration
With the root domains saved, I ran subdomain enumeration using four different tools in parallel. Using multiple tools is important because each one queries different data sources β passive DNS, certificate transparency logs, brute-force wordlists β and combining their results gives a much more complete picture.
-
subfinder β passive subdomain discovery using APIs from SecurityTrails, VirusTotal, Shodan, and more. subfinder -dL domains.txt -all -recursive -o subs/subfinder_domains.txt
-
amass β active and passive enumeration with DNS resolution and graph mapping. amass enum -config ~/.config/amass/config.ini -passive -df domains.txt -o subs/amass_passive.txt
-
assetfinder β fast passive discovery using certificate transparency and web scraping. assetfinder -subs-only domains.txt > subs/assetfinder_domains.txt
-
crt.sh β certificate transparency log search. Certificates expose subdomains that never show up in DNS brute-force. curl -s "https://crt.sh/?q=%25.gecina.fr&output=json" \ | sed 's/\u0000//g' \ | grep -v "<html" \ | jq -r '.[].name_value' \ | tr '\n' ',' \ | tr ',' '\n' \ | sed 's/*.//g' \ | sort -u > subs/crtsh.txt
_5._Filter all the unique subdomains into a one file. cat subs/*.txt | sort -u > all_subs.txt
Step 3 Checking Live Subdomains with httpx
Raw subdomain lists contain a lot of noise β expired domains, parked pages, dead hosts. Before wasting time on a URL, I use httpx to probe each subdomain and filter down to only the ones actually returning HTTP responses. This saves hours.
cat all_subs.txt | httpx-toolkit -silent -ports 80,443,8080,8443 -o alive/alive_subs.txt
Step 4 URL Collection from Live Subdomains
With a clean list of live domains, the next step is collecting every URL those domains have ever served β JavaScript files, API endpoints, parameters, paths. I use three tools here because they pull from different sources: active crawling, Wayback Machine archives, and Google's indexed URLs.
-
katana β fast, active web crawler that follows links and extracts URLs from JavaScript. cat alive/alive_subs.txt | katana -d 3 -silent -o urls/katana_urls.txt
-
waybackurls β pulls historical URLs from the Wayback Machine archive. curl -s "http://web.archive.org/cdx/search/cdx?url=.target.com/&output=text&fl=original&collapse=urlkey" | sed 's_https*://__' | cut -d'/' -f1 | sort -u > subs/wayback.txt
-
gau (Get All URLs) β aggregates URLs from Wayback Machine, Common Crawl, and OTX. cat alive/alive_subs.txt | gau β subs β threads 50 | sort -u > urls/gau_urls.txt
combine all the unique urls into a one file. cat urls/*.txt | sort -u > all_urls.txt
Step 5 Filtering JavaScript Files
From tens of thousands of collected URLs, I use grep to filter down only the JavaScript files. JS files are goldmines β they contain API keys, internal endpoints, tokens, and business logic that developers forget is publicly accessible.
cat all_urls.txt | grep -E ".js$" >> js.txt
Step 6 β Scanning JS Files with Nuclei
Now comes the automated part. I ran Nuclei with its JavaScript secret detection templates against all the collected JS file URLs. Nuclei checks each file for common patterns β API keys, tokens, credentials, private keys β using community-maintained signatures.
cat js.txt | nuclei -t ~/.local/nuclei-templates/http/exposures/
This is where the API key surfaced. Nuclei flagged a match inside the file:
https://www.onevasco.com/dir{1}/dir{2}/main.file_name.js
The key was embedded in plaintext inside the minified JavaScript bundle β a token for the IPData geolocation API.
Verification: Is This Key Actually Valid?
Finding a string that looks like an API key means nothing until you verify it works. A lot of researchers skip this step and report dead keys β that's a fast track to getting your report closed as N/A.
Basic curl Check
My first move was a simple curl request to the IPData API endpoint using the exposed key:
curl https://api.ipdata.co/152.59.150.110?api-key=
The API responded immediately with full geolocation data:
{
"ip": "152.59.150.110",
"region": "Assam",
"country_name": "India",
"latitude": 26.188499,
"longitude": 91.739196,
"asn": { "name": "Reliance JIO Infocomm Limited" }
}
The key was live and working. But a valid API key that only gets a few requests is low-impact. I needed to understand the actual blast radius.
Bash Script β Testing API Rate Limits
Many APIs β especially on free tiers β enforce a daily request limit. If this key was on a free plan with a 1,500 request/day cap, the risk is moderate. But if it was unlimited or on a paid plan, the risk is significantly higher. I wrote a basic bash script to find out:
#!/bin/bash
# Send 1000 requests and check for rate limit errors
for i in $(seq 1 1000); do
_STATUS=$(curl -s -o /dev/null -w "%{http_code}" _
"https://api.ipdata.co/8.8.8.8?api-key=")
echo "Request $i: HTTP $STATUS"
if [ "$STATUS" != "200" ]; then
echo "Rate limit hit at request $i"
break
fi
done
Every single one of those 1,000 requests came back with HTTP 200. No rate limit. No throttling. No error response. The key kept working without interruption.
"When all 1,000 requests returned 200, I knew this wasn't a free tier key. This was something the company was paying for β and I could exhaust it."
Checking IPData's Pricing Plans
I went directly to the IPData platform and looked at their pricing. Their free plan caps out at 1,500 requests per day. Paid plans scale into millions of daily requests depending on the tier β and they cost real money.
Given the key handled 1,000 back-to-back requests without any pushback, this was clearly a paid subscription. That changes the risk profile entirely.
Business Impact: Why They Paid $200 for "Just an API Key"
This is the section most researchers get wrong. Finding a key is step one. Explaining why it matters to the business is what converts a report into a bounty.
Here is the real impact I documented:
Service Disruption via Quota Exhaustion
VFS Global uses the IPData API as part of their application infrastructure β likely to power geolocation features for users accessing the platform from different countries. If a malicious actor sends enough requests to exhaust the daily quota, those geolocation features stop working entirely for legitimate users.
For a company processing hundreds of thousands of visa applications globally, a degraded or unavailable service translates directly to lost productivity, user frustration, and potential SLA violations. This is a real business outage β caused by a single exposed API key.
Financial Loss from API Abuse
On a paid plan, every API call costs money. An attacker running continuous requests against this key isn't just disrupting the service β they're running up VFS Global's bill. Depending on the plan tier, thousands of requests per day could translate to hundreds of dollars in unauthorized charges that VFS Global has no easy way to dispute without rotating the key and investigating the abuse.
Infrastructure Intelligence Gathering
The geolocation data returned by IPData includes latitude, longitude, ASN, ISP, and carrier information for any queried IP. Applied to VFS Global's own server IPs, this is a reconnaissance tool β it gives an attacker a detailed map of where infrastructure physically lives, which network it runs on, and who operates it. That kind of intelligence is the starting point for targeted physical, network, or social engineering attacks.
"The lesson I took away from this report: always show the actual impact on the business. Not just 'I found a key' β but 'here is what breaks, here is what it costs, here is who it affects.' That's the difference between a duplicate/informational and a paid bounty."
Disclosure Timeline
Bug bounty is a test of patience as much as skill. Here is how the six-month disclosure played out:
β’ November 5, 2024 β Submitted report on YesWeHack. Received acknowledgment within hours.
β’ November 27, 2024 β Team asked to demonstrate security impact. Went back to research additional impact vectors.
β’ December 13, 2024 β Provided geolocation PoC: extracted lat/long coordinates of infrastructure IPs using the exposed key.
β’ December 14, 2024 β Added note about paid API plan and financial abuse potential.
β’ December 15, 2024 β Submitted the bash script PoC demonstrating quota exhaustion via 1,000+ continuous requests.
β’ February 5, 2025 β Followed up professionally after extended silence.
β’ April 22, 2025 β Team confirmed they had shared the report internally and were reviewing.
β’ May 14, 2025 β Report accepted. CVSS updated to 5.3 Medium. Fix deployed. Asked to verify.
β’ May 25, 2025 β Noticed the API was still responding despite reported fix. Flagged to the team.
β’ October 22, 2025 β Team confirmed the key was fully removed from the JavaScript bundle.
β’ November 3, 2025 β Verified: endpoint returns no key, access denied. Fix confirmed.
β’ November 4, 2025 β Report closed as Resolved. $200 bounty awarded. β
Key Lessons for Bug Hunters
Follow a methodology, not a hunch
This bug wasn't found by randomly clicking around. It came from a repeatable pipeline: scope β subdomains β live hosts β URL collection β JS filtering β secret scanning. Build your own methodology and run it consistently on every target.
Use multiple tools for the same task
Subfinder alone would have missed subdomains that amass found. Katana alone would have missed URLs that Wayback Machine had archived. Redundancy in recon tools = better coverage.
Always verify before reporting
A key that looks like an API key is not a finding. A working, exploitable key with documented impact is a finding. The curl test and the bash rate-limit script turned a potential N/A into a $200 bounty.
Show business impact, not just technical facts
The triage team asked me to prove impact β twice. The researchers who can't answer that question lose their bounty. Always ask: what breaks for the business if an attacker exploits this? Service downtime? Financial loss? Data exposure? Make the answer impossible to ignore.
Patience and professionalism win
This report sat open for six months. I kept following up, providing new PoCs when asked, and staying respectful throughout. That matters. Security teams are busy, programs get backlogged, and researchers who stay constructive are the ones who get paid.
Responsible Disclosure: This vulnerability has been fully remediated by VFS Global. All testing was conducted within the scope and rules of the YesWeHack bug bounty program. This write-up is shared for educational purposes only.