As Luck Would Have It — tales of good fortune in bug bounty

2 months ago 35
BOOK THIS SPACE FOR AD
ARTICLE AD

LS

We’ve all been there: 8 hour sesh trying to bypass a WAF so your fancy XSS payload can go through or perhaps another all nighter trying to fuzz hosts with a 5 million line wordlist and getting 0 responses

It’s no secret bug bounty can feel like a thankless grind but on the other hand, some times the stars align and you run into something by pure luck and you start questioning how the hell it was even possible. I’ll be exploring 3 recent reports in the latter scenario which all had me scratching my head, ranked from lowest to highest impact.

Starting off pretty simple, this will probably be the easiest bug I’ll ever find. I was browsing the target which is an eCommerce site we’ll call target.com, I put some items in my cart then proceeded to check out.

On target.com/cart, I opened the page source to just skim through and noticed an div with some random metadata and someone else's full name and email address. No idea how it got there or who it was, I explored
every JS file to try figure out what was happening and I simply didn’t
find anything that referenced this data. I logged out for the day and came back later and saw the same div but this time it had someone elses information.

Once again, no clue why this was happening but I knew it shouldn’t be, I tried timing this change of data and realized it occurred every 4 hours. The target i’m hunting on has multiple locales and every region yielded someone else's information.

So, I figured as an attacker you could poll the sites every 4 hours which would equate to 6 peoples information per site per day. I made the case that this can be extrapolated to all the sites which would easily be 1000s of accounts in a week. The department of labor and GDPR consider the combination of a full name and email address as PII, so this was triaged as a medium.

this was the first result when I googled “free money” because that’s exactly what this was

Okay i’ll admit this one wasn’t as trivial as the others but the way I got there was very much “i can’t believe this is real”.

I started digging into the functionality a bit more and once I added paypal as a payment option I saw the below request:

Any time you see a request that is clearly constructing another request for another service your alarm bells should be ringing louder than the Vatican.

With my knowledge of the target and some research I learned that what was happening here was a multi step process where the proxy sends this data to internal-ecommerce.target.com/v1/paypal/token which initializes a paypal express checkout flow. This is a legacy “NVP” operation according to paypal’s API docs however I tested it extensively and I wasn’t able to abuse anything on this flow.

As time went on I changed the request that is being proxied from “type”:”POST”to “type”:”GET” and I got the expected response from internal-ecommerce.target.com/v1/paypal/token, which this time was my own previous express checkout tokens and also my billing address.

I was just about to give up on this but at some point I was switching cookies to try make sure it returns the specific user’s information with their auth cookie and I sent one request without the cookie and I got a massive response with a bunch of other peoples information.

Example:

{
"resource": {
"partner": "redacted",
"dataCenter": "redacted",
"environment": "redacted",
"accountHash": redacted,
"shardKey": redacted,
"date": "2013-09-06T15:26:34.548-0700",
"processorRef": "http://api.target.com/payment/processors",
"currencyCode": "USD",
"accountId": "target.com-redacted",
"orderId": 1234,
"resultCode": "Ok",
"transactionId": "1234",
"processorRespCode": "2100",
"processorServer": "https://redacted.com",
"processorClient": "prd2-app4",
"transactionType": "CaptureCreditCardAuth",
"creditCard": {
"oid": "target.com-1234",
"expmonth": "4",
"expyear": "2014",
"fullname": "Redacted",
"cardtype": "MC",
"accountoid": "target.com-1234",
"typeName": "paymentType",
"firstname": "Redacted",
"lastname": "Redacted",
"street1": "Redacted",
"street2": "",
"city": "Redacted",
"state": "Redacted",
"country": "Redacted",
"postalcode": "Redacted",
"_id": "target.com-1234",
"accountId": "target.com-1234"
},
"otcBreadcrumb": "redacted",
"_id": 1234,
"request": "redacted",
"response": "redacted",
"deleted": false,
"testAccount": false,
"functionalTest": false,
"beforeInitiateBidContexts": false,
"items": [],
"storeAddress": false,
"addressExists": true
}
}
ok then

Turns out for some reason the internal proxy generates a service token if the main auth cookie isn’t available but this token has access to all accounts so it just fetches all users. The results are paginated and unfortunately I wasn’t able to pass parameters to control the pagination but every so often the proxy would would return a random set of user payment data.

In the end this was also some what limited in the amount of exposure, since I had access to about ~100 accounts it was enough to get this report triaged as a high.

Thank the lord this token used by the proxy wasn’t exposed on the HTTP response, surely there’s no way they would allow this to happen right?

Spoiler alert: it did happen.

This time around I was taking a look at a bunch of archived URLs I pulled using waybackurls. I came across a generic looking URL containing the tracking info for a purchase. The url looked something like this: target.com/tracking?order=123&trackingId=456.

I opened one of these URLs and got redirected to the login page, rightfully so. I went down the list of urls again and opened another one and for some reason this one loaded!

Granted the page didn’t have any PII however it definitely shouldn’t be accessible without being authenticated as the right user. I had no clue what just happened. I eventually check the source and low and behold there was a token in the cart config that was in the DOM for some reason, again no idea where it came from.

he thinks we can’t see him

After some testing I realized this behavior happened when you open this tracking page which initially works as expected (redirect to login) but after a refresh, somewhere down the line the cart service generates a token that is sent to the front end. I happen to be testing while not being authenticated to the website however when I was authenticated to my own account this never happened and it would always error out.

I took this token and used it on the internal-ecommerce.target.com host and it had full access to the orders and billing info for every single user on the website. Just to recap this the chain of events:

open tracking page for an arbitrary userrefresh oncereceive a tokenuse token on API to view anyone’s information.
in a nutshell

Yeah I didn’t believe it either but it happened!

Test everything! You just never know what you’re going to come acrossStay persistent, sometimes you just have to be at the right place at the right timeUnderstand how different pieces of the site work together to strengthen your intuition
Read Entire Article