Never trust user input. Most developers who have worked with online systems understand this concept, but identifying what constitutes user input can be confusing in larger systems, and a naïve approach is to assume that a videogame client’s limitations are validated somewhere else within the server’s code before reaching the current code path.

It’s not always the case that user input is blindly trusted. It could be that the system used wasn’t fully understood by the programmer. For example, in PHP, any parameter passed in the URL is referenced as $_GET['foo'] and is usually represented as a string, but the user can transform it into an array by passing arguments such as foo[]=bar rather than foo=bar. Because of the way PHP will handle mismatched types this can result in unintended behaviour or even security flaws later on.

With that preamble out of the way, what kinds of things can go wrong without proper user input validation in videogames?

Practical Examples of Failing to Validate User Input

This first set of examples is from a videogame I will call Blokes & Warmongers. The issues I discovered were reported to the developer of the game and were patched out. In this WWII-era first person shooter game each player has an account with several characters within different factions (American, German, and Russian). These factions each have their own unique equipment (weapons, vehicles) and mods (ammo, scopes, health kits on vehicles). The equipment is purchased using an in-game currency called credits which is also earned through gameplay. There are several limitations within the client which prevent doing things the player shouldn’t be able to. But the server is another story.

In this game, each weapon or utility has a weight, and the player cannot equip more than their character’s weight will allow for. That is, unless the player simply tells the server they’re going to equip a PTRD in every weapon slot—which is way over the weight limit!

The packet sent to the server for equipping items includes the target character ID to equip the item to, the slot number (1-4), and the ID of the item itself. Since every item in the game needs to keep track of several factors including the underlying item ID, custom name, applied equipment mods, ammo stock, durability, etc. they each have their own unique entry into the database.

Which means: if I get the ID of the item from one character, I can modify the packet to instead equip it to a different character. Normally each character has to purchase each item themselves after unlocking them, and each faction cannot use the other faction’s equipment. What happens if I send the same packet, but this time with an American character as the target? It also works! An American can be lugging 4 Russian PTRDs around, which is an item they shouldn’t even be able to obtain!

But then, I got an idea. An awful idea. A wonderful, awful idea… what if I changed the ID of the weapon to one less than its current value? Oops, that worked. I just equipped a random item to my character that was owned by somebody else! If I were patient, I could try iterating through all the items to find one I wanted, and the true owner of it would have to foot the bill from me using it, too! (I wonder if I could have also equipped my own items to other players’ characters…)

Another Example: Unlimited Ammo, No Tools Required!

Not all exploits require packet or memory editing to bypass limitations. This next example was achieved within the limitations of the game client itself. As previously mentioned, items in Blokes & Warmongers have a “durability” stat which reduces with use and requires credits to replenish. The durability of ammo actually directly correlates with the amount of ammo remaining, and the remaining durability of an item when entering into a battle is the maximum amount of that item which can be used. This means if a battle is entered with only 10 ammo remaining, the player will have just those 10 shots before their weapon will remain totally empty for the rest of the match; refilling its ammo is impossible until they exit the battle completely and refill it.

Another feature in the game is that when a player is killed they drop their weapon on the ground and teammates or enemies can pick it up and use it. If someone else picks it up and uses it, the original owner of the item isn’t charged and its durability remains the same. If the original owner picks up their own dropped weapon, however, they will still be charged for its use.

As a consequence of this, the player can enter a battle with 10 shots remaining, die, and pick up their own weapon from the ground. They would in effect have 20 shots, which isn’t all that impressive so far. But what happens when the player fires 10 shots from the picked up weapon? The server subtracts one ammo from the player’s stock each time, so the server faithfully subtracts until the player has zero ammo—except they’re still carrying their original weapon with 10 more rounds in it! When the player fires the 11th shot, the integer storing their ammo underflows and, when they spawn next, they will have 2^32-1 ammo. Not quite infinite if we’re being picky, but a problem nonetheless, and the player won’t be charged for any of its use!

Always Validate User Input

Just because the client has limits in place doesn’t mean the server can trust the user’s inputs. Cheaters can and will use packet and memory editing to achieve unintended behaviour, and even then the client may not successfully limit the player’s actions sanely.