Insights on the incidentFor a few years now FAF is running an open api, offering player and game data for free use to build cool features for which the core FAF developers don’t have time. As part of that it has always been possible to query the list of players and filter on them.
Example: the API call
https://api.faforever.com/data/player?f ... tus5000%22 returns my user.
If you look closely you will see that there are only very basic fields to see (in the “attributes” block): create date (of the account), update date (which is the equivalent to “last seen”) and the username.
In the database this is querying data from this table here
http://faforever.github.io/db/tables/login.html and as you can see there are other very sensitive information like your email or your password hash.
This fields were also available in the api, but only after login and only for a limited set of people. Basically moderators are allowed to see all the sensitive fields (for support cases with lost email, steam id etc. and for detecting of fake accounts, duplicate accounts etc.) and every player was able to see it’s own fields. A whole bunch of tests in our application ensured the correct hiding of these fields for unauthorized people.
And so we thought we were safe. Until silenceluke launched his new service
https://fafscore.nl . For some users fafscore asks for their username. Other users were magically detected. So people asked us how that was possible. When we investigated the website, we noticed that fafscore was calling the player list as above, but instead of filtering on the user name it first tried to filter on the users ip-address and checked for a match with the last login ip address.
But the ip address was supposed to be hidden away from non-authorized access. Alarm bells started ringing. As it turns out it was possible to filter on every field even if it was hidden from viewing. Even with wildcards!! And that is a huge vector of attacks:
Now imagine you want to know my last ip address. You could just use a filter query that you know to return a result and then start appending wildcard queries. If there is a match, your partial guess is correct, if there is no match, your guess is wrong.
So you could take any player query and append &ipAddress==”1*”. If it gives a match, you check for the next character: &ipAddress==”11*”. If there is no match you test the next allowed value for the character: &ipAddress==”12*”, &ipAddress==”13*”, &ipAddress==”14*”.
You do this until you found the whole value. Using this attack it was possible to resolve an ip address with a maximum of 120 http calls. Or an email with roundabout 1000 calls.
When we are able to confirm the criticality of the isse, we contact the authors of the library responsible for this (some very nice people at Yahoo) who after some explanation gave us a workaround and fixed the issue within the next week.