TFAR Code Execution
If we can modify GVAR(actions), then we can change the values of our specific actions keybind. To do so, we must take a look at CBA’s CBA_fnc_addKeybind function. This is quite a large function, so I have provided a link to it on my hastebin. I will be using screenshots of snippets from CBA_fnc_addKeybind but will refer to line numbers. I recommend opening the hastebin to follow along. First off, let’s take a look at line # 80, where GVAR(actions) is given a value.
From this, we can see that it is a common namespace that CBA likes to make. CBA does this thing where they use virtual location objects to store variables like another namespace. I don’t understand why, but it does the job they need it to.
So ‘actions’ is a location object. From here we can take a few approaches, we could try to trick the game into pre-defining actions as another namespace (such as uiNamespace). This is difficult because locations and namespaces do not line up 1 to 1. We’ll likely run into an issue. The approach I decided to take from here was to figure out what sets the value of keybinds in the ‘actions’ namespace.
Take a look at line # 93 in CBA_fnc_addKeybind.
Here we set the action, and give it some keybinds in index #2. So if we can somehow modify the incoming _keybinds variable, we can still exploit this. Looking farther up, we can see it being used. Note that this image contains a lot of the content discussed below.
_keybinds comes from CBA’s CBA_fnc_hashGet method. It is then checked for default (or overwrite) and possibly overwritten. If the TFAR keybind is overwritten, this is game over, we can’t exploit it. So before moving on, let’s just make sure we’re not overwriting the preset value from CBA. In TFAR, we can find the CBA_fnc_addKeybind call for LRTransmit in fn_ClientInit.sqf.
Looking here, it seems like only a few parameters are passed. Taking a look back at CBA_fnc_addKeybind, we can see _overwrite is defaulted to FALSE.
Okay, so as long as the CBA_fnc_hashGet returns a valid keybind, the data will be inserted & we can exploit the lazy eval. Take a quick look at line # 69 in CBA_fnc_addKeybind, where CBA_fnc_hashGet is called. We can see two parameters passed in, _registry and _action. We already know _action is our action string, we can see so on line # 23, so lets look above where _registry is assigned a value.
Line # 62 shows us that _registry comes directly from the profilenamespace! This is terrific. Any exploit that runs back into profilenamespace or uinamespace is one that we can really abuse! We can also see that if a value is not set, then a default is given (line # 64-67). Okay last thing to do is know how CBA_fnc_hashGet pulls our keybinds from the registry.
In this, _hash refers to our registry (profilenamespace), and _key refers to our action name. So we start by finding our action within the registry. The list of action names is stored at index 1 (HASH_KEYS). If we sucessfully find the action in that list, then we go pick out our Keybinds from the list stored at index 2 (HASH_VALUES). This is all we care about. Take note that we return directly from the “select” command, which keeps a reference to the list. This will be important later on.