Web security and Mobster World: a tale of woe

I belong to a forum for web game developers 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

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.

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.

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.

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.

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.

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!

Sending messages

Once I got bored of looking for “boring” exploits, I decided to check out XSS 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. 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.

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 if you’re curious how much of a dick I can be when I’ve hacked you black and blue.

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 will give you a great deal of help. The most important thing here is that most database libraries have built-in features for keeping things at least moderately safe (bind variables, for instance, such as “SELECT * FROM FOO WHERE ID = ?”, where the library will make sure the variable that’s substituted for the ‘?’ is 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.

One Reply to “Web security and Mobster World: a tale of woe”

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.