Before you deep dive in the technical information, I wish to confirm that this vulnerability has been FIXED. Thanks to PAYTM for taking a quick action. Looking forward for such quick response on security concerns. Kudos!
Don’t get this wrong. I wish to share a vulnerability that can be leveraged by attackers to perform/ initiate a spear phishing attack. The website in discussion is paytm.com. There is an information disclosure vulnerability in the main website, and an un-authenticated user can query for a mail address against a mobile number. It means, if you have a mobile number of a person who is a member of paytm, you can find his registered email address on the website. Join these 2 elements, and you can send a targeted email to the victim. Let us dive straight into it. paytm uses the following link as the login page, URL: hxxps://hub.paytm.com/user/authenticate
Now, as you can see, there are options to login via Facebook or via Mobile Number/ Email address. If you are aware of the mobile number, it is possible to find the email address associated with that number. Bam! Now, let us extract couple of mail addresses from the website. The vulnerability is with handling JSON responses. To initiate this, we will click on the "Forget Password".
Here you can see it asks for ‘Enter Email OR Mobile". Let us try to enter an email address ‘firstname.lastname@example.org’. Voila! we get the following message and see the email address is hidden with **** . Is this hidden actually? It means the email has been sent to the concerned user. Great. Enough of this GUI mumbo-jumbo, why not go into some packet nitty-gritty?
Request Packet (HTTP) – Check Email
Details: In this packet a GET request to /api/users/<email address>/checkemail is issues to the host hub.paytm.com. This request also carries two (2) custom headers: X_PAYTMAPP_USERNAME (value: hub.paytm.com) and X_PAYTMAPP_KEY (extracted from the previous response page).
Now, the response packet to the checkemail request is shown below,
Response Packet (HTTP) – Check Email
Details: Here you can see that we have received some interesting information in the response,
Allowed Methods – GET, POST, PUT, DELETE, OPTIONS (isn’t that too many to raise concern?) The response content type is application/json from the nginx web server.
Let us now check the DATA field of the response,
Status: Success (hopefully, query was successfully completed) Code: 200 (HTTP response code) Method: check_email (application module that was queried) Message: Email already exist email_exist: true (email already exists) email_count: 15 (this is perhaps linked to the number of mobile numbers this email address is linked with) verified_account: false (I think this email account has not been verified)
Hence the reply does have information about the <email address> we did a query for. Now, let us post this request/response, another request is sent for ‘forget_password’ module.
Request Packet (HTTP) – Forget Password
Details: In this packet, a POST request is sent to /api/users/forgetPassword with data field as mobile=<email address> and the regular cookies with custom headers (X_PAYTMAPP_USERNAME & X_PAYTMAPP_KEY). Here is the response to this,
Response Packet (HTTP) – Forget Password
Okay! So, in the response packet we can see it has the content-type of application/json. Also, we can see it echoes back the email address we queried for. Remember – The ‘Forget Password’ also accepts the Mobile Number. So, will this work with the mobile number as well. For testing purposes, let us say the mobile number of the user is: 9123456789 (a random number). When we enter this number and check .. Voila! It actually exists with the vendor.
In the message, it says – We have sent an email to your registered id email@example.com. This way, the email address is not visible for extraction. Good Idea. Now, let’s see the request/ response packets for this request,
Request Packet (HTTP) – Check Mobile Number
Details: In this request packet, as expected a GET request goes to /api/users/<mobile number>/checkmobile. This is very similar to the checkemail module discussed before (compare it with: /api/users/<email address>/checkemail). Rest of the headers/ and fields remain the same.
Response Packet (HTTP) – Check Mobile Number
Details: In this packet we receive the validation message (same as checkemail). This message ensures that the mobile number is existing with the vendor. Also, it says ‘Mobile number unavailable’ but the mobile_exists: true. Rest, it echoes back the number. Let’s see what happens when the request is sent to forgetPassword module.
Request Packet (HTTP) – Mobile Number – Forget Password
Details: Here is the most important step. After validation, it sends a POST request to /api/users/forgetPassword with DATA as mobile=<mobile number>. This also contains the same custom headers/ and cookie values.
Response Packet (HTTP) – Mobile Number – Forget Password
Details: @^%#WTF. So, the response does show the registered mail address of the user. Bad idea! This can be leveraged to perform targeted spamming. Now, with this mis-configuration and little search online, an attacker can create a spear phishing email. Here is the information that can be used together with specific details like ‘mobile number’ and ‘registered mail address’ extracted from the website. URL: hxxp://www.consumercomplaints.in/?search=paytm URL: hxxp://www.complaintboard.in/complaints-reviews/paytm-mobile-solutions-l120761.html Extract information from here about the mobile number, order number and date etc. Find the registered mail address and WTH! you can definitely fool the user(s) from a forged domain and/or using your social engineering skills. Game Over. Here is a small program I have scripted for doing this work. Here is a screenshot of the same,
By the way, for the people who do not vote public disclosures, I have already tried reaching paytm team multiple times, but no positive replies or follow-ups from them. Here is a brief on the disclosure timeline I followed,
- 20.07.2013: I shared first level information with the website via contact us page)
- 27.07.2013: I opened a ticket 100794989 (via an email to firstname.lastname@example.org).
- 28.07.2013: I received a reply about an expected callback within 24 hours.
- 01.08.2013: My FINAL MESSAGE to the same email thread with a 12 hr. window for acknowledgement.
- 02.08.2013: Public Disclosure
- 05.08.2013: Addressed and Fixed (Closed)
Thats it guys. Don’t click, don’t download, if you don’t know. Stay safe, and be smart!