In this post we'll do some static analysis on the game binary and also look at some dynamic approach to learning more about the game itself. Finally, we'll also look at some network requests to get a hint on how things are working behind the scenes.
Before jumping into an exploitation phase, we must have an understanding of the game on a fundamental level. So in this post, we'll do some Information Gathering or Reconnaissance.
First off, let's check out the official page to see if we can find anything interesting and this could be a good starting point.
Our hackable components all live in their own sandbox, too (the net code and game logic are completely custom), so you won't have to hack the engine (Unreal 4) itself!
The above line does give us something important. We don't have to find and exploit any vulnerabilities within the game engine itself, but instead, all we have to do is to look for bugs in the game logic since most of the components are custom.
Going down a bit, we see that there are three different versions of the game based on the operation systems(Windows, OS X, Linux). This means the code had to be compiled for each one of those operating systems differently. But also probably not every file is different. So it would be interesting to look at the differences and similarities. To do that I wrote a simple python script that simply walks the directory tree to get all the files, and then creates hashes to identify the unique and identical files.
As you can see, there are a lot of identical files, some are DLLs, and some are config files. Nothing sounds too exciting here. Now maybe you're wondering why DLL files are present in the Linux and Mac game clients? It's because this game is based on mono, a Cross-platform, open-source .NET framework. This lets us write Windows .NET applications and compile them to run on Mac and Linux. Finally, the unique files are also not that interesting; you can see different binaries for the different systems. So
.dylib is a dynamic library for mac, while on Linux it's an
.so file(a shared object).
The actual game files are downloaded during the update process, so after gathering all the updated files, let's run the script again. Each of these fully updated clients are around 2GB, so this will take a while since hashing large files takes a couple of seconds.
The results showed a lot of
.ini files and also a bunch of
.pak Files. One file
GameLogic.dll seems interesting.
$ find . | grep -i logic
As you can see, there are three different for three different operating systems, seems like it's worth checking them out, but this time, with a dynamic approach.
Like I said in the first video, I will mostly do this on Linux, so let’s start with that and execute the Launcher. Since the Launcher is not the real game, so let’s just click on play and wait for the actual game to start.
$ pstree -aclp
If we look at the process tree, we see a bunch of children threads.
Each thread has a meaningful name, which is helpful. The process ID is
30900, let's check that out in the regular process list.
$ ps aux | grep 30900
So this binary that was executed lies in the
Let’s exit the game and see what happens when we call it directly.
We see some debug or log output, and the game is actually starting! So there's a way to start the game without the launcher.
Next, let's look at the /proc file system. So we go to
/proc and the process ID of our currently executed game, and with
ls we can view the available folders and files. Let's look at the memory map.
$ cd /proc/31041
$ cat maps
As we can see from the above-simplified representation, there are memory maps to some interesting files like
libGameLogic.so. If we try to search for
mono we don't find any references to it, so basically only the launcher is written in .NET using the mono framework and not the game itself.
Analyse the Binary
Let's start with the
$ file /path/to/PwnAdventure3-Linux-Shipping
PwnAdventure3-Linux-Shipping: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.27,
- Binary here is a 64bit executable.
- Dynamically linked
- Stripped (no debug info)
ldd we can see which dynamic libraries it requires.
The output of the above command shows all the dynamical libraries and interestingly
libGameLogic.so is one of them.
$ file /path/to/libGameLogic.so
libGameLogic.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, not stripped
As we can see, the
libGameLogic.so is a shared object, which is also dynamically linked, but this is not stripped, which means we can get a lot of debug information. Before we move on, let's check out the
fd folder, which contains a list of all the opened file descriptors and to which file they point.
live@live:/proc/31041$ ls -l fd
In the output,
2 are stdin, stdout and stderr respectively, but all the others are other files. Most of the files here are
.pak resource files.
Now when I tried to play the game, I got an error "Connection Error, Failed to load master server certificate". I confirmed that it works when the game is started using a launched but doesn't work when we directly start it. I looked into the environment variables and checked if there were any command-line arguments passed to the binary, but nothing special. I simply went into the folder where the binary was present and executed it from there, and it worked like a charm.
After dropping into the game, we'll look at the network connections made by the game.
$ netstat -a -c | grep -i pwn
We start to see connections made to
master.pwn3 on port
3333 is the master server specified in the server ini file and
3002 is the actual game server. Since both the
game.pwn3 are mapped to the same IP in the
/etc/hosts file; we only see
Now let's exit the game and open Wireshark. Let's try to monitor any interface and filter for all tcp packets to port 3333 and also apply the filter for only the packets that contain a payload.
# Filter for 3333 with payload
tcp.port==3333 && tcp.len>0
If we relaunch the game and drop in as some character, we start to see some traffic in Wireshark.
As we can see, the content of the packets looks like binary. However, there are a few ASCII strings in there, for example, "Ghost in the shellcode" which can be seen in some packets. "Ghost in the shellcode" is also the name of the CTF this challenge was part of.
Now let's switch to port
3002 which is supposed to be the game server.
# Filter for 3002 game server with payload
tcp.port==3002 && tcp.len>0
Here there's a lot more action, the game constantly sends updates to the server and receives some of them too. Of course, it's an MMO so its kind of expected.
At the start, Wireshark tells us that there are some weird packets. Here Wireshark tries to guess and decode certain protocol types, but it looks like it might be something custom. So in order to not get a wrong decoded protocol, I enabled only ethernet, ipv4, and TCP.
Here's the first time the game client sent something to the server that might be worth looking at.
That looks like ASCII, so that doesn't look encrypted at all. Moving around inside the game doesn't immediately show a change in the Wireshark dump; however, jumping triggers a slightly larger packet. And actually, it seems to trigger two, one for initiating the jump, and one for getting back onto the ground, kinda like key down and key up events.
I think we already learned a lot about the game today. But I just wanted to show you that it's important to do some investigation and slowly learn more about the game so that this helps in the exploitation phase. And also making notes is also important. While performing this recon, I was writing down what I did and what I found, because this way, I don't forget a week later what I already discovered. That's all for this post, in the next one we will open the disassembler.