I belong to a forum for game developers (”http://www.pbbgdev.com”:http://www.pbbgdev.com) and I recently
posted about how to keep one’s game from being a target of the most common
security problems. The information seems, to me, to be so obvious, but apparently
there’s a lot of ignorance about how to secure an application as well as why it
matters. So let me relate a tale of exactly why website security is so damned important.
I relate the details of this hackery not only to brag (I am proud to have
hacked this game so thoroughly even if it wasn’t much of a challenge), but also to point out how “minor” security
issues can destroy a game (or other web application) completely. This is not
a “Security on the Web 101″ as much as proof that bad security can destroy a good
concept.
A long time ago, in a land far far away, there lived a game designer. We’ll
call him “Alphonso”. Because that’s his name. Makes things simplest that way, really….
Alphonso had a grand idea for a mobster-oriented PBBG(Persistent Browser-Based Game).
His idea was pretty decent overall, and he opened up the short-lived
site Mobster World. Don’t bother looking for the site, it died a long time ago.
And this story will tell you why.
In this game, Alphonso had built a few key areas that I’m going to cover:
* Logging in
* Jobs
* Buying Items
* Shooting a player
* Reading “private” messages
* Sending messages
h3. BUT FIRST…
The basic information in here is this: do not trust user-supplied information!
You can build an HTML page with all kinds of hidden form fields and use cookies
and all that stuff, but at the end of the day, if you assume that the user will
supply you with a valid URL, valid cookies, and valid form fields, you will
get a hacker eventually.
h3. Logging in
This was the most absurd area. You’d put in your name, password, and the
CAPTCHA image to prove you weren’t a bot. The security image was a collection
of three digits. The images were shown to you on the form and you’d enter the
digits in the appropriate field. Fine and dandy up to this point. Problem was,
the images were shown separately (CAPTCHAs usually show a single image that
contains all the numbers/letters) and this allows an attacker to analyze the
filenames of each image to figure out which corresponds to a given number. But
worse, the filenames were #.jpg. That is, the image representing “1″ was
“1.jpg”. So I could look at the form and see the <image> tags to know
exactly what I needed to type - very easy for a bot to do, by the way.
When I thought the login couldn’t get any worse, I noticed a “hidden” field. In
HTML, a hidden field doesn’t mean the user cannot see it! It merely means the
field isn’t immediately visible! This particular hidden field contained the
exact security string Alphonso was expecting. So my bot was very quickly able
to grab the expected CAPTCHA string and supply it. The CAPTCHA succeeded in
stopping only the most inexperienced of hackers, and those ones weren’t likely
to know how to script a bot properly anyway.
Also please note that having a CAPTCHA may indeed stop bots (though rumor has it good anti-CAPTCHA technology is more accurate than most users), but it may also annoy regular users, especially those with minor-or-worse visual problems. If you insist on a CAPTCHA, at least make it accessible to all users.
h3. Jobs
There were two kinds of jobs, where a player could perform a job to gain stats
and/or money. The “big jobs” were dangerous (rob a bank, steal a car, etc), and
could land you in jail if you failed. The “small jobs” weren’t dangerous - things like
petty theft, bar fights, etc. They didn’t have the same rewards, and therefore
I didn’t bother to try hacking them.
Each job would give you two or three options for how to perform the job,
usually a situation where you could choose to be stealthy or direct or whatever
(Robbing a bank via the front door or back door, and other totally unimportant
crap). But when the page was created, the
actions were pre-determined. The html would have hidden form fields saying
whether a given button was going to be successful. This meant when I chose to
rob a bank, the “front door” option would already be set up via hidden fields
to succeed or fail. So one could very easily submit the form with any button
they wanted so long as they set the value of that hidden field to “1″ instead
of “0″. Since big jobs were so risky, success yielded pretty good cash.
100% success meant tons of money and no time wasted waiting for your jailtime
to end.
Moral: Don’t set up future actions in hidden fields! It’s stupid and very easy
to hack! All Alphonso needed to do was do the random check after the form
was submitted and this issue would not have existed.
h3. Buying Items
But why bother getting a bunch of cash? What a waste of time! Because the game
was so poorly scripted so far, I decided to look at buying items, and sure
enough I was confronted with awesome hidden fields. The hidden fields would
tell the game that a certain button would buy item X at price Y. Hack the form
via a bot, and you could buy any item for $0. This meant the most
powerful gun for $0. All the ammo you wanted for $0. Bodyguards for $0
a piece. Bulletproof vests? $0. Medical kits: special limited time offer, two
for $0!
So you buy great items for free and you realize you don’t need money.
This is a clear case of relying too heavily on the form to determine what’s
going to happen. Instead of having the form store the cost of things, it should
be stored somewhere on the server - database, bdb file, whatever. User buys an
item, sends that item’s ID to the server, and the server pulls the price from the
only source it can trust: itself.
h3. Shooting a player
Mobster World was written to stress uneasy alliances. People start shooting
each other and the game degrades into total chaos if some of the mob families
(essentially in-game alliances) don’t force order by disciplining their members.
Because of this, shooting a player was usually not a good idea without a good-sized
family behind you. Unless, of course, you could cheat.
The “shoot a player” area was also plagued with hidden fields. By setting
the %-to-hit field to 100, all shots would hit. The best gun only hit
50% of the time, meaning you could fire off a shot and do no damage, but still
have all sorts of consequences. And if your target had bodyguards or armor
(both were essentially just ways to increase bullet-taking ability), your shot
could be totally wasted. So again, shooting was usually limited to a family
trying to take down another family. But with a 100% chance to hit, free healing
(bodyguards, body armor, medical kits), and free ammo, a cheater could
do tremendous damage relatively safely.
The game allowed a shot every 10 minutes, so even a cheater had his limits, but
with a single bot I was able to knock an unsuspecting don (leader of an entire
mob family) down to 6 bodyguards (from 12) in a matter of about two hours. A
smarter cheater could have run multiple bots and destroyed an entire
in-game alliance in an hour or less.
This is exactly the same as above - there was no need for the form to ever know
the chance of a successful shot. Calculate that on the server and only on the
server. Yeah, you might want to display it to the user, but don’t let the user
be the one who tells you anything other than the weapon they’re using (and of
course validate that they own the given weapon and have ammo for it) and the
player they’re trying to shoot.
h3. Reading “private” messages
This is where we move from forms to URLs. Reading a message would require a hit
to a page like “/messages.php?id=xxx”, where xxx is the id of the email. Well,
because Alphonso didn’t think users could modify the URL themselves, you could
put in any id you wanted, and then read anybody’s email. Using this passive
cheat, you could see what your enemies planned. Following up with a similar method on
the message deletion URL, you could see your enemies’ plans but keep them from
letting each other know! I was able to discover that my “enemies” thought I was
an ex-player they had pissed off a while before I started playing. I catered to
this fear and made up all kinds of interesting stories about revenge and such.
Simple fix here - if a user requests access to anything private, make sure they
are authorized to see/edit that item!
h3. Sending messages
Once I got bored of looking for “boring” exploits, I decided to check out
“XSS”:http://en.wikipedia.org/wiki/Crosssitescripting possibilities. I’m not a
security expert, so I only knew how to do something similar to what the
wikipedia article calls a “type-2 attack”. And I wasn’t interested in stealing
these people’s accounts or anything, I just wanted to mess with their game.
When sending a message, I found that I could embed any HTML I wanted. So with
very little effort, I made the private message receipt form appear to have a button on it that looked
like the usual “Delete” button, and made the rest of the real page end up
hidden so that the only button on the form ended up being mine. When my delete
button was clicked, it actually took the user to the “Shoot a player” page, with
one of my enemies as the target.
After some testing with a friend, I
discovered that I could make a user run literally any action in the game, from
failing a big job (giving them jailtime), shooting their own don, going into
hiding (forcing them to log out for 8 hours of real time, unable to perform any
actions), etc. Had I been evil enough I could have logged out all the players
who disliked me except for one, and systematically killed them one at a time.
With a little more tweaking, I found that I could use AJAX to actually make the person perform these actions
without even clicking a button. The incoming message could be as simple as “You
suck!”, and by merely viewing it, the player committed to the action(s) of my
choosing.
It is important to note that many designers think they can get around this
issue by stripping out <script> tags - this is not the case. I can embed
malicious code in something as simple as a <b> tag just by handling
a JavaScript event, such as onMouseOver. Simple solution: do not allow HTML in
user-supplied data. For my “big” game (Bloodsport Colosseum), I allow formatting
via “Textile markup”:http://en.wikipedia.org/wiki/Textile(markuplanguage).
There are many similar solutions for all kinds of scenarios, and they are, in
general, far safer than trying to allow HTML in any form, even if you think you’re
being careful.
h3. Email to Alphonso
I wrote Alphonso an in-game email asking if he was aware of cheating issues. I
figured he’d deny it like so many web app designers who don’t know security. He
surprised me:
yes I am aware of it and thank you very much for assisting me in this game: I
have other areas that I am repairing first and I will be getting there soon.
Please continue to inform me of areas that you find.
At this point I felt pretty bad and told him the truth - I’d been exploiting
the game from day 1, and I pointed out all the areas I thought he needed to
look into.
Read my response “here”:http://blog.nerdbucket.com/pages/emailtoalphonso_mw if
you’re curious how much of a dick I can be when I’ve hacked you black and blue.
h3. Final Word
Do not assume users won’t edit forms and submit bogus data. Do not let a user
alter or view anything he doesn’t own (if he says he wants to view message id
10, make sure he is authorized to do so!). Cookies, URLs, and form fields are
extremely easy to edit!
There is also the unmentioned SQL Injection attack. I can’t help much with
these as I know very little about the attack, but “this wikipedia
article”:http://en.wikipedia.org/wiki/Sql_injection will give you a great deal
of help. The most important thing here is that most database drivers have
built-in features for keeping things at least moderately safe. USE THEM!
Web security is much more important than most programmers seem to realize. If
you want a game or other app to get popular and last a long time, do not
skimp on security. Or you, too, could end up with a good idea that does as
well as Mobster World.
“Digg it”:http://www.digg.com/security/WebsecurityandMobsterWorldataleofwoe!