PiTyUs.Hire me

Engineering

FiveM server security — exploits, event abuse and resource theft

How FiveM servers actually get exploited: event validation, trust boundaries, backdoored resources, txAdmin hardening and what anti-cheat cannot do.

Updated 12 min readBy PiTyUs · FiveM developer

The one rule: the client is the attacker

A FiveM client is a program on someone else's computer that you do not control. It can call any event you register, with any arguments, as often as it likes. It can lie about its position, its money, its job, the item it is holding and the vehicle it is in. If your server believes any of that, you do not have a bug — you have an economy that anyone can print money into.

If a value arrived from a client, it is a request — never a fact.
Client sendsServer mustNever
An item nameLook the item up in a server-side tableTrust the label or the price attached to it
A quantitytonumber, range check, integer checkPass it straight into an addItem call
CoordinatesRead GetEntityCoords(GetPlayerPed(src)) itselfUse the coordinates the client claims
A target player IDVerify both players exist and are near each otherAssume the ID is anyone but the sender
A price or a rewardRead it from config on the serverAccept a number from the payload

Event validation in practice

Here is the shape of a server event that cannot be abused. It is not complicated — it is just consistently applied.

A hardened server eventlua
local COOLDOWN = {}

RegisterNetEvent('garage:retrieve', function(plate)
  local src = source

  -- 1. rate limit
  local now = GetGameTimer()
  if COOLDOWN[src] and now - COOLDOWN[src] < 2000 then return end
  COOLDOWN[src] = now

  -- 2. type check
  if type(plate) ~= 'string' or #plate > 8 then
    return DropPlayer(src, 'Malformed request')
  end

  -- 3. identity, from the server
  local identifier = GetPlayerIdentifierByType(src, 'license')
  if not identifier then return end

  -- 4. ownership, from the database
  local owned = MySQL.scalar.await(
    'SELECT 1 FROM owned_vehicles WHERE owner = ? AND plate = ?',
    { identifier, plate }
  )
  if not owned then return end

  -- 5. proximity, measured server-side
  local coords = GetEntityCoords(GetPlayerPed(src))
  if #(coords - Config.GarageCoords) > 10.0 then return end

  -- only now do the thing
end)

AddEventHandler('playerDropped', function()
  COOLDOWN[source] = nil
end)
  • Use RegisterNetEvent only for events clients are meant to call. Internal server-to-server logic should use plain AddEventHandler.
  • Never register a net event that takes a money amount, an item to give or an admin action without an authority check.
  • Rate-limit anything a loop could spam. An unbounded event is a denial-of-service vector as much as an economy exploit.
  • Log unusual input rather than silently returning. The first sign of an exploit attempt is a burst of malformed payloads.

Admin commands and permission checks

Admin functionality is the highest-value target on any server. The mistake is checking permissions on the client — showing or hiding a menu — and then trusting whatever that menu sends.

Permission checks belong on the server, every timelua
RegisterNetEvent('admin:setJob', function(target, job, grade)
  local src = source

  -- ACE check, server-side, on every call
  if not IsPlayerAceAllowed(src, 'command.setjob') then
    return DropPlayer(src, 'Unauthorised')
  end

  if type(job) ~= 'string' or not Config.Jobs[job] then return end
  -- ... apply
end)
  • Use the ACE system with principals and groups rather than hard-coded identifier lists scattered through resources.
  • Never gate an admin action on a client-side variable such as `isAdmin`.
  • Give staff the narrowest permission that lets them do their job. A moderator does not need the ability to spawn money.
  • Log every admin action with who, what, when and against whom — to a channel staff cannot delete from.

Backdoored resources

The most common way a FiveM server is fully compromised is that its owner installed a free leak that contained a backdoor. These are not subtle once you know what to look for, and they are extremely common in "free release" packs.

  • Obfuscated or minified Lua in a resource that has no reason to be protected. Escrow looks different — escrow files are marked and unreadable by design; a wall of base64 in an otherwise plain script is a red flag.
  • Any use of PerformHttpRequest to a domain you do not recognise, especially on resource start.
  • load(), loadstring() or assert(load(...)) applied to a downloaded string — this is remote code execution by design.
  • Hidden ACE grants: add_principal or add_ace calls buried in a script instead of your config.
  • Events with meaningless names that execute arbitrary commands, or a hidden command that grants admin to a specific identifier.
A five-minute audit of any new resourcebash
grep -rniE "loadstring|load\(|PerformHttpRequest|add_ace|add_principal|ExecuteCommand|base64" .
grep -rniE "http(s)?://" . | grep -viE "github|cfx|fivem|localhost"

What anti-cheat can and cannot do

Anti-cheat resources detect client-side modifications — menus, injectors, blacklisted natives, impossible movement. They are useful and you should run one. They are also, structurally, the second layer of defence.

ThreatAnti-cheat helps?Real fix
Mod menu spawning vehiclesYesAnti-cheat + native blocking
Player teleportingYesServer-side position sanity checks
Calling your money event directlyNoServer-side validation
Spamming an event to lag the serverPartiallyRate limiting in your own code
Stealing your resourcesNoEscrow, access control, trusted staff
Weapon damage modificationPartiallyServer-authoritative damage handling
  • Keep sv_scriptHookAllowed set to false.
  • Prefer a maintained anti-cheat over a leaked one — a leaked anti-cheat is, by definition, a resource of unknown provenance running with wide permissions.
  • Do not stack three anti-cheats. They fight each other and produce false bans.

Hardening the machine, not just the game

  1. Never expose txAdmin to the public internet. Bind it to localhost and reach it over an SSH tunnel or an authenticated reverse proxy with HTTPS.
  2. Never expose MySQL to the internet. Bind to 127.0.0.1 and connect locally.
  3. Use SSH keys, disable password authentication, and do not run the FXServer process as root.
  4. Firewall everything except the ports you actually serve: 22 (or a moved SSH port), 30120 TCP and UDP, 80/443 if you host a site.
  5. Keep off-site, automated database backups and test a restore at least once. An untested backup is a hope.
  6. Give staff individual accounts everywhere — txAdmin, the panel, the host — so you can revoke one person without changing everyone's password.

If it already happened

  1. Take the server offline. Do not try to debug it live while an attacker still has access.
  2. Snapshot the database and the resources folder before you change anything — you will want the evidence.
  3. Rotate every secret: database passwords, licence key, bot tokens, panel logins, host account.
  4. Find the entry point. Check recently modified files, the resources you installed last, and your web-facing services.
  5. Restore from a backup taken before the compromise, then reinstall only the resources you have audited.
  6. Tell your players what happened. Communities forgive an incident handled openly far more readily than one they discover themselves.

Frequently asked questions

How do FiveM servers get hacked?

Overwhelmingly through two routes: unvalidated server events that any client can call with arbitrary arguments, and backdoored resources installed from leaked or free packs. Exposed txAdmin panels and database ports are the third.

Is an anti-cheat enough to secure a FiveM server?

No. Anti-cheat detects client modifications, but the most common exploits use entirely legitimate client behaviour — calling your own server events. Server-side validation is the primary defence; anti-cheat is a second layer.

How do I check if a FiveM script has a backdoor?

Grep the resource for loadstring, load(), PerformHttpRequest to unknown domains, ExecuteCommand, base64 blobs, and hidden add_ace or add_principal calls. Obfuscated Lua in a resource with no commercial reason to be protected should be assumed malicious.

Should I run txAdmin on a public port?

Never. Bind it to localhost and access it through an SSH tunnel or an authenticated HTTPS reverse proxy. An exposed txAdmin panel gives an attacker full console access to your server.

Need this built, not just explained?

Ten years of FiveM work, from Lua to NUI

Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.

Related guides