Hello everyone, last year I found an interesting account takeover on a bug bounty program named Namava, they were rather quick patching the vulnerability, but I was also able to break the patch with the help of a friend.
In this blog post, we are going to discuss the steps from the beginning until the end.
First Account Takeover
Namava is an online TV shows platform like Netflix, it has wide scope (*) and quite interesting functionalities to test for. As always, I started doing basic recon on the target and found the interesting subdomain
This web application was designed to ease the user’s login process. so instead of using TVs to log in, they can use their web application.
Users are given a QR code after clicking the “login by code” button. Here was the exact flow:
- The user use the “login by code” feature, to get a QR code and a token from tv.namava.ir.
- The user’s browser sends the request containing the previously generated
tokento the TV application backend each 10 seconds to check the token status.
- If the user who has already logged in to the main website scans the QR code and the login link will be opened on their browser.
- The application backend (namava.ir) verifies the token in a hidden channel with an API call to tv.namava.ir, if the token is correct, then the
token's statuswill be changed to
- Since the token has been verified, the user will be logged in with the subsequent request.
Here are the screenshots regarding the generated HTTP requests to make the above steps more clear. The first step, login by token feature:
Here is an example API call to validate the token:
The response, if the token is not verified (QR code is not scanned):
The issued authentication token after opening the QR code link:
The flow mentioned is vulnerable by design. You may ask why? In general, one-click login without the user’s confirmation is not safe. The confirmation terms refer to an action requiring a user’s interaction. So what’s the attacking scenario here?
- The attacker opens the website and clicks on the “login by code” button.
- The attacker scans the QR code and extracts the login link containing the
- The attacker makes a hidden iframe sourced to the login link.
- If the victim visits the URL while logged in to namava.ir, then the
tokenwill be verified and the attacker gets access to the victim’s account.
If the victim is lured to open the attacker’s website while logged in to the Namava application, the account takeover will be possible.
The exploit code is as follows (the link is generated in
<!DOCTYPE html> <html> <head> <title>Test</title> </head> <body> <h1>Hello!</h1> <iframe src="https://www.namava.ir/a/nn4zqq" style="width: 0; height: 0; border: 0; border: none;"> </iframe> </body> </html>
This was enough proof of concept for the Namava team to triage the vulnerability and pay the bounty. They also patched the vulnerability rather quickly but it wasn’t a proper patch for this issue.
Bypassing the Patch – Second Account Takeover
After approximately two weeks, when I came back to the program to verify the patch. they’ve implemented a confirmation button (Arrival on TV) after opening the login link.
After clicking on the “Arrival on TV” button, there was an HTTP request to namava.ir to confirm that the request wasn’t forged (CSRF). The attacking scenario is to check CSRF bypassing methods. The confirmation request:
There was no CSRF token on the request, although it wasn’t possible to accomplish CSRF as there were several other headers such as
X-Auth-Token that makes the browser send a preflight request as the request is not simple.
Their protections against the CSRF were to implement custom headers (
X-Application-Type) and the JSON content type leading to a preflight request.
When it comes to cross-site requests, there are several rules applied by the browsers, one of them is the preflight request (OPTIONS Request), and if the CORS configurations allow it, then the original request will be sent. If you are unfamiliar with this concept I’d suggest you read this link from Mozilla’s website, then this post which explains cross-site requests and browser security features.
I tried several tests, one of which was to remove the
X-Auth-Token headers, to check if the user’s authentication is kept by the cookies. I was amazed that this method worked, so I was one step closer to change the request to a simple request.
The other issue was with the JSON content type. I changed the
Content-type header to
application/x-www-form-urlencoded, and it worked! so the request was then a simple request with no preflights requests needed.
The request was vulnerable to CSRF. The exploit code:
<html> <body> <form action="https://www.namava.ir/api/v1.0/accounts/login/by-remote/verify" method="POST" target="namava"> <input type="hidden" name="fastLoginCode" value="n27494" /> </form> <iframe name="namava"></iframe> <script> document.forms.submit(); let iframe=document.getElementsByTagName("iframe"); iframe.style.display="none"; </script> </body> </html>
However, another protection was not discovered yet until then. After the exploit code was executed, the server responded:
The server was checking the
Origin header and it didn’t accept external origins. The HTTP request was sent with the help of exploit code:
It seemed the flaw was un-exploitable, after digging a bit more revealed that the server was accepting a
null origin as a valid value.
null value as origin header may lead to different security flaws, if you are unfamiliar with the concept please visit the Portswigger’s academy labs on this topic.
The final exploit code:
<html> <iframe src="data:text/html;base64,PHNjcmlwdD4KICAgICAgZnVuY3Rpb24gc3VibWl0UmVxdWVzdCgpCiAgICAgIHsKICAgICAgICB2YXIgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7CiAgICAgICAgeGhyLm9wZW4oIlBPU1QiLCAiaHR0cHM6Ly93d3cubmFtYXZhLmlyL2FwaS92MS4wL2FjY291bnRzL2xvZ2luL2J5LXJlbW90ZS92ZXJpZnkiLCB0cnVlKTsKICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwgImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCIpOwogICAgICAgIHhoci53aXRoQ3JlZGVudGlhbHMgPSB0cnVlOwogICAgICAgIHhoci5zZW5kKCJmYXN0TG9naW5Db2RlPTdyNTdteiIpOwogICAgICB9CiAgICAgIHN1Ym1pdFJlcXVlc3QoKQo8L3NjcmlwdD4="></iframe> <script> let iframe = document.getElementsByTagName("iframe"); iframe.style.display = "none"; </script> </html> <h1>Hello</h1>
When it comes to CSRF, there are several protections and bypassing methods, I will discuss each:
- The best protection is using CSRF tokens.
- If custom HTTP headers are used with the purpose of CSRF protection, then they should be compulsory.
- The common security practices should be implemented on the
Content-typeheader. The developers usually use libraries that supports different content types which makes this bypassing method possible.
- The origin header should be correctly whitelisted, the
nullvalue is commonly forgotten by developers.
- Eliminating custom headers, they could be removable with no change on the website.
- Changing the
Content-typeheader to common simple requests header.
- Using various bypassing methods for the
I’m hoping that you liked this write-up. leave me your feedback on the topics you would like to read more about.
Thanks for reading!