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
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