Thursday 21 November 2013

Facebook bug bounty: secondary damage (one report that leads to more bugs), fairness, and why I really like reporting to Facebook


Usually, the process for bug bounty is as follows:

  1. Person finds a bug, reports it to company
  2. Company fixes the bug
  3. $$ sent to the reporter.
An example is a critical bug I reported to Facebook some time ago.

But few days ago, I read a blog by Facebook security: one paragraph states that "Bugs that lead us to more bugs get bigger payouts. In these cases, the initial bug is much more valuable because the subsequent investigation and fixing of the original bug leads us to additional issues that we can fix."

Frankly, I did not believe this;  why would Facebook (or any other company) increase the reward for bugs they found in their internal investigation? The person who reported the original bug would never know that thanks to their report, Facebook fixed bugs the reporter did not find.

It turns out, I was very, very wrong - Facebook does pay for secondary bugs, and are extremely fair about it. 

My original report:

Subject: Report a Security Vulnerability - Low-risk CSRF that adds verified students to school groups
Title: Low-risk CSRF that adds verified students to school groups
Product / URL: Groups for Schools
Description and Impact: Hello,

This vulnerability allows attacker to make victim a member of groups for schools.
It is very specific and low-risk because victim must:
1. have school/university email VERIFIED
2. not already be part of group

In order for this to work, this must also be victim's FIRST time to join group.

Attacker must know that victim has email verified in one of school/uni domains.

Reproduction Instructions / Proof of Concept:

1. a victim must have verified school address
2. victim must make GET request to{{schoolGroupName}}/_join_/
for example, in my test case:
victim with jxfXXXX@***.edu verified visits site with <img src="***/_join_/">
and becomes member of the group.

Basically, I hoped for the minimum reward because the bug I reported was, as you can see, really crappy one: very specific and has really limited functionality.
Few days later, Facebook Security team replied that they are looking into the issue and will reply when it is fixed. Then came the

Facebook's second reply: 

Hi Josip,
 OK, just talked with the team. This particular issue is actually a wontfix for them: the goal when you have a school/university email is for you to be associated with your school/university community. However, it did lead us to a related issue which would have allowed for CSRF to add people to arbitrary groups, so we'll be paying you a bounty for that. Nice work. :-)

Wait, what? Of course, I wanted to know what bug did I miss. I checked if _join_ CSRF works for usual groups, and it did not. So where was the bug?

Here is explanation from the sec team:
I don't think it's something you would have found; when we were investigating your report we found references in the code to a _join_if_can_ flag which worked the same way you described but on more general types of groups (not arbitrary groups though: I misspoke earlier). It looks like the flag wasn't being used and it was simple enough to remove. :-) 

WAIT, WHAT???!!111??!
I got a (pretty huge) reward for a bug that was IMPOSSIBLE to find to outside people, because it is pretty much impossible to guess the "_join_if_can_" flag - it was not in production. So, a reward on a basis of really, really low-impact report.

Through this blog I would like to say a GIANT THANKS to the Facebook security team, not only for running their program, but also for being extremely sincere and fair towards us, the bug bounty hunters. 

Edit: timeline of the report:

  1. 18. of October, 2013 - bug reported
  2. 18. of October, 2013 - reply from Facebook team (not bot, but team)
  3. 26. of October, 2013 - Facebook is working on a fix, should be live soon
  4. 20. of November, 2013 - Facebook team replies that their investigation led to another bug and they decided to award me for it
  5. 20. of November, 2013 - Chat with the team, bounty rewarded
  6. 21. of November, 2013 - This blog published
I am not really sure when exactly they fixed the vulnerability, or found the new flag/bug (never checked as it was low-priority). I can only assume it was between 26th and middle of November.

--Josip Franjković

Tuesday 30 July 2013

SQL injections in Nokia sites.


I have found out about Nokia security reward program somewhere mid-April. Reports of people getting one or more mobile phones made me interested and I started searching for bugs.
As usual XSS/CSRF did not bring people real reward (only Hall of Fame), I started looking for SQL injections (and SQL injections only).

In one week I have found total of 4 SQL injections in their sites, but will write about three of them as fourth one is fairly similar to one of these three.


SQL injection in

Found this site using Google. The actual vulnerable link was:

Vulnerable variable is the User-Agent header, there was no response on page except for blank or load, so it was blind SQL injection.

The query behind was INSERT INTO, MySQL was the database.
So, how do you exploit something like
INSERT INTO table(a,b,c)VALUES(1,2,'$user_agent')
if there is no error reporting, and you cannot see output from the database?

After trying to make valid PoC for 10 minutes, I did not know what to do. So, I asked one of my old friends, Bryan de Houwer. Few minutes later, he said "Hey, did you try inserting into multiple rows?"... I forgot that, for some reason.

You could do something like 
INSERT INTO table(a,b,c)VALUES(1,2,3),(4,5,6);
After some brute-forcing, I found out the INSERT query had 5 columns.

Setting my User-Agent to 
',1,1),(1,2,3,4,5)-- -

The site loaded, meaning the query worked.  

Now, you cannot use usual AND 3=23 in INSERT queries, as
 INSERT INTO a(b)VALUES(1 and 3=23);
is a valid query, and it will go through - meaning I cannot get any data. 

I needed some way to trigger error for true/false.

Now what?
One of my favorite blind SQL injection tricks is to make the database return multiple rows in a subquery.

For example, 
SELECT a,(select b from table) from table; 

will return "Subquery returns more than 1 row" - if there is more than one row, of course. 

Now, I used the UNION keyword to do the trick for me:

->  "Subquery returns more than 1 row";

with a CASE statement, my final injection looked like:

User-Agent: ',1,1),(1,2,3,4,(select 1 union select case when(substr(version(),1,1)=5) then 1 else 2 end))-- -
The page loads. 

User-Agent: ',1,1),(1,2,3,4,(select 1 union select case when(substr(version(),1,1)=4) then 1 else 2 end))-- -

Blank. See the change in version. 

22. April 2013 - Vulnerability reported.
22. April 2013 - Response from Nokia Team
23. April 2013 - Vulnerability fixed (That was pretty fast!)

 SQL injection in ***********

URL is hidden for now, as there is still one unpatched vulnerability. As soon as it gets patched, I am publishing it.
Found this site using **********
This is a PHP site from <2005. I think this told you enough.

Login using
User: ' or 'x'='x
Pass: 'or 'x'='x

From there, pretty much every SINGLE... well, everything was vulnerable to SQL injection.
The DB was PostgreSQL.
Error reporting was on.
Multiple queries, DROP/CREATE privileges, much like connecting to your database and sending your own queries.

23. April 2013 - Reported
23. April 2013 - Reply from Nokia Team
25. April 2013 - Fixed (took them some time to find login details).

Gotta love old, forgotten sites. subdomain SQL injection

Got no idea how I found this one.
The injection is blind, time based.




Fourth SQL injection is almost the same as first.

Nokia fixed those bugs really fast.

I was awarded a single Nokia Lumia 820 (yellow color, looks great and is an amazing phone) and the Top Reporter status for April.

Although those vulnerabilities were found in short time frame, and I was not really working my ass off, I have expected at least a Lumia 920 or another Lumia 820 - I know of people who got multiple phones for XSS/CSRF in similar domains.

I would like to thank the Nokia Incident Response Team for their quick fixes, and  Bryan de Houwer for reminding me of multiple inserts in SQL. 

Tuesday 23 July 2013

How I found my way into Instagram's Ganglia, and a bug with Facebook likes.


I have recently taken part in Facebook Whitehat reward program, and here are some of my findings:

Access to Instagram's Ganglia:

The original report of this one is too short, so I will post how I got there.
I have read this old post from Facebook Bug Bounty official page:
Great! I'll just run some nmap scans and find something...
2 hours later....

After some sub-domain bruteforcing, I have found there is a Ganglia here:
Unfortunately, it is protected by basic HTTP auth.
At this point, I did not know what to do, and then it came to me: let me search the all-mighty Shodan for "".
On the second page:
Visit the IP address, and you get Instagram's Ganglia. 
There was also a reflected XSS on<script>alert(2)</script>

This was reported on 22.4.2013, fixed "5 minutes later" according to Facebook security team, but I believe it was fixed between 23-28 April (I did not really check it).
Please, DO NOT test the IP; it is not Facebook's IP anymore. 

Facebook likes bug:

This bug is connected with Facebook likes and how Facebook managed them.
Visibility of likes is connected to visibility of objects (so, if objects is "Friends Only", likes are "Friends only" et cetera), and I have found a way to bypass that - that is, to get likes of the object.

So, whenever your friends like some object, you can check who liked it on following URL:[[ID of object]].

Example for my profile picture:
Facebook did not check who was making this request, so anyone could view likes of an object. After my first report, Facebook team replied, saying "this is unexpected case, but they will look into it".
This was somewhere at end of December 2012.

Great! I should wait for the fix and payment.

Somewhere in June came the reply saying Facebook's security engineer would dig into this more. 

So I replied with better PoC, maybe they take it. 


While likes may be public, this is really the easiest way to get list of at
least some friends of a certain profile.
For example, [[some profile]] has a totally closed profile.
[[Some profile]] does not allow public to see list of friends; you can check that here:[[some profile]]/friends
Now, I go to [[some profile]]'s profile and get list of people who liked that
profile picture:[[hidden]]
There is currently xx likes there, and they are friends.
Is there any way to get list of at least some friends, despite [[Some profile]]'s profile being pretty locked? I am pretty sure no APIs allow that without authorization.

The bug was fixed on 11.7.2013.

Engineers from Facebook told me it took that long to fix it because it was not a trivial change to make. I cannot believe how complicated some parts of Facebook are. 

I included this bug to show how even if bug is an "edge case" or "not an issue" first time, you can always try answering with a better PoC.

Also, I wanted to show what Facebook takes for "bugs" under privacy - so if you though some "design flaw" you found some time ago is not worth any reward, try reporting it. You might get surprised :)

Payments for these two bugs were quite much more than I expected. 

I have found more bugs, mostly in Facebook acquisitions or other Facebook websites. I will post few more bugs later during summer. 

Giant thanks to Facebook Security team for their efforts and generous rewards!
Also, thanks to Shodan for being a great tool!

Thursday 3 January 2013 cross site scripting and privilege escalation in Consumer Surveys


I have recently found a persistent cross site scripting and privilege escalation in Google Consumer Surveys. Here are proofs of concept for both vulnerabilities:
Cross site scripting (XSS)

You can create a new Google Consumer Survey here. I have entered  "</script><script>alert(document.cookie)</script> as name of my survey and clicked Continue. The JavaScript was executed. Now the problem was, how do I exploit this on other users?
When creating a survey, there are four steps. Step 1,3 and 4 links could be used to exploit it on other users, while Step 2 (still) gives a 500 Internal server error if viewing other people's surveys (I do not know why, maybe you can find something there :)). Here are the 3 links (the survey is deleted).

Visiting any of those three links would execute the JavaScript in your browser.

Privilege escalation
In the same service, you could delete anyone's Consumer Survey with a single POST request. Keep in mind that this is a paid Google service.

A POST request to this URL with following parameters:

POST parameters:

You could change survey parameter to any valid survey, and it would get deleted. 
When trying to visit a deleted survey, 500 Internal Server Error would pop out, and you wouldn't be able to view it. 

Thank you Google Security team for quick response and fix!