Cracking a Microsoft contest or why Silverlight-WCF security is important
A few days ago Microsoft Belgium did something great for the community; a contest! The prizes for this contest include Windows Phones, TechNet subscriptions, books…
The concept is simple: make sure you register for the MSDN/TechNet newsflash and play the online game (Mastermind look-a-like). If you subscribe to thenewsletter you’ll get an email every 2 weeks that will reveal a color. Each of these colors you can exclude while playing, since they’ll never be part of the solution.This means that readingthe newsletter can help you score better. And the only way to score is by solving the puzzle – but you also need to have the best time.
Also important to know is that only the best 20 times will win a great prize (like a Windows Phone 7 or a laptop). And to keep things interesting the high scores don’t show the top 20 scores.
Now there I was playing the game while I was debugging an application with Fiddler when I noticed something… the Silverlight was communicating with a WCF service. Nothing special here, were it not for the fact that after some time I was able to access the list of all high scores, insert my own highscore (which could make me win a Windows Phone or a laptop), …
After doing a few tests I notified Microsoft Belgium, but I guess someone already took advantage of this ‘exploit’. The top score is someone who solved the puzzle in a little over 2 sec… sure! Even if you tweak your mouse and have loads of luckit’s not possible to drag the4 balls that quickly.But this is as much the fault of the people who cheated as it is the fault of the people who created this game.
The reason why I’m writing this article is because I findit frustrating that people don’t always take security seriously. This is a game with cool prizes and this means people will try to cheat, so don’t make them. Make sure the contest is cheat-proof so everyone can play the game and win the prizes in an honest way. Because in the end it only takes 1 person to ruin the game and a few days after the start of the contest this already happened. Even if security isn’t always a functional requirement, don’t ignore it!
I’ve also been wanting to write an article on security for some time now and this seems the perfect case. Hopefully Microsoft Belgium will fix the security leaks, reset the score and give people the chance to really play! And for the honest people who reached the top scores, if you did it before I’m sure you’ll do it again.But more importantI hope people will learn the possible ways your applications can be abused and how you can fix it. This is really important for your internet facing deployments.
See update at the end of the article: There seems to be a server-side anti-cheating mechanism acthive so there is no need to re-initialize the highscores.
Now, let’s get started…
Intercepting the webservice calls
Like I was saying before, I stumbled upon these webservice calls while I was debugging an other project with Fiddler. Fiddler is this great tool that will nest itself as proxy between your applications and the internet. It can also monitor webservice calls and supports decoding of HTTPS sessions.
For example, when I click the highscores button of the gamea webservice request is made toSilverlightService.svc. As youcan see in the redrectangle it seems a method GetHighscores was called.If you already used Fiddler before (and I really hope you did) you’ll maybe find it strange that you’re not seeing the realXML that was sent to the server.This is because the binding on the service side is configured using binary message encoding.
To decode these binary messages you’ll need a nifty plugin for Fiddler: WCF Binary-encoded Message Inspector for FiddlerThis plugin will decode the binary messages to clean SOAP.
Now as you can see the GetHighscores method allows you to specify a minimium and maximum rank. Does this mean I could also get the top 20 high scores? Maybe…
Problem: *Webservice calls can be intercepted.
*Solution: Well there isn’t much you can do to prevent people from looking at the webservice calls. Just make sure you send as little as possible over the wire! And a little HTTPS wouldn’t hurt either (even though you can still decode it with Fiddler).
Connecting to the webservice
Now that we know the address of the webservice we could try to connect. Just copy the URL, create a Console application in Visual Studio 2010 and add a Service Reference. If everything goes well the code generation should start to kick in and all service methods will be available through a service client.
After testing all these methods it looks like I can use 2 of them without any problem. CreateEntry seems to create a new game but that’s it – this seems to be safe. But then there’s the GetHighscores method… very disappointing! Without any hacking or cracking I can call this method with min. value 0 and max. value 20 and this returns the top 20 scores. Besides that I also have the full names and the email adresses of the people who played.
Problem 1: Requesting all high scores, even the secret ones. If the top scores should be kept secret until the end, why still expose them using the GetHighscores method? The Silverlight application doesn’t use them so you are exposing more data than required.
Solution: Only implement methods and expose data that will actually be used by the Silverlight application. A better option would have been to create a parameter-less GetHighscores method. Since the top scores should only be available mid january one could have added some logic on the service side to only show the secret high scores if required.
Problem 2: This is actually the same problem… exposing too much data. The email addresses of the players are not displayed in the application so why expose them in the service? Don’t expose data if you don’t use it! This can have serious consequences. What if there were money prizes involved and I had to give up my address and/or bank account? I wouldn’t want anyone to be able to access this data.
Solution: Only expose the non-confidential data that is required by the application.
Problem 3: Anonymous access. I don’t understand why anyone could just access this service without any security?
Solution: Take a look at security options like Windows Live ID, Windows Azure, cookies or even custom message headers. Beware that most of these options can be ‘bypassed’, in this case it’s not even relevant, but some security is always better than no security at all.
Problem 4 (Steve Degosserie): Anyone can generate a service proxy by adding a service reference to the url.
Solution: Disable the metadata so you can’t use a service reference. This can be configured in the web.config (look for MEX/Metadata exchange). Do note that this isn’t enough; if the generated code is available in the Silverlight assemblies (see below) anyone can still use the service. This just makes it harder.
Decompiling the Silverlight application
If you navigate to the website Fiddler will show you all resources being downloaded, including.css files, .js files but also the .xap file. Then you can just copy the URL to the .xap file. When everything pops up correcty – besides the .xap file this means it was cached from a previous visit. Try clearing your temporary internet files or use InPrivate browsing to make the URL show up again. Alternatively you could search for the .xap in your temporary internet files.
If you download this file and open it with 7zip for example you’ll see the contents of the Silverlight application:
For this case I had to analyze the msbe155.Silverlight.dll assembly. Using Reflector I was able to decompile the assembly and take a look at the code and the resources.
Let’s start with the resources. Just by taking a look at the resources – based on the filenames – I already spotted the colors that were marked as excluded.
Now to be sure I’m not running on a hunch I decided to go and look in the code. Since Reflector allows you to decompile each class in each assembly I stumbled on the following code:
With a bit more effort I was able to gain access to all the resources and all the code, including some logic that could work to my advantage.
Problem: Secret business logic was discovered while decompiling the code and much of the processing is done on the client.
Solution: This kind of business logic should not be on the client but on the service (even if this wouldn’t be the best solution for this case). If you do decide to keep the logic on the client you should make sure not everyone can look into it (more on this later).
Cracking the security
We haven’t done anything too evil until now. Finding some excluded colors or accessing the secret top scores is still semi-evil. But what if I’m really evil and I want to cheat? I could try to add a highscore with my name and make sure this score has the best time. The best time would result in me being at rank #1.
When I saw the method SaveHighscore I was again very disappointed but this feeling went away rather quickly. The SaveHighscore method accepts the Highscore class but hold your horses! It looks like I can’t just make up every property! The following fields look like they implement some fancy logic to prevent me to adda randomhighscore!
The fields I’m talking about are:
There you got it! A SecurityHash! Now I can just give up… nobody can cheat here. Let’s play a bit and then get back to work. Wrong! I still had the assembly open in Reflector and after a few minutes I found a block of code that showed me how to build a Highscore object, including the security hash.
Without too much effort I now had the code required to position myself at rank #1. This could be a real cash cow for me; I could add some friends and family on the following ranks and get all of them sweet Windows Phones! Having this code was great but it didn’t seem to work. After all it looked like I was missing something from the original game; some cookies seemed to be required for everything to work correctly.
Finally I had to write a small MessageInspector for the outgoing WCF calls and an EndpointBehavior that used this MessageInspector to get the cookies working correctlyand poof… the highscore was added.
Problem: Custom security logic can be decompiled and can be used to exploit the system.
Solution: Adapt your application to have all the business logic on the server. In this case this isn’t an option because the scores are based on time and too many service calls would just slow things down. A better solution for internet facing applications is obfuscation. Invest in a product like Dotfuscator and you won’t be sorry! It supports code encryption, XAML encryption, string encryption and so much more! Don’t take the risk of people decompiling your code and finding all your secret business logic.
Maybe I took "KRAAK DE CODE" (crack the code) a little too serious but hopefully this case will help you understand that security is an important part of our daily routines as developers and we are responsible for the applications we build. And even if you’re not an expert on security there are many good resources available on the net: just take a look at Channel9, Windows Identity Foundation, Azure AppFabric ACS, books by Juval Lowy on WCF, …
I will post the MessageInspector, EndpointBehavior and the rest of the working solution after the contest.
Update 1: Added remark by Steve Degosserie to ‘Connecting to the webservice’.
Update 2: I’m happy to announce I just received a call form Arlindo (MSFT) about this article and he assured me that cheaters can and will be detected. I added several records in the high scores (with 2 other names besides my own) and he assured me they were able to detect all my cheating attempts. Besides that they also detected cheat attempts by other players, the 2sec. guy for example. Cheaters are still visible in the highscores but they will be removed manually. The developers are also working on the service to make sure the email addresses remain private. And maybe after the contest they will communicate how the anti-cheating mechanism works.
In any case I’m very pleased with Microsoft Belgium’s openness, quick response and the fact that they didn’t want me to delete this article. Thanks!