A complete guide ~ How exploits work & how to best prevent them

Over the summer i’ve taken up reverse engineering and strengthened my C skills. I tend to see a lot
of misinformation in threads regarding exploits, and now knowing how they work I felt it was time to make everything clear and help developers in preventing them.

Firstly to clarify:
yes, I have reversed Roblox to write an exploit to mess around with their Lua to get a feel for how it works. I don’t plan on ever publicly releasing my exploit nor using it for cheating in actual games. I just wrote it simply for learning purposes.

First: what types of exploits are there?
There’s a lot of exploits that exist for Roblox. The most popular are script executors, since they allow you to do almost anything you want without restrictions.
There’s 3 main types of ways that exploiters achieve script execution, but they’re not too important to helping you develop counter measures.

Second: why doesn’t Roblox do more to prevent exploiters?
After seeing their security measures, I know they implement as much as they can to prevent exploiters. The only reason there’s so much exploits
is because of smart and dedicated people in the community who work together to bypass Roblox’s checks, such as louka, eternal, defcon, ect.

Roblox doesn’t do more to prevent exploiters simply because they can’t without risking breaking a lot of games, or it would require sketchy UAC privelages, which might make people think that Roblox is a virus.
The anti-exploit development mostly comes down to you, as the developer. You understand your game the best and therefore can prevent exploits the best on your game.

To clarify some things before moving on:
1. It’s near impossible to detect an expliot by just injecting it. Roblox does do this, but it’s actually not the hardest to get around.
Some exploits are however detectable upon injection, we’ll cover that in a bit.
2. Roblox actively tries to stop exploiters. That’s why theres updates every week, to basically shuffle important info that you’d need to get an exploit working.
3. Roblox can’t just check if an exploit’s window is open. It does check for certain windows, but most exploits will just randomize their window name.
4. You have to write your anti-exploit around detecting scripts instead of exploits themselves.
5. Filtering Enabled is not a one fixes all solution. All this does is prevent exploiters from replicated stuff to the server without revsere engineering anything.
6. Asset & script stealing is near impossible to prevent. Remember: anything that the client uses can be seen by exploiters, so don’t be dumb about what you let them see.
7. Exploiters CANNOT see server script code. It’s not replicated to them, so you can hide a lot of check inside them.

Section 2: different types of exploits
There’s 2 main types of ways that exploiters can mess with your game.
Firstly: client script executors. These are the most common. These are the ones that just run only on your client and have to find a way to actually exploit your game. Some examples are Synapse X, Sentinel, Tempest, JJSploit
Second: server script executors. These are often abbreviated as “SS”, short for server-side. These ones are much more rare, and typically only happen through developer stupididty. If you take a look at the front page models in the catalog, you’ll see pages full of SS’es, or “backdoors”.

Backdoors work by having a server script that listens to the client, and typically will execute commands, or even full on scripts. These are much more dangerous since they don’t have any filtering, and can easily get your game locked if you don’t prevent them, like what we saw with MeepCity and tubers93

Backdoors are usually hidden away far inside some script, so this is why you should check over every script a free model uses if you’re using FMs.
Exploiters often try to conceal their backdoors behind obfuscated code, but i’ve never seen one be actually smart about it. An easy way to tell if something is backdoored is if you see obfuscated code, or just Ctrl + F and search for “require” and it’s hidden away.

To prevent backdoors/SS’es is very simple, be sure the free models you use are clean, and the plugins you install are from actually verified developers.
Preventing client exploits is a bit more complicated, but i’ll explain how to below.

Section 3: how to write your anti-exploit
As i stated previously, Roblox puts most of the anti-script execution work on you. This is because only you understand your game’s workings best, and therefore know what a player should and shouldn’t be doing.

You want to start by relying on the server almost ENTIRELY for your anti-exploit. I’ve seen way too many people just paste an anti-RC7 script into the client, which an exploiter could literally just delete and then be off the hook.
However, if they don’t have a backdoor, an exploiter cannot delete a server-sided anti exploit.

You should start by error logging. We’ll use this to our advantage in a bit to detect a lot of poorly written scripts.
We need to connect an event to log whenever an error occurs. We then can use the message and script parameters that it passes to determine if it’s an exploit. However, we need to use this on the client because of Roblox’s replication boundary, so be sure to hide this code deep inside some large client scripts you use!

game:GetService("ScriptContext").Error:connect(function(message, stack, scriptFrom)
game:GetService("ReplicatedStorage").ConsoleError:FireServer(message, scriptFrom:GetFullName())
end)

Then you need to create a remote event in ReplicatedStorage called ConsoleError. This will now log whenever somebody errors. To prevent false positive bans, you should send the errors to some server and manually review it to determine if they should be banned.
The scriptFrom parameter uses getFullName, because most scripts that exploits execute will be parented to nil, so look out for that.

Now, let’s make a lot more scripts that exploiters use error!
This is fairly simple, but should be done before you write anything else for your game as it could break your own scripts on accident.

We’re going to randomize the names of a lot of DataModel children that scripts use. This will require a bit of work to write your game’s scripts with, but it’s worth it. (i promise)

You can put this script in either a server or client script, it doesn’t really matter. But if you put it inside the client, be sure to destroy the script after it runs.

game:GetService("Lighting").Name = tostring(math.random())
game:GetService("Workspace").Name = tostring(math.random())
game:GetService("ReplicatedStorage").Name = tostring(math.random())
game:GetService("Players").Name = tostring(math.random())
game:GetService("ReplicatedFirst").Name = tostring(math.random())

This will now randomize the names of commonly used services in exploit. However, this could easily break your entire game if your scripts aren’t correctly written.
To fix this, you should use game:GetService(“ServiceName”) instead of game.ServiceName. It’s good practice too anyway :stuck_out_tongue:

Now, if an exploiter executes a script that uses game.ServiceName, it’ll error, and you can catch it much easier!

But, what if they execute something such as:
game:GetService(“Players”).LocalPlayer.Character.Humanoid.WalkSpeed = 100

Wow, that’s not good. We shouldn’t check the walkspeed on the client, because remember that an exploiter could just delete a client sided anti-exploit.
We should write a distance checker on the server. Every time interval, we check how far a player’s moved. If they move further than they should’ve in a certain amount of time, they’re probably using some sort of speed hack.

game:GetService("Players").PlayerAdded:Connect(function(plr)
	
	spawn(function() -- open a new thread so we don't waste stuff on the playeradded connection
		repeat wait() until plr.Character  -- Give the character some time to load
		wait(2)
		
		local normalWalkspeed = 16 -- This is your player's normal walkspeed, which Roblox defaults to 16. If you use something different, be sure to change this.
		
		local lastPosition = plr.Character.HumanoidRootPart.Position
		while wait(4) do
			local newPosition = plr.Character.HumanoidRootPart.Position
			
			local distTravelled = (newPosition - lastPosition).magnitude -- See how far the player's walked.
			
			local distError = 2 -- Allow some error in the distance if just for some reason the player does this
			-- We check every 4 seconds, so the player should have AT MAX, travelled 4 * 16 studs
			
			if ((distTravelled - distError) > 4 * normalWalkspeed) then -- Check if the distance - error is further than they should've walked.
				warn(plr.Name.." is probably speed hacking, they travelled", (distTravelled - (4 * normalWalkspeed)),"studs more than they should've!")
			end
		end
	end)
end)

This should lead as an example of what you should do to prevent exploits for your games.
Some other examples are:
If you have a money remote, did the player award themself an insane amount of money?
If you have a money remote, make sure that it will only affect the player who fired it, and nobody else.
If you have a cup/item giver, make sure that a player can only give themself the item a certain amount of times per second.

This concludes my guide on how exploits work & how to prevent them. I’ll update the thread later with more examples and in-depth descriptions, but I hope this can guide you to making your Roblox game more secure against exploiters.
Remember that most people who’ll exploit your game are script kiddies who just paste stuff off of V3rmillion, and hardly know what they’re doing, so you’ll probably have an advantage.

Leave any feedback in the replies, I hope to hear how I can help you or improve this thread.

292 Likes

Couldn’t the exploiter just delete the connection or something? Plus, they could also spam the ConsoleError remote and (possibly) crash the server. Correct me if I’m wrong.

And also, changing service names is useless since they could just use GetService().

12 Likes

Yes, they could use something like
for i,v in pairs(getconnections(game:GetService"Script Context".Error)) do v.Disable() end, but most won’t so that’s why I recommend just burying it inside your largest client script.

And secondly, old scripts tend to not use GetService and most people won’t bother checking first if the names are randomized, so they might execute some old script and error.

8 Likes

There are a few issues with your last example.

Starting with the second line, spawn isn’t needed. PlayerAdded creates a new thread every time already.

Next, busy loops are bad practice. A better alternative would be plr.CharacterAdded:Wait().

After that, you define lastPosition but never actually update it. This means the player will never be able to venture far from where they spawned, whether exploiting or not.

while wait() is also bad practice. You should avoid using it in the condition because it’s not obvious right away why it works and it’s not its intended use.

There are also potential issues with plr.Character.HumanoidRootPart erroring if at any point Character is made nil (which I believe can happen briefly during death).

It’s also questionable with how you calculate distance traveled. Depending on the terrain, it’s possible for a player to move further away from where they last were last check. Think falling or sliding for example. I get that it’s not supposed to be a perfect solution but those are common enough to consider.

33 Likes

Used this method on a game I was working on for a while fully knowing it could be bypassed at any given time – and it eventually was. This guide is somewhat misleading to the extent that it encourages developers to “intecerpt” exploits before / while they’re happening. My best advice would be to secure your remotes using sanity checks on the server to validate data the client is requesting.

19 Likes

on top of this, chances are that the exploiter will be using a universal script instead of one talored to your game (if your game isn’t on the front page). this means that they won’t have any specific methods of bypassing your game’s anticheat.

client side anticheats aren’t necessarily bad, you just shouldn’t ever rely on them for obvious reasons. they should never be a replacement for serverside anticheats / remote security.

you’ll stop the exploiters that have no clue what they’re doing (a considerable portion, see the initial quote). If someone ever makes a script that deletes it, you lose nothing except an hour of your time.

10 Likes

You told us how to do simple obfuscation of the Service names then told us how to counteract it; game:GetService("ServiceName").

The best way to prevent exploiters from targeting your game is writing a good server-sided anti-cheat… That’s really all there is to it.

8 Likes

An excellent reason to not change the name of services. This really isn’t a way of preventing exploits.

17 Likes

I don’t like how you make your anti exploit on the client even when you know you have to do it on the server. It doesn’t matter how hidden it is, someone will find it.

It’s also horrible practice to shuffle the names of services, because it only makes it more inconvenient for you, and if you can just call GetService(), so can the exploiter.

3 Likes

here, let me let you all in on a idea that most developers overlook and I, due to my job in a studio have not been able to code up myself.

The reason exploiters are able to exploit the player’s character is because the character is all on the client. If you ever noticed when you yourself lag, your character is walking just fine, on your client but n for others, you’re lagging and for you, they are lagging.

This right here is the very reason exploiters can exploit their character and for instance, teleport across the map.

Now, what you’d ideally want to do if anti exploiting is so important to you and top priority. You’d recode the BACKEND of the player controller and have the server side know the position on the server and all the client ever really does is predict where the input in the server is and if that input is offset from the server, then the client automatically corrects itselfand if a player tried to alter this in any way, shape or form on the client, thanks to your server side, you can detect if that player might’ve altered this at all. This is a true fix to exploiting. Not exploit prevention, but it makes the player’s character unable to really move on the client, rather, predict where the client moved to begin with and if the server and client posiion is offset,then the client will “lag back in time” to where the server’s last known position was.

Of course, this has one major drawback, which is the deal breaker for a lot of people. Say someone had 400 ping, the game would be literally unplayable cause you’lll be lagging back in tine constantly cause the client will constantly be offset from the server to where it’ll always try and correct itself to the server’s last knownposition and repeat. That’s a majo drawback so if you are looking to run a game where lag has a huge difference, you’ll need to choose which is more important. 100% play ability or a more secure player controller.

8 Likes

Not too sure how your post is much different to the Exploiting Explained post but nonetheless, I have a few things to say:

This is an interesting discussion point already, as we truly need to define what is “as much as they can”. Roblox certainly have implemented quite a fair share of security checks, such as the memcheck, random hackflags within the regular client and even Luau has some security checks for stack checks and the like.

However, even with all that, you can execute code fairly simply without needing to bypass the majority of Roblox’s security checks.

Moving away from client-side security though, Roblox are fairly naive when it even comes to their client → server architecture. Roblox will accept a lot of information without any scrutiny or sanity checks for it so in that perspective, Roblox has a lot of room for improvement - and the Roblox client is no exception.

Ultimately, it’s always going to be a cat and mouse game and a lot of Roblox’s security issues can be attributed to technical debt.

This “shuffle” you’re referring to doesn’t have a massive impact to exploits. It’s part of the Roblox client build process and a lot of functions and whatnot that exploits need to function can be found by scanning for byte signatures within the client.

Roblox update on a weekly basis as part of the Agile development system. This is why they use fast flags and have regular meetings, it’s simply just part of the development life cycle which a lot of companies adopt in one way or another.

With regards to server-side exploits on Roblox, it’s unfair to say this is due to developer stupidity because even in the real world, exploits happen due to insecure dependencies, etc. This is why auditing your code is important, making sure to use open-source libraries and understanding what you’re working with.

Roblox is an ideal place to share these types of malicious backdoors, as a lot of the developers on the platform are not familiar with real world development standards and processes - such as code review, etc. This doesn’t make a developer on Roblox “stupid” but simply inexperienced as they’ve not had the real world experience to develop their skills. Perhaps making a code review guide for Roblox would be more beneficial than this post?

This entire section you’ve written doesn’t really cover a lot about making their scripts fail. You’ve even mentioned that this can be fixed by using GetService() which is good practice, you’re right. At the very most, it’s a very slight inconvenience to an exploiter.

If you’re looking to protect your game more seriously, you’ll focus purely on server-side security and always verifying the data that’s given from the client - even if that’s as simple as character position, since with Roblox being naive as I mentioned, it’s possible to spoof your position via Roblox’s replication packets - the RakNet bitstream is a lot of fun to play with :wink:

I’m going to let @GameAnnounce say the rest, as he’s also really experienced with all this as well :slightly_smiling_face:

16 Likes

First and foremost, not to rain on your parade, but this is not really a “complete” guide. There are many nuances to exploiting and for that matter exploit detection and deterrence.

This section is redundant, there aren’t really different ways to be able to execute scripts on Roblox - there’s no compiler on the client. It’s as simple as generate some bytecode, feed it to the deserializer, and run.

This section is almost entirely opinion and speculation, but incorrect speculation at that. The battle of developer v exploiter is one entirely of catch-up. Preventative measures will always be circumvented: where there’s a will there’s a way.

I don’t agree that Roblox doesn’t do more because they can’t due to “risk of breakage”, but instead that it’s inherently difficult to do anything to prevent or detect exploits that won’t be found out by anyone who has Roblox installed on their machine. By design, you should never trust the client (a mistake Roblox made years ago and band-aided with Filtering Enabled). You should always assume someone is going to reverse and understand your code since they have it on their machine.

The downside of adding more “security” is the degradation of performance - especially in the Luau VM if any countermeasures were placed in there. The tradeoff is not worth it if it will be circumvented within the week or two.

I’m not going to elaborate on this too much, but there’s a lot more that can be done (cough, cough Roblox please hire me).

This isn’t exactly true, Roblox added shuffling and member obfuscation a few years ago. The weekly updates to the best of my knowledge aren’t to inconvenience exploiters, but just how Roblox works because it’s expanding so quickly and there are always new features to add. New technology, new standards!

Checking for window names is redundant, and I’m not sure why it was ever done in the first place. Cheap security to stop those who are new to the field.

I don’t like these terms, they’ve been bastardised over the years. You cannot do anything against a user who can execute arbitrary code on the server, only Roblox can make a difference here by patching the method used (friendly reminder: LogService:ExecuteScript was a thing lol). However, filtering and scrutinising code you’re putting in your game is good practise. It prevents malicious code seeping into your game and potentially allowing bad actors on the client manipulating your game.

They don’t at all. Maybe I misunderstand the wording you’re using but, they can’t do anything about it and nor can you. It’s a case of writing inherently safe code where the server is authoritative.

As for the rest, I’m not going to repeat what others have said but I will say: be careful of what you’re checking and how you’re checking it. Errors, Names, and Properties are all well and find to verify when you know what the bounds are. But if you don’t…well…that’s different kettle of fish

22 Likes

Correct me if I’m wrong, but can’t you do :FireServer(“legitEvent”) and then do in the handler, :Connect(function(Event)
if Event == “legitEvent” then
—Do stuff
Else return end?

Sorry I’m on mobile so I can’t do the lua symbols.

2 Likes

The exploiter could easily use something like RemoteSpy and figure out that they just have to fire the event with “legitEvent”.

You shouldn’t try to use any of those weird key techniques, just don’t trust the client since the end user can do pretty much anything.

4 Likes

Correct me if I’m wrong,

What can Exploiters do

Exploiters can disable functions in a Client side script,

For example Player:Kick()

You can’t kick them from Client-Side, even if you fire a remote to the server to kick the exploiter, it won’t work

They could do simple stuff like Walkspeed and JumpPower, they could also Fly

Also they could Decompile Client scripts and checks how your system works on client and how to abuse remotes

Try not to include important stuff in a Client side script

Don’t make Client sided Anti Cheats because its pretty much useless and exploiters can delete them or bypass it

Remember Exploiters can control anything related to Client Side so be careful.

NetworkOwnership

Recently exploiters can do custom animations because of SimulationRadius

They can do other stuff as well, they could bring players with Tools and Network.

  • They can control Not Anchored Parts

  • They can control Hats with network

  • They can make objects with hat parts ( Delete meshes from the hats )

MaximumSimulationRadius has been patched by Roblox.

SimulationRadius Patch:
NetworkOwnership Exploit Patch

Backdoors

They mostly come from free Models

Exploiters can launch a backdoor check, if you have a backdoor in-game, the exploiter has Server side access, So please be careful with free models.

You could easily prevent most Backdoor checks by inserting a remote and if the Exploiter fires it, it kicks him or logs the Exploiter or whatever you want to do with the Exploiter, this won’t be a lot useful.

Securing your remotes

Don’t do checks on Client-side,

for Example a Buy button, instead of checking from Client-side just fire the remote and let the server check the currency that the Player has since Exploiters can manipulate Client-side.

if your remotes aren’t secure, Exploiters will 100% abuse it

12 Likes

There is a lot of truth some of what you said. The fundamental client-server model of Roblox gaming is a bit of a mess, with various compromises made to keep the game playable, but which also leaves open doors for cheating.

There indeed is an entire community dedicated to cheating, and the manpower behind cheat development is huge, like so huge that client-side anticheats are only good on a rolling basis.

There is also a lot of scripted models created and botted to the top with unscrupulous intent. These models use heavy obfuscation to hide their true intent and try to seem innocent by containing comments along the lines of “obfuscated to prevent stealing” or “this script is required by Roblox Studio. Do not delete.”

However, I have some criticisms.

Many of the front-page backdoored items go as far as hiding the keyword itself behind more obfuscation so you don’t find it with Ctrl + F, usually through a combination of getfenv and insane string manipulation. Here’s a simple example:

local a = getfenv
a()[string.reverse("eriuqer")](12345)

I don’t like being verbose with GetService throughout my game code.

For publicly-shared scripts, yes, anything can happen to service names, which is why it’s good practice, but for game code written for its respective game, I’m historically more productive using game.ServiceName whenever it’s available. Autocomplete works better with that. Coupled with the fact that many exploits, whether game-specific or in-general, account for randomized service names, it doesn’t seem worth the trade-off, at least to me.

Also, take note that randomizing Workspace is useless since there is a hard-coded property such that game.Workspace will work regardless.


What if the cheater fired the remote with normal amounts a million times, instead of once with an insane amount? Okay, let’s use a rate limiter! Then what if the cheater is firing the remote when they are in a location where they shouldn’t be awarded money at all? Okay let’s use check zones in the game world! Then what if a cheater briefly teleports into a valid zone and then fires it before your anti-teleport example code gets around to detecting them.

The point is, it would be better that only server scripts can award money. There should be no remotes for awarding yourself money whatsoever.

Except maybe in singleplayer games where cheating isn’t a problem for anybody other than the player cheating.


There’s also other things that you didn’t cover, such as undocumented replication behaviors and the ways they could be dealt with. So I would recommend you do some research to improve your coverage of the topic.


I’m like, “oooh; That’s brave to admit.”

9 Likes

It will work. the kick on the server has to work because it cant be tampered with.

1 Like

exploits have bypassed this now, thanks to this now they also have figured out to get your country and osPlatform

What he means is that firing a remote event can be intercepted by the client. So if you attempt to kick via remote it can be disabled from going out too.

5 Likes

I really don’t get the point of this post. The main server-sided checks you should have to prevent exploiting is anti-teleport (checking position), anti-speed (checking velocity), and anti-noclip (raycasts/:GetTouchingParts()). Renaming services is a waste of time. The only thing you should worry about regarding client-sided anti-cheat is a quick LocalScript that detects changes in the Humanoid (to prevent the script kiddies). Remotes are easy to protect. Just make sure that the player is actually allowed to fire it using some if then checks. This isn’t really a “complete guide” if the only server-sided check covered is position checks. You also need to add a lot of checks to your script to make sure it doesn’t error and let exploiters roam freely. You have to check to make sure the character exists before it is referenced, and then make sure the HumanoidRootPart exists using if :FindFirstChild().

3 Likes