I have covered remote execution in the past. Everyone by now knows that any remote execution cheat is brutal to the security of game servers in Arma. The developers have made strides to block unlogged and unfiltered remote execution, and they have come a long way since Arma 3 Alpha. This post covers an exploit that has existed in the Real Virtuality engine since RV3, Arma 2.
If you haven’t read my last post, Breaking the SQF Firewall. You should do so now. Piggybacking on this last post, there is a vulnerability in how the game processes network messages prior to the Firewall killing sqf threads. Specifically, the publicVariable and setVariable network messages can be sent to the server prior to the firewall killing our existing sqf loops. I am going to cover the vulnerability first and finish up with a series of exploits I have created that use the vulnerability to take advantage of other aspects of the game.
Just as an introduction, here is an example of how the vulnerability operates. For all examples in this post, clicking the image will open the code in a new tab.
This script operates by using the onEachFrame command at the main menu. Using onEachFrame in this way will carry our code all the way into the server. Terminating at the SQF Firewall. The exploit exists within the if statement on Line 2. By defining a variable and then publishing it over the network, we can distribute data onto all machines loaded into the server, including the server itself. Lines 5 and 6 show the two commands we can use to distribute our data. Both work, however, I found that setVariable is often the command server owners leave the least protected.
From this example, we can see that the code for this exploit is both simple and extremely powerful. Being able to distribute any data to all machines opens a huge wave of possibilities.
I want to start by saying that in no way do I condone using any of these exploits on servers. I will be providing the tools for servers to detect this style of exploit. I simply want to showcase how important it is that servers protect themselves from this vulnerability. Some of my examples may be way out of date. I am not going to do research on them, so if my claims are invalid now than let us all be thankful the example I provide no longer works. Now, onto some examples.
Disabling Server Connections
First up, we have a method of preventing future players from loading into the targeted server. This exploit breaks BIS_functions_mainscope, a critical object used during the functions initialization process. By breaking this variable, all connecting players will get locked in the loading process, preventing them from both joining the server, and leaving the server. This forces all future players to restart their instance of Arma.
A3Wasteland SQF Code Execution
This exploit lets us run code after connecting to an A3 Wasteland server. playerSpawn is a function that is unprotected, not compileFinal’ed, that runs during the loading process in A3Wasteland. GUI_Run is the variable my exploits use to define what code we want to run.
storeParamsValues Code Execution
This exploit I want to cover in detail. First, a look at the code.
This exploits BIS_fnc_storeParamsValues and BIS_fnc_getParamValue to execute code after the connection process.
Here is a quick look at BIS_fnc_storeParamsValues. This is the section of code we are interested in, the rest I could care less about.
Okay, so we can see the variable is compileFinal’ed, this is good, also it is global, so we can safely assume the server distributes this value to all clients. Now a look at BIS_fnc_getParamValue.
Okay, from this we can see the variable BIS_fnc_storeParamsValues_data is run as code. This variable should be compileFinal’ed and distributed to clients, so we shouldn’t have any issue.
This is another critical component of the exploit we found. It turns out, data distributed over setVariable global, can be overwritten by a client during the connection process. This allows the exploit shown to operate. During our connection, we overwrite the original value of BIS_fnc_storeParamsValues_data with our new value. For some strange reason, the server is completely okay with this. As far as I could tell, it only works for variables distributed with setVariable, however, I could be mistaken on that fact.
As you can see from just these three examples, there are a lot of nasty things this exploit can do. It does get worse, but I don’t want to show the code for my other examples as they really would be game-breaking for most servers.
Now, how about server protection?
In order to protect your server, you need to understand two simple facts.
- Always protect your functions with compileFinal
- Have strong BattlEye filters
If your client functions are protected with compileFinal, there is no way this exploit can overwrite them. As well, this exploit is entirely logged by publicVariable, setVariable, and setVariableVal filters. By having strong filters and protected client functions, your server will be perfectly safe from this exploit.