I think many of the readers with a kind word will remember the
Cossacks series of games, many hours of battles, military tricks and unmatched soundtrack - an excellent strategy of its time.
After 15 years, they returned, and now on-line, the problems and vulnerabilities of the new version will be discussed in this article.
Disclaimer
All studies were conducted solely for good purposes, the
right holder and the developer were deliberately notified of the problems found, the article is instructive in nature and does not call for the active exploitation of the problems described.
Network packet structure
It all started with a simple question - “how is the network data encrypted?”, After catching the first packet, the answer became obvious - “no way”. No xor operations, no signatures, honest and truthful bytes.
')
At the same time (after realizing the simplicity of the possible analysis), it was decided to go further and try to recreate the primitive operation of the server (login, registration, password recovery), for this it was necessary to understand:
- What is a package header?
- How data is serialized and deserialized
- Where data about servers is stored (IP, port)
After catching the next ten packs, the “cap” became bright and very clear:
struct NetPacketHeader { unsigned int Size;
In the same way the main features of serialization were revealed:
- Writing and reading is done using special classes (the initial guess was that these are simply reduced structures)
- There are two types of strings - small and large, the size of the string is indicated before its beginning, in the first it is one byte, in the second it is two bytes
- Numeric data is written as is (floating could not be found)
- There are special blocks filled strictly with zeros that serve as a separator: 4 bytes - the end of the array, 6 bytes - the end of the record (initially it seemed that this is a structure offset from the compiler)
A list of servers was found by the usual search by name (
data \ resources \ servers.dat ), the readable script of its own production turned out to be the data keeper.
Upon completion of these steps, the decomposition of packages began to be reduced only to perseverance and attentiveness, for example:
3D 00 00 00 9A 01 00 00 00 00 00 00 00 07 31 2E 30 2E 30 2E 37 05 31 2E 32 2E 33 14 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D 0A 61 61 61 61 61 61 61 61 61 61 0E 39 30 30 30 2D 38 30 30 30 2D 35 30 30 30
Size: 3D 00 00 00 (just 75 bytes in the packet, but 61 bytes is the body, and 14 others are the header)
Direction: 9A
Mode: 01
SessionId0: 00 00 00 00
SessionId1: 00 00 00 00
VersionStringSize: 07
VersionString: 31 2E 30 2E 30 2E 37 (1.0.0.7)
UpdateStringSize: 05
UpdateString: 31 2E 32 2E 33 (1.2.3)
EmailStringSize: 14
EmailString: 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D (aaaaaaaaaa@gmail.com)
PasswordStringSize: 0A
PasswordString: 61 61 61 61 61 61 61 61 61 61 (aaaaaaaaaa)
GameKeyStringSize: 0E
GameKeyString: 39 30 30 30 2D 30 30 30 30 2D 35 30 30 30 (9000-8000-5000)
This is the server login request packet.
Work from the lobby
In a short period of time, a primitive server (based on
asio ) was made, able to communicate with the original client, in a little more than an hour he could:
- Process login request
- Process registration request
- Handle password recovery request
- Process private and public chat message
- Get a list of users on the server, as well as the lobby
Such a quick realization was better than any coffee and it was decided to spend the rest of the night working with the lobby, namely:
- Create / Update / Delete
- User synchronization (flag color, country, etc.)
- Opening / closing slots by the creator
From the very first point, the problems that were not completely understood (at first) began:
It turned out to catch and decompose three pairs (client request and server response) of packages — creation, update (which also included deletion), as well as entry into the
public lobby.
It was the latter that caused headaches, the entrance to the public lobby went off with a bang, but it wasn’t quite private; it wasn’t clear where to look for the password entered by the user, to check if the data was correct, because there’s one package - how to enter the public , and to enter the private lobby, and it contains only a cap and a single whole (session ID of the creator of the lobby), for explanations had to climb "under the hood", but not accustomed to the result of the compilation of Delphi eye code did not find anything sensible.
In the end, it became obvious - the server does not handle lobbies passwords in any way, from the word “absolutely”, which means in theory it was possible to go to any lobby and on the original server, because the client receives the password in its pure form.
The theory was proven in three steps:
Getting the password of the desired room That's right - private == public.
The rest of the agenda items went relatively easily, user synchronization is a simple mirror, from one user to all lobby participants, closing the slot (for example, to kick a participant out of the lobby) caused a little concern, the server accepts the request to close the slot if the slot I was busy with the participant - he kicked him out, sending an alert, but if you ignore the package on the client’s side - visually we stay in the lobby without losing contact, does it cause problems with launching the game for the remaining participants - good Question the answer to which is obtained from the time of the day failed.
It is also no small concern caused by the issuance of the name of the PC creator of the lobby, why it is given to the participants at all if the creator! = Host is a question that has yet to be answered.
At the end of the night, it was possible to reach the entrance to the game, out of ten attempts to synchronize the gameplay, only one was successful, and she was successful only in part, one player does not receive data on the actions of the other, which causes asynchronization and leads to a hasty stupor of the game, which means many more.
Summing up
Any programmer who had to bring the network application to the public somehow felt the main rule: never trust the client.
Each client action must have server approval, otherwise there is a significant risk, if you don’t ruin the whole project, then lay mines in it that will explode at the most unexpected time, a good example, but poor network work is
MU Online , more than ten years old. , and the trivial problems of cloning game items (using package manipulations) led to the fact that they
had to disable the functionality of the personal store.
A separate topic for discussion is the preservation of personal data, as can be understood from the examples described: such an approach, the transmission of pure bytes, and especially lines, is a great sin for any company operating in the information sphere, a direct way
to hell for stealing accounts.
→
GitHub Example