Hello, as some of you already know me, I’m Syed Mushfik Hasan Tahsin aka SMHTahsin33. And for those who doesn’t, I’m a 19 Y/O Cyber Security Enthusiast from Bangladesh with 3+ years of experience. And passionately doing Bug Bounties in my free time solely out of curiosity. I am an eWPTXv2 as well.
The target I was working on, was for collaboration purpose with a bug hunter from Bangladesh. Initially he didn’t provide me with the whole scope information, leaving me with just the root domain. I will mention the outcome of this at the end of this writeup. Let us suppose the program was REDACTED.com.
The target website is used for collaborating with teammates, digital contacts, doing projects, sharing them securely with one another. The whole site was made with Angular.JS and used JSON for data communication with the server. The server uses a variation of versions of their APIs all mixed up.
We could upload pdfs, images, videos etc as documents for presentations. These items are saved there in a directory, and can be added to projects the projects anytime. This is where the first suspect lies.
When uploading documents, tried to exploit the uploader but the files were being uploaded to a S3 Bucket, so nothing could be done here.
The POST request looked like this:
Now the question is can we control the URL to inject our input? The answer is actually Yes, but have to be followed by a https://
or else…
The server responds with a 400 Bad Request. And Doesn’t allow us to input anything of our desire.
The answer is straight, No. If we look at the initial request it sends a POST request to /v2/items/. While surfing the website we saw that some of the requests also uses v3 of their API too😉
As our wise assumption goes, shouldn’t it too? When we tried:
The server responded with a 422 Unprocessable Entity Error, with a bunch load of errors in the body
It was indicating some missing parameters, requiring us to add those in the request.
When we did, it again poured us with an error telling the format doesn’t match their regex. We can fix that after observing their regex from the error dump.
Now when we made the POST request to /v3/items with their requirements, it did a 200 OK and Now we can see that, we successfully injected a normal text in the URL input without any https://
If we add this document to our project and preview, we can see that, reflection is inside double quotes, which we need to escape.
Let’s Inject “><script>alert()</script>, we used \ to escape the double quote inside, or else it would’ve broke the JSON Syntax.
If we again add this in our project and look at the preview now, we can see:
Yeah, it successfully escaped from the double quotes enclosure and reflected as valid tags. But wait a second, where is the damn popup? :)
When we check the console it is showing that our script got blocked by CSP as it doesn’t allow unsafe-inline.
The defined Content Security Policy is as below:
default-src 'self';frame-src https:; script-src 'self'
https://player.vimeo.com/ https://www.youtube.com/ https://s.ytimg.com/;
child-src https://www.youtube.com;
connect-src 'self' https:; img-src 'self' data: https://i.ytimg.com/
https://app.redacted.com/ https://fakeimg.pl/;
style-src 'self' 'unsafe-inline'; font-src 'self';
The content security policy defines that only the scripts hosted on that specific domain and the mentioned domains are allowed only.
Among these the only domain that caught my eyes is youtube.com
YouTube has an interesting function called oEmbed, used for video embedding purposes. But the interesting part is that it also allows a “Callback” parameter. Every input there, is reflected on the page and the content-type is also identified as text/javascript
Now we if we visit https://www.youtube.com/oembed?callback=alert(1337); we can see that it is a valid JS now with a delimiter too.
Now we can craft our payload as:
\“><script src=“https://www.youtube.com/oembed?callback=alert(1337);”></script>
Now we can add this to our project and visit the preview page.
And, Now the popup is finally here!
Now let’s talk about the first section of this writeup. As my collab mate provided me with just the domain, I was like their subdomains linked with the root domain would also be in-scope. Even though my testing was done in the root domain and reflection point actually was in one of their subdomains which they used for this preview purpose. And wasn’t mentioned in the scope. So yeah, an Unlucky end. A great roller coaster ride of Adrenaline! 😂
Follow Me 🔗:
https://twitter.com/SMHTahsin33
https://www.youtube.com/@SMHTahsin33
https://www.facebook.com/smhtahsin33/
Thanks for Reading, hope you all enjoyed. And don’t forget to share. Bye until next time 👋