The bug that I am going to describe here was actually discovered accidentally while I was checking my privacy settings in Facebook. And, it is so simple that one doesn’t need to be technical at all to find it. It could have been discovered by anybody (literally). I guess I just got lucky and the fact that I have been a Facebook user since 2007 aided in the discovery as well. But, the bottom line is that you just need to be looking at the right place at the right time to earn bounties from the various bug bounty programs out there.
Anyways, let’s get to the bug now.
Privacy Violation Bug#1
This bug allowed disclosure of “parents” information (to the public) of some Facebook users inspite of the privacy settings being explicitly set to not allow that information to be viewed by the public or friends. I believe this affected certain Facebook users and not all. Specially, those that have been Facebook users around 2007 or so.
I’ve had my Facebook account since 2007 and I believe Mark Zuckerberg did too :)
Both, Zuk and I were affected because of this. I am sure there were others affected as well.
I’ll let you watch this video http://youtu.be/UFd68EG3E98 to show this in action.
It was as simple as clicking a hyperlink for the “BORN” highlight on your timeline. That would take you to a page that looks something like https://www.facebook.com/<user-id>/posts/<post-id>/. And, you would see yourself tagged with your parents.
This bug was worth $5000. I think this is a pretty generous amount for this bug. But, I am sure they rewarded this considering the ease of how this information could be leaked and the privacy violation for a lot of Facebook users.
Before I get started, following is a legend:
- Victim – V
- Attacker – A
- public URL – PU
- Shared URL – SU
Now, let’s get to the issue.
There is a hidden feature in Slack that is not directly accessible from the UI. It is not documented either. But, it is a pretty simple call to an API endpoint that can be made via a proxy tool such as Burp. This API call is basically used to “unshare a file shared with a Slack user”. An important point to note here is that this vulnerability is regarding sharing a file with a different user and NOT within a channel. The sharing-unsharing aspect of files within a channel is a legitimate feature in the UI. It is also mentioned in the tweet from Slack here. But, this vulnerability is not about that. It is about sharing-unsharing files with *users* directly and not within channels.
So, due to this hidden feature, it is possible to share a file from V to A and then unshare it again from V to A (assuming V changes mind and does not want to share the file anymore with A) rendering the file inaccessible to A via a SU.
It was observed that it is possible to get past this control by accessing the now unshared file via a different URL – PU. Please see the video PoC or the Reproduction steps on how A can find PU and store it before V decides to unshare the file with A.
So, now after the file is unshared with A, A accesses PU (stored earlier) and the file now becomes public to everyone in the team without V’s knowledge. You can think of it as an Insecure Direct Object Reference vulnerability. This is the first problem.
Then, assuming V happens to navigate to that file again, V suddenly notices that the unshared file now has been made public via the PU without V’s knowledge or consent. But, V does not freak out because V can still revoke the PU and it won’t be accessible by A or anybody else anymore. This revoking feature is provided in the UI as well. This is true. The PU indeed gets revoked and becomes inaccessible and it appears that this file could not be accessed/viewed by A or any other team member by any other means.
But, the problem does not end just yet. On A’s Slack homepage, on the right hand pane, A notices that this file is still visible. A clicks on the file, refreshes the UI and can still view the contents of this file with whatever changes V has made or makes in the future. This is the second problem.
So, this is clearly a security vulnerability where an attacker can view a file despite of it being unshared repeatedly.
I also sent them a video PoC demonstrating this in action. If you are interested, you can view it here. The video is a bit long (~9 mins) and the volume is a little bit low so you would need some kind of headphones to listen to my irritating voice :-)
The report along with the comments on HackerOne is available here.
I am disappointed with how Slack dismissed my original report without even bothering to read the report properly and making any sense out of it or ask me questions if they didn’t understand anything. I totally understand and respect their decision that this falls outside the scope of their Bug Bounty program but I wasn’t asking to be rewarded in the first place. I was simply reporting a security vulnerability. The scope and whether to reward a certain bug or not is completely on them and I understand that as a researcher, I need to respect that. Oh btw, they have not mentioned anything about “Undocumented APIs” in their scope so how would a researcher know what is in scope and what is out of scope? All I can see in their guidelines is “Our security team will assess each bug to determine if it qualifies.” But, they failed to assess the bug properly in the first place.
Anyways, some takeaways for both programs and researchers from this are:
- Read the bug report once. If its confusing or doesn’t make sense, read it again. Ask the researcher if its still not clear. Make an effort to watch/read the PoC provided. Don’t just assume things.
- Document features/functions/API calls if you allow them. Not documenting something and yet silently allowing them can be an issue as is evident from this case. They are relying on the fact that this feature is not being used by Slack users. This is naive IMHO.
- Revise your scope to make it fine grained and much clearer. Scoping is a constant learning/revision process.
- Don’t ignore the underlying problem which, in this case, I *believe* is the fact that the “permalink_public” URL is generated without the need of it. For instance, why would they want to generate this URL even before a file is revoked? And, even if they are generating it before its revoked, why send it to the client? It is like opening a can of worms. I don’t think its necessary to do that but they failed to even acknowledge that fact or reason as to why they are doing that.
- Researchers need to submit quality reports and should not be discouraged by dismissing responses. We need to change the general thought most Bug Bounty Programs have these days – that all researchers want is a bounty for a crappy report.
That’s it folks.
Edit: After I wrote this post, I found out this link – http://www.cnet.com/news/twitter-bug-makes-users-fear-invasion-of-push-notification-ads/
In that link, you will notice that Dick Costolo (CEO of Twitter) claims that “We don’t send ads via push notification. Will look into it.” This is dated way back in Sep’13.
So, after a year, they are still doing that, aren’t they?
I also wanted to clarify that even though this is only aimed towards followers (I haven’t tested against people who are not followers), it is still an ad/promotion being actively sent out as a push notification. That doesn’t happen for normal tweets. Followers don’t get notified actively when somebody tweets. Same thing with promoted tweets. It just shows up on the follower’s timeline. But, this is not the case here. The promoted tweet that I mention below doesn’t appear as a regular tweet on the timeline.
I recently discovered an interesting quirk on Twitter. Sadly, it is a Won’t Fix. I have requested public disclosure so it probably will go live soon. The HackerOne report number is #31073. But, below is what was reported in the meantime:
It was observed that I could promote ads on twitter without paying anything for them.
Steps to Reproduce:
- Sign up for a twitter account and enable Ads & Analytics on your profile. For the sake of PoC, this is abtest66.
- Create a campaign. The one that I did was “Website clicks or conversions” “Targeting interests and users”. I chose all locations and for targeting, I chose the following:
- Added two of my own accounts (abtest67, anshuman_bh)
- Targeted all my followers
- Targeted users like my followers
- Don’t select any promoted tweets as of now. Go ahead and launch the campaign. You will be taken to the payments page. Ignore that and navigate to the Campaign Dashboard. Notice that the Campaign shows as running.
- Now, edit this campaign and under the Creative section, add a few promoted tweets. I added 6. Notice that inspite of not having any payment setup, the user is allowed to add promoted tweets. I think this is the main problem here.
The result was that in my account anshuman_bh (one of my targets of the above campaign), I got a notification of this promoted tweet. See Screenshots 1 (notification of the promoted tweet) and 2 (the actual promoted tweet when clicked on the notification).
Also, under abtest66’s Analytics Dashboard -> Promoted section, I did see some data. See Screenshots 3 and 4. I believe this shouldn’t have happened either.
Hope this helps!
Twitter folks were not able to reproduce following the steps above so I had to send a better Steps to Reproduce along with a video so here it goes:
I have tried reproducing it again and it works. Here are the steps.
- Create a test account – @A1
- Follow @A1 from another account @A2
- Now, enable Ads and Analytics for @A1
- For @A1, create a new campaign -> Promoted Tweets
The URL will look like
- Enter the Campaign Name, choose Start immediately, target interests and followers.
- Add @A2 as a target. Also check the box “Also target your followers”.
- Choose Show ads in all available locations
- Add a promoted tweet.
- Set daily max 4.00 and max bid per engagement as 2.00
- Click on save campaign -> Launch Campaign
- Notice that @A1 is redirected to a payments page. Ignore the payments page
- Navigate to https://ads.twitter.com/accounts/. Notice the campaign shows running but technicallyits not.
- Now, go back to @A1‘s twitter homepage and tweet something.
- Notice @A2 gets a notification (on his mobile phone for example) saying @A1 just tweeted for the first time. Welcome @A1 to Twitter!
When clicked on that notification, it takes @A2 to the first tweet from @A1 – This is as expected. This tweet is also visible on @A1‘s timeline since it is an actual tweet.
- Now, go back to the Campaign created by @A1 and click Edit.
- Under tweets, add one more promoted tweet lets say test1
- Notice @A2 gets the same notification again saying @A1 just tweeted for the first time. Welcome@A1 to Twitter!When clicked on that notification, it now takes @A2 to the promoted tweet from @A1 test1 – This is not as expected. This tweet does not appear on @A1‘s timeline either. It is a promoted tweet which shouldn’t have been promoted.Basically, @A1 just promoted a tweet to one of his followers @A2 without running a campaign or paying anything.
Btw, this activity is captured in the Dashboard so you get all those numbers as well.
This was finally triaged and I got an initial reply stating:
“Thank you for your report. We believe it may be a valid security issue and will investigate it further. It could take some time to find and update the root cause for an issue, so we thank you for your patience.
Thank you for helping keep Twitter secure!”
But, a few days later, they replied back saying:
“Hello again. After consulting with the security team and the relevant engineering team, we decided since it only affects notifications of first tweet, the impact is so low that we aren’t going to fix it. Thanks again for looking at Twitter security.”
And, then another clarification saying:
“Hi, please let me clarify. I should say that it only happens when it shows up via a notification (such as first tweet notification). You should only be able to get notifications sent to people who follow you. So in this case you’re “promoting” tweets to people who follow you, in which case you could just have tweeted. Anyway, please let me know if I’m missing something.”
To this, my final replies were:
“The first tweet notification should technically be sent to my followers only when I tweet for the first time. It does not get sent anytime after that. If I can leverage this behavior to send promoted tweets to all my followers as and when I wish, then I’d say I am abusing the platform and doing something that I am not technically supposed to do.
Not to mention, I get all the numbers in the Analytics Dashboard as well under Promoted tweets like who clicked, who retweeted, etc. I am getting all the impressions without paying anything. Isn’t this foiling the whole purpose for promoted tweets?
Yes, you could have just tweeted but when you tweet, your followers are not actively notified. It just appears in their timeline. In this case, the followers are being actively notified about it in the form of a notification. It is more like a promotion than just regular tweeting.”
“In the end, I’d say this really boils down to the business decision and risk acceptance. If you guys are okay with this behavior, I don’t have any problems. In that case, do you mind changing the status to “Won’t Fix”? Thanks!”
Phew! It took me around an hour to figure this mess out but I did and I am so glad I did. I just hope my post is helpful for anybody who might be facing similar issues when trying to proxy requests via Burp and eventually end up getting the damned “Received fatal alert: handshake_failure” error every time.
I tried searching everywhere but none of the forums were helpful. So, I had to combine tit bits from different forums and a little bit of my brain to get this sorted out.
The Burp forum here – http://forum.portswigger.net/thread/717/burp-ssh-tunnelling along with the error messages in the Alerts tab in Burp were helpful. Specially, the comment from a Burp developer in the above thread. But, they don’t mention any details and leave it upto the users to figure it out. So, I will hopefully try to help those who are still stuck with this error.
So, assuming you are trying to proxy requests to a website, and end up getting the “Received fatal alert: handshake_failure” error message, pay close attention to the error logs under the Alerts tab in Burp. You will notice a message saying “You have limited key lengths available. To use stronger keys, please download and install the JCE unlimited strength jurisdiction policy files, from Oracle.”
If you ignore that, you are going nowhere. So, lets get the stronger keys as mentioned in the above error message. But, before you do that, you need to first figure out the JRE version that is installed on your machine. I have a Macbook and the following command helped me determine the JRE version that was being used. This command can be found here – http://docs.oracle.com/javase/7/docs/webnotes/install/mac/mac-jre.html under “Determining the Installed Version of the JRE” section. The command is:
/Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java -version
I was running java version 1.7.0_60 which corresponds to JRE 7. The next step is to get the JCE unlimited strength jurisdiction policy files corresponding to the JRE version. So, I searched for “Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 Download”. Notice the 7 because I was running JRE 7. Depending on the JRE version you are running, you will have to search for the appropriate JCE policy files. Download the zip file, unzip it. You will notice a folder with a bunch of files. The 2 files that we need are “US_export_policy.jar” and “local_policy.jar”.
Once, we have those files, navigate to /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security. You will notice that there are already these 2 files present in that directory. But, these are the old ones that we need to replace with the new ones that we just downloaded. So, to be safe, create backup of the old “US_export_policy.jar” and “local_policy.jar” files. And, replace them with the new ones.
Voila! you should be done at this point. Fire up burp again and navigate to the website that was causing problems. You should be able to access it without any problems. You just replaced the older jar files with the newer ones with much stronger keys that would help in the SSL negotiation.
PS – Finding the damn folder to replace the jar files was the hardest part. There were tons of folders, at least in my case, where these jar files were located. But, replacing them didn’t help. I had to find the right path and eventually, the docs.oracle.com link pasted above came to the rescue. There were a lot of threads about changing Java versions, running different Burp versions, etc. but none of them were helpful.
The Squareup website has a feature where users can add mobile staff on their team to collect payments on their behalf, issue refunds, etc. Let’s refer to this user as an admin and his mobile staff members as staff.
The way this works is that an admin sends an invitation to the people he wishes to add as mobile staff as shown in the above pic. It does not matter if the invited staff already has an existing account on Squareup. As soon as the invitation is sent, the invited staff receives a link in an email. When that link is clicked, the staff is asked to create a new account to accept payments as shown in the pic below:
If that staff already has an account on Squareup, unfortunately he would have to use a different email address, as he will get an error message “Email has already been taken” if he tries to use his existing email. It sounds weird but this is how it has been designed to work. A user cannot use the same email address to be a legitimate Squareup user as well as a mobile staff on somebody else’s team simultaneously.
So, the staff now uses a different email address to create an account so that he can accept payments on behalf of the admin. After the account is created, the staff sees the screen below:
Notice how all the different tabs (Home, Sales, Items, Orders, etc.) in the top row don’t appear anymore. So, basically the staff is only supposed to accept payments and do nothing else.
Well guess what – that’s not true. With forceful browsing, this staff can easily navigate to any page in the dashboard he wants to. There is no check being performed at all. So, the staff has as much access to his “own” dashboard as any other Square user might have access to theirs. This means that one of the many actions that a staff can do is send invoices to the admin’s customers, which he is not technically supposed to do. There is a blessing in disguise here though. When the staff sends this invoice, the invoice appears to be sent from the staff and not the admin. So, the staff cannot impersonate the admin and send the invoice on the admin’s behalf.
But if you think about it in a real world scenario, if I as an admin have assigned a person A to be my staff member, I would certainly inform all my customers of this new addition to my team and my customers would trust A henceforth. I would not go beyond that and inform them that “Do not pay invoices that are not sent by me”. This means if my customers see an invoice coming from my staff member, they would not hesitate in paying that. I feel this is a very viable situation where a rogue staff with a little bit of social engineering can literally go berserk without the admin even knowing about it.
Square qualified this as an “UI aesthetic bug” and not really a security issue. They went ahead and fixed it too as it is not reproducible anymore. They said this has no security implications whatsoever. If you ask me, I’d say forceful browsing is a known security issue regardless of the impact. By design, if a staff is not supposed to see their dashboard yet they can, I’d like to call it a security issue. Whatever happened to least privilege access? Not to mention, the sending invoice feature discussed above is just one thing out of the many things that a staff could possibly do as a result of this. I don’t think any of those actions are intended to be performed by a mobile staff member.
Moving further, if the admin removes this staff from his team, the staff member can now see his entire dashboard, which is fine. But, this behavior would just confirm my suspicion that it was not intended for the staff to access anything in his dashboard in the first place as long as he was a mobile staff member. Why would you ask existing users to create a new account otherwise? Why not just allow existing users to have full access to their dashboard as well as act as mobile staff for other teams?
Sending Invoice Bypass
As soon as a user creates an account on the Square website, he is taken to the page https://squareup.com/begin#step/0/us_business_information which looks like below:
This is the first step for getting a new user’s account activated. But, this step along with the next few steps can be ignored/bypassed by directly navigating to the dashboard at the URL – https://squareup.com/dashboard that looks like below:
If you then navigate to Sales -> Invoices -> Create Invoice, you see the following screen:
Now, it is quite obvious from the UI that Square wants you to activate your account first before doing operations like sending invoices, creating your business public profile, etc. Isn’t it? At least from the client perspective it is.
Using a proxy tool such as Burp, send the request that looks like below. Replace the redacted values accordingly – Use the X-CSRF-Token, _sessionv2 values of the newly created user. Use any invoice number that looks sequential like 000006, and the payer_email as the email address of the person who you wish to send the invoice to:
You will notice that you will get a successful response.
So, what happened? This newly created user just sent an invoice to a person for an item that is not even owned/created by this user. The above item is actually created by a totally different user. The email received looks like below:
So, essentially there are no server side authorization checks being performed on:
- Checking whether a user is actually supposed to be sending invoices or not without activating his account, adding his bank details, etc.
- Sending invoices for items that are not owned by the user.
Not to mention, if this issue is coupled with the first issue of Forceful Browsing, it is quite evident how one issue can lead to a chaining attack and make other attacks feasible.
This bug has been fixed so it cannot be reproduced anymore. I responsibly disclosed this to Square and they fixed it.
Password Reset Token
In the Square Cash application (https://cash.square.com/cash/login), on requesting multiple password-reset links, the old links are still valid. Now, I believe it is acceptable to have old links valid if the latest links have not been used because it is confusing and a hassle for end users to know which one is the latest if they haven’t reset the password yet but requested for multiple links.
But, one would imagine that if the new links have been used, at the very least, all the old links get invalidated, correct? That’s not the case here. The old links continue to be valid and if an attacker gains access to them, he would be able to reset the victim’s password. Not to mention, these tokens are all present in a GET request (https://square.com/cash/login/set-password/<token>), which means they often, get logged in browser’s history, proxies, logs, etc.
The only positive thing about this flow is that the tokens get invalidated after 12 hours. But, if I were a determined attacker, 12 hours is a lot of time I’d say. I’d also like to mention here that the same behavior is not observed on their flagship Squareup website – https://squareup.com/password/reset/<token>. The password reset functionality on that website is working properly. Old tokens get invalidated once new tokens are generated. So, if they have it there, why not here? I don’t know.
PS – I reported one more issue that was pretty interesting. I am waiting for Square to fix it before I blog about it.
I believe this issue affects a lot of applications that have friction-less signup flows i.e. creating accounts without first confirming it via email. This can affect applications in different ways depending upon the functionalities that exist in that application.
For instance, this can lead to a Logical Denial of Service (which is what I will be discussing in this blog), spamming legitimate victim users of activities that they are not aware of, etc.
For the Logical DoS, the attack is actually pretty simple. The only assumption I am going to do here is that username enumeration is possible – which we see is a case for a lot of websites as it is considered by design and a feature provided to aid users:
- An attacker enumerates a legitimate user account email in the vulnerable application. Let’s say this is firstname.lastname@example.org. This of course means that the application uses email addresses as their username.
- The attacker then locks this user’s account by providing the right email and wrong password multiple times.
- The legitimate user tries to login his account by providing his password but cannot login because his account is locked out.
- Meanwhile, an attacker creates multiple dummy accounts that have email address in the format victim+<id>@gmail.com. Remember there is no email confirmation required for an attacker to confirm the creation of such accounts.
- Now, the victim user obviously has to request password reset since he is locked out. So, he goes ahead and requests one.
- At the same time, the attacker also requests password resets for the multiple accounts that he created i.e. victim+<id>@gmail.com. There is nothing stopping the attacker from doing this. The rate limitation on password reset is a moot point here because the attacker is not requesting the password reset for the same account multiple times. But, instead, he is requesting password resets for multiple different accounts that eventually correspond to the same email address. See the risk of friction-less signup yet?
- All these password reset emails will go to the victim i.e. email@example.com since victim+<id>@gmail.com is the same as firstname.lastname@example.org from an email perspective. An important point to note here is that common email providers like Gmail clubs these emails into one single thread making it even more confusing to determine which email corresponds to which email address. In Gmail particularly, unless one clicks on a small arrow to see the email address, all the emails say “To me”:
Thus, if there is no clear distinction in the body of these emails as to what account the password-reset email corresponds to, the victim will have a difficult time finding the right password reset link for his original account and will continue to be locked out. This results in a Logical DoS.