Thursday, April 19, 2012

Words with Friends - Revisited

A few months ago, I showed some people how trivial it was to modify the Words with Friends Free application on the Android Market (or, “Play Store”) to allow you to play any word you desired (with the letters you had).  I’m sure I’m not the only one who figured this out, as it was literally as easy as changing one line in the application.  That was with version 4.62 of the application.


I tried to demonstrate the weakness a few weeks ago, but was surprised when I noticed some differences as I was reversing the app.  Apparently, Zynga wasn’t too fond of people doing this, so they decided to “fix” the weakness.  The version of the app I’m focusing on is 4.83.

Unfortunately for Zynga, their “fix” wasn’t really a fix for the issue.  Rather, it just made it a little more annoying for the reverser.  Their fix was to use code obfuscation to strip all of the class, field, and method names from the application’s source files.  What this does is change obvious methods like submitWord(String word) (just an example), to something like a(String b).  This helps protect what the app is actually doing and requires someone to follow the code flow and spend much more time understanding the app.  While I think that obfuscation is a great choice for all release-builds of applications, it didn’t actually fix anything!

The problem with the app is that the word files are stored on the device itself as “raw” resources.  A quick look at the app decoded with “apktool” shows the resources still exist on the device, meaning that the word validation still takes place in the client, which is a poor practice.

 

Worse yet, all raw resources are read the same way, using the openRawResource(int id) method.  This method will not be renamed during obfuscation, as it is an Android API call.  Therefore, a search for this method quickly shows us at least where the resources are read:


That gives us a good starting point.  Now we can work backwards to try to find the method that is performing the check.

Of the two results, let’s start with the first one, “c/a/a.smali”.  We see that the method that called “openRawResource()” is private a(Ijava/lang/String;)Z, or written in Java syntax: private boolean a(int, String).


The fact that it is a private method suggests that it is only called from this class.  So we should be able to search for a string like “c/a/a;->a(Ljava” and find where this is called:


At the bottom, you can see that the method is called, and its result is placed into v0 and returned.  Judging by the Exception string above, I’m willing to bet this method is what checks for a validly formatted word, and then look it up in the dictionary files.  In fact, what this method does is:
  • Make the word lowercase
  • Get the number representation for the first letter of the word (0-25)
  • Add that number to 0x7f050003 (probably where the resources start)
  • Pass the new number, which is the location of the raw resource word file it wishes to open, and the word to the function “a”
  • Return true or false depending on if the user played a real word
If we’d like to alter this to allow any word to be played, we just need to modify what gets returned from this function (or not call the function at all), just like the previous version.



You can now rebuild and sign the APK, install the new version, and continue to play whatever word you’d like.  Is this a real security risk?  Of course not.  It is just a silly game and should be taken only as a demonstration of an insecure design in a harmless manner.  Enjoy! :)

-jakev