October 31, 2026
API Key & Secret Leak Hunting: GitHub Dorking + TruffleHog at Scale
The single highest ROI skill in bug bounty โ find real, live credentials sitting in public code, at scale, with one automated pipeline

By Monika
6 min read
We've covered recon automation, IDOR, Business Logic bugs, Subdomain Takeovers, Rate Limiting, File Uploads, real-world XSS patterns, SSRF, GraphQL, and Password Reset Poisoning. Today's topic is arguably the best time-to-payout ratio in the entire series: hunting for leaked API keys and secrets across public code at scale.
This isn't a theoretical bug class. Developers push .env files by accident, hardcode AWS keys "just for testing," commit a Slack webhook and forget to rotate it, or leave a Stripe secret key in a Postman collection they shared publicly. Every single one of these is a live credential sitting in plain sight โ and most programs pay very well for proof of a genuinely exploitable leaked secret.
Why this bug class is so high-value
Unlike XSS or IDOR, where you're hunting for a flaw in logic, this is hunting for a mistake that already happened โ the vulnerability already exists, fully formed, somewhere public. You're not exploiting anything complex; you're finding something that was never supposed to be visible in the first place. That makes the skill ceiling low to start, but the payout ceiling extremely high, because a leaked production AWS key or database credential is often rated Critical on its own.
Part 1: GitHub Dorking โ finding leaks manually first
Before automating anything, you need to understand what you're actually searching for. GitHub's code search supports targeted queries (called "dorks") that narrow results to exactly the kind of file or string pattern that tends to contain secrets.
Core dork patterns to start with
org:target-company "AKIA"
org:target-company "api_key"
org:target-company "secret_key"
org:target-company filename:.env
org:target-company filename:.env.local
org:target-company filename:config.json "password"
org:target-company extension:yml "secret"
org:target-company extension:json "private_key"
org:target-company "BEGIN RSA PRIVATE KEY"
org:target-company "BEGIN PRIVATE KEY"org:target-company "AKIA"
org:target-company "api_key"
org:target-company "secret_key"
org:target-company filename:.env
org:target-company filename:.env.local
org:target-company filename:config.json "password"
org:target-company extension:yml "secret"
org:target-company extension:json "private_key"
org:target-company "BEGIN RSA PRIVATE KEY"
org:target-company "BEGIN PRIVATE KEY"If the company doesn't have a clean GitHub org (many don't), search by domain/product name instead:
"target-company.com" "api_key"
"target-company" "AWS_SECRET_ACCESS_KEY""target-company.com" "api_key"
"target-company" "AWS_SECRET_ACCESS_KEY"Dorks specific to common services (these patterns are very recognizable)
"AKIA" AND "aws_secret_access_key" โ AWS access keys
"xoxb-" OR "xoxp-" โ Slack bot/user tokens
"sk_live_" โ Stripe live secret keys
"AIza" โ Google API keys
"ghp_" OR "github_pat_" โ GitHub personal access tokens
"SG." โ SendGrid API keys
"eyJhbGciOiJ" โ JWT tokens (base64 header signature)
"mongodb+srv://" โ MongoDB connection strings with credentials
"postgres://" OR "mysql://" โ Database connection strings"AKIA" AND "aws_secret_access_key" โ AWS access keys
"xoxb-" OR "xoxp-" โ Slack bot/user tokens
"sk_live_" โ Stripe live secret keys
"AIza" โ Google API keys
"ghp_" OR "github_pat_" โ GitHub personal access tokens
"SG." โ SendGrid API keys
"eyJhbGciOiJ" โ JWT tokens (base64 header signature)
"mongodb+srv://" โ MongoDB connection strings with credentials
"postgres://" OR "mysql://" โ Database connection stringsEach of these prefixes is intentionally distinctive by design (so services can detect and auto-revoke their own leaked keys quickly) โ which also makes them perfect search patterns for you.
Don't forget these often-overlooked sources
site:pastebin.com "target-company" "api_key"
site:gitlab.com "target-company" "secret"
"target-company" site:trello.com
"target-company.com" filename:*.postman_collection.jsonsite:pastebin.com "target-company" "api_key"
site:gitlab.com "target-company" "secret"
"target-company" site:trello.com
"target-company.com" filename:*.postman_collection.jsonPublic Postman collections are a massively underused source โ developers export a collection with saved environment variables (including live API keys) and share it in docs or forum posts without realizing the secrets are embedded in the export.
Part 2: Automating at scale with GitHub's search API
Manual dorking works, but to actually cover a large org (hundreds of repos, forks, old branches) you need automation. 'github-search' and 'gitrob' / gitleaks style tools or a simple script using GitHub's REST/Code Search API get you there:
# Using GitHub CLI + jq to search code and pull matches
gh api -X GET search/code \
-f q="org:target-company AKIA" \
--paginate | jq -r '.items[].html_url'# Using GitHub CLI + jq to search code and pull matches
gh api -X GET search/code \
-f q="org:target-company AKIA" \
--paginate | jq -r '.items[].html_url'Save all matching file URLs, then feed them into the next stage.
Don't just search the default branch โ check forks and history
A huge number of leaked secrets exist in a commit that was later "fixed" by removing the secret โ but the secret still exists in the git history, fully retrievable:
git clone https://github.com/target-company/some-repo.git
cd some-repo
git log --all --full-history -- "*.env"
git log -p --all | grep -i "api_key\|secret\|password"git clone https://github.com/target-company/some-repo.git
cd some-repo
git log --all --full-history -- "*.env"
git log -p --all | grep -i "api_key\|secret\|password"Also check forks โ a company's own repo might be clean, but a former employee's or contributor's fork of it, made before a secret was scrubbed, often still has the original history intact:
# List all forks of a target repo
gh api repos/target-company/some-repo/forks | jq -r '.[].full_name'# List all forks of a target repo
gh api repos/target-company/some-repo/forks | jq -r '.[].full_name'Part 3: Scanning at scale with TruffleHog
Manually grepping through hundreds of repos doesn't scale. TruffleHog is the industry-standard tool here โ it combines regex-based detection (known secret formats) with entropy analysis (catching random-looking high-entropy strings that don't match a known pattern but still look like a secret), and critically, it can verify many secret types live against the actual service.
Scan a single repo, including full git history
trufflehog git https://github.com/target-company/some-repo.git --only-verifiedtrufflehog git https://github.com/target-company/some-repo.git --only-verifiedThe โ only-verified flag is the single most important option for bug bounty use โ TruffleHog will actually attempt to authenticate with the service each detector supports (AWS, Slack, Stripe, GitHub, etc.) and only report secrets it confirmed are still live and valid. This cuts out the noise of old, rotated, or fake/example keys entirely.
Scan an entire GitHub organization in one pass
trufflehog github --org=target-company --only-verifiedtrufflehog github --org=target-company --only-verifiedScan beyond GitHub โ GitLab, Docker images, S3 buckets, and filesystems
TruffleHog supports multiple source types, which matters because secrets leak in more places than just git repos:
trufflehog gitlab --endpoint=https://gitlab.com --org=target-company --only-verified
trufflehog docker image target-company/some-image:latest --only-verified
trufflehog s3 --bucket=target-company-public-assets --only-verified
trufflehog filesystem /path/to/downloaded/postman-collection.jsontrufflehog gitlab --endpoint=https://gitlab.com --org=target-company --only-verified
trufflehog docker image target-company/some-image:latest --only-verified
trufflehog s3 --bucket=target-company-public-assets --only-verified
trufflehog filesystem /path/to/downloaded/postman-collection.jsonDocker Hub in particular is a goldmine โ developers frequently bake .env files or hardcoded credentials directly into a public image layer without realizing every layer is downloadable and inspectable.
Part 4: Building the full automated pipeline
Here's a script that ties GitHub org discovery, repo cloning, and TruffleHog scanning together โ run this against a target company's GitHub presence end to end:
#!/bin/bash
# Usage: bash secret_hunt.sh target-company
org=$1
mkdir -p secret_hunt_$org && cd secret_hunt_$org
echo "[*] Enumerating all public repos for $org..."
gh repo list $org --limit 1000 --json name,url -q '.[] | .url' > repos.txt
echo "[*] Scanning each repo (including history) with TruffleHog..."
while read repo_url; do
echo " -> Scanning $repo_url"
trufflehog git "$repo_url" --only-verified --json >> verified_secrets.json
done < repos.txt
echo "[*] Enumerating forks for deeper history coverage..."
while read repo_url; do
repo_name=$(basename "$repo_url")
gh api "repos/$org/$repo_name/forks" --jq '.[].html_url' >> fork_urls.txt 2>/dev/null
done < repos.txt
sort -u fork_urls.txt -o fork_urls.txt
while read fork_url; do
echo " -> Scanning fork $fork_url"
trufflehog git "$fork_url" --only-verified --json >> verified_secrets.json
done < fork_urls.txt
echo "[*] Scanning org's public Docker images (if any known)..."
# Manually add known image names here, or pull from Docker Hub's API for the org
echo "[+] Done. Review verified_secrets.json for confirmed, live findings."#!/bin/bash
# Usage: bash secret_hunt.sh target-company
org=$1
mkdir -p secret_hunt_$org && cd secret_hunt_$org
echo "[*] Enumerating all public repos for $org..."
gh repo list $org --limit 1000 --json name,url -q '.[] | .url' > repos.txt
echo "[*] Scanning each repo (including history) with TruffleHog..."
while read repo_url; do
echo " -> Scanning $repo_url"
trufflehog git "$repo_url" --only-verified --json >> verified_secrets.json
done < repos.txt
echo "[*] Enumerating forks for deeper history coverage..."
while read repo_url; do
repo_name=$(basename "$repo_url")
gh api "repos/$org/$repo_name/forks" --jq '.[].html_url' >> fork_urls.txt 2>/dev/null
done < repos.txt
sort -u fork_urls.txt -o fork_urls.txt
while read fork_url; do
echo " -> Scanning fork $fork_url"
trufflehog git "$fork_url" --only-verified --json >> verified_secrets.json
done < fork_urls.txt
echo "[*] Scanning org's public Docker images (if any known)..."
# Manually add known image names here, or pull from Docker Hub's API for the org
echo "[+] Done. Review verified_secrets.json for confirmed, live findings."Run it:
bash secret_hunt.sh target-companybash secret_hunt.sh target-companyPart 5: What to do when you find a verified secret
Finding a live key is only half the job โ you need to demonstrate real impact without causing damage, and report it responsibly and fast.
- Confirm scope and ownership first. Make sure the repo/organization is actually in the program's defined scope โ a leaked key in an unrelated third-party's repo that merely mentions the company is not the same as a leak in their own infrastructure.
- Verify minimally, don't exploit. If it's an AWS key, running
aws sts get-caller-identitywith the credentials is usually enough to prove validity and show what account/permissions it belongs to โ you don't need to go further (don't list S3 buckets, don't read data, don't modify anything).
AWS_ACCESS_KEY_ID=<leaked> AWS_SECRET_ACCESS_KEY=<leaked> aws sts get-caller-identityAWS_ACCESS_KEY_ID=<leaked> AWS_SECRET_ACCESS_KEY=<leaked> aws sts get-caller-identity-
For database connection strings, a connection test that confirms authentication succeeds is enough โ don't query or dump actual data.
-
Report immediately. Live credentials are actively dangerous the longer they sit unreported โ most programs explicitly want speed here over a more "polished" write-up.
-
Redact properly in your report. Show enough of the key to prove it's real (first/last few characters) without publishing the full secret in your report text itself.
Reporting tips
- State exactly where the secret was found (repo URL, specific file, specific commit hash โ not just "somewhere in GitHub")
- Include the TruffleHog detector type and confirm it was flagged as verified, not just pattern-matched
- Show the minimal verification step (e.g.,
get-caller-identityoutput) as proof of validity - Clearly state what the credential has access to, if you can tell from the verification step (account ID, IAM user/role name, associated permissions where visible)
- Recommend immediate rotation in your report โ this is standard practice and triagers expect it
Quick reference: secret-hunting checklist
[ ] Run core GitHub dorks (AKIA, api_key, filename:.env, etc.)
[ ] Run service-specific dorks (sk_live_, xoxb-, AIza, ghp_, mongodb+srv://)
[ ] Check Postman public collections, Pastebin, Trello, GitLab mirrors
[ ] Enumerate all repos in the target's GitHub org
[ ] Scan full git history, not just the current branch
[ ] Check forks for secrets scrubbed from the original repo
[ ] Run TruffleHog with --only-verified across org, GitLab, Docker images, S3
[ ] Minimally verify any hit (get-caller-identity style check only)
[ ] Report immediately with repo/commit location and verification proof[ ] Run core GitHub dorks (AKIA, api_key, filename:.env, etc.)
[ ] Run service-specific dorks (sk_live_, xoxb-, AIza, ghp_, mongodb+srv://)
[ ] Check Postman public collections, Pastebin, Trello, GitLab mirrors
[ ] Enumerate all repos in the target's GitHub org
[ ] Scan full git history, not just the current branch
[ ] Check forks for secrets scrubbed from the original repo
[ ] Run TruffleHog with --only-verified across org, GitLab, Docker images, S3
[ ] Minimally verify any hit (get-caller-identity style check only)
[ ] Report immediately with repo/commit location and verification proofFinal Thoughts
This is one of the rare bug classes where automation genuinely does most of the work for you โ the skill isn't in crafting a clever payload, it's in casting a wide enough net (every repo, every fork, every commit in history, every Docker image) and filtering the noise down to verified, live findings. Run this pipeline against your favorite targets on a recurring basis; new commits happen daily, and a fresh secret can slip into a repo and get found by you before a researcher with a less thorough pipeline ever sees it.
If this guide helped you, consider buying me a coffee โ โ it keeps me motivated to dig up and write more bugs like this. Buy me a coffee
Monika Hi, I'm Monika โจI break down real-world bug bounty reports into simple, practical write-ups.If my articles help youโฆ