Anti-Exploitation Script: Need feedback on what to add or change

Anti-Exploit (Server Script)
--- Cardinal - The program that overlooked SAO

--- Cardinal Set Up
local CardinalSetUp = {}
CardinalSetUp[1] = script:WaitForChild("SpeedLimitEnabled")
CardinalSetUp[2] = script:WaitForChild("SpeedLimit")

--- Create Cardinal Event
local CardinalEvent = Instance.new("RemoteEvent")
CardinalEvent.Parent = game.ReplicatedStorage
CardinalEvent.Name = "CardinalEvent"

--- Modules
local DSS3Module = require(game.Workspace.Modules.DSS3Module)
local TimeAndDateMod = require(game.Workspace.Modules.TimeAndDateMod)
local StatsModule = require(game.Workspace.Modules.StatsModule)

function BanPlayer(Player,Duration,Reason)
	if not Reason then
		Reason = "No Reason Provided"
	end
	DSS3Module.SetData("CardinalBans",tostring(Player.UserId), tostring(Duration) .. ";Split; " .. Reason)
end

game.Players.PlayerAdded:Connect(function(NewPlayer)
	local Ban = {}
	Ban[1] = DSS3Module.GetData("CardinalBans",tostring(NewPlayer.UserId))
	if Ban[1] == nil then --or NewPlayer.UserId == 58666138
		-- Player Is Not Banned
	else
		if string.split(Ban[1],";Split;")[1] == "inf" then
			NewPlayer:Kick("Banned For Eternity For:" .. string.split(Ban[1],";Split;")[2])
		else
			if tonumber(string.split(Ban[1],";Split;")[1]) - os.time() > 0 then
				NewPlayer:Kick("Time Left On Ban: " .. tostring((tonumber(string.split(Ban[1],";Split;")[1]) - os.time()) / 60) .. " Minutes")
			else
				DSS3Module.RemoveData("CardinalBans",tostring(NewPlayer.UserId))
			end
		end
	end
	local SpeedCoroutine = coroutine.create(function()
		local PreviousPosition = {}
		while CardinalSetUp[1].Value == true do -- Alternative to using a coroutine (Can be immediatly stopped)
			if game:GetService("Players"):FindFirstChild(NewPlayer.Name) then
				wait(1)
				if NewPlayer.Character then
					if PreviousPosition[1] then
						if (NewPlayer.Character.Torso.CFrame.Position - PreviousPosition[1].Position).Magnitude > CardinalSetUp[2].Value then
							print("Speed Triggered")
							if NewPlayer:FindFirstChild("Teleporting") then
								wait(1)
								PreviousPosition[1] = NewPlayer.Character:FindFirstChild("Humanoid").CFrame
							else
								NewPlayer.Character:FindFirstChild("Torso").CFrame = PreviousPosition[1]
							end
						
						end
					end
				PreviousPosition[1] = NewPlayer.Character:FindFirstChild("Torso").CFrame
				end
			else
				break -- Player Has left the game
			end
			wait(1) -- Prevents lag and is set to 1 to get distance per second
		end
	end)
	coroutine.resume(SpeedCoroutine)
end)


--- Cardinal Event
CardinalEvent.OnServerEvent:Connect(function(Player, Data)
	local EventProccessed = false
	----- Change EventProccessed upon event completion
	----- Pcall to prevent bans on errors
	local Success, msg = pcall(function()
		
		---- Blank fires result in banning the player
		if Data == "" then
			BanPlayer(Player,"inf","You got banned by the Cardinal System for one of the following reasons: Firing Remotes, Unautharised Module Access or Workspace Exploitation")
			EventProccessed = true
		end
		
		
		
		---- Cardinal Admin Use
		if Player.UserId == 58666138 then 
			if Data[1] == "Ban" then
				if Data[2] == nil then
					Data[2] = "inf"
				end
				BanPlayer(Data[4],Data[2],Data[3])
				EventProccessed = true
			end
		end
		
		
	end)
	------ 
	if Success == nil and msg then
		EventProccessed = true
		print("Server Sided Error, printing diagnostics. \n " .. msg)
	end
	------
	if EventProccessed == false then
		-- Ban Player
		BanPlayer(Player,"inf","You got banned by the Cardinal System for one of the following reasons: Firing Remotes, Unautharised Module Access or Workspace Exploitation")
	end
end)

The Idea of this is to prevent people going to fast or teleporting by any means. This also handles bans and I hope also bans players trying to access my RemoteEvents. And yes I named it Cardinal from SAO

DSS3 (Module Script)
local DSS3 = {}

local DataStoreService = game:GetService("DataStoreService")

local RunService = game:GetService("RunService")


---- Cardinal Event (Big Brain Game Security)
local CardinalEvent = game.ReplicatedStorage:WaitForChild("CardinalEvent")
---- Ban If Running On Client
if RunService:IsClient() then
	CardinalEvent:FireServer() -- Blank Remote Fire Resulting In A Ban
end

----------------------------------------- // Base Functions (Save, Load, Update, Remove, CheckForData)

function DSS3.SetData(Store,Player,Data)
	for i = 1,10 do
		local S, R = pcall(function()
			local NewStore = DataStoreService:GetDataStore(Store)
			NewStore:SetAsync(Player,Data)
		end)
		if S then
			print("Set Data")
			break
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end
end

function DSS3.UpData(Store,Player,Data)
	for i = 1,10 do
		local S, R = pcall(function()
			local NewStore = DataStoreService:GetDataStore(Store)
			NewStore:UpdateAsync(Player,Data)
		end)
		if S then
			break
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end

end

function DSS3.GetData(Store,Player)
	local Result
	for i = 1,10 do
		local S, R = pcall(function()
			local NewStore = DataStoreService:GetDataStore(Store)
			Result = NewStore:GetAsync(Player)

		end)
		if S then
			return Result
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end

end

function DSS3.RemoveData(Store,Player)
	for i = 1,10 do
		local S, R = pcall(function()
			local NewStore = DataStoreService:GetDataStore(Store)
			NewStore:RemoveAsync(Player)
		end)
		if S then
			print("Removed Data")
			break
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end
end

function DSS3.CheckForExistingData(Store,Player)
	local Data
	for i = 1,#3 do
		local S, R = pcall(function()
			Data = DSS3.GetData(Store,Player)
			if Data == nil then
				local Result = false
				return Result
			else

			end
		end)

		wait(1)
		if S then
			break
		end
	end
	if Data == nil then
		local Result = true
		return Result
	end
end
----------------------------------------- // ArraySaving, ArrayLoading
function DSS3.SaveArray(Store,Player,Data)
	local Combined = table.concat(Data,";Split;")
	DSS3.SetData(Store,Player,Data)
end

function DSS3.LoadArray(Store,Player)
	return string.split(DSS3.GetData(Store,Player),";Split;")
end

----------------------------------------- // DataStore Listing (For BackUps or Databases)
function DSS3.CreateListing(Store,Data)
	for i = 1,10 do
		local S, R = pcall(function()
			local EntryCount = DSS3.GetData(Store .. "EntryCount","EntryCount")
			if tonumber(EntryCount) == nil then
				EntryCount = 0
			end
			EntryCount = tostring(tonumber(EntryCount) + 1)
			DSS3.SetData(Store,EntryCount,Data)
			DSS3.SetData(Store .. "EntryCount","EntryCount",EntryCount)

		end)
		if S then
			break
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end

end

function DSS3.GetListings(Store)
	local StoreNames = {}
	for i = 1,10 do
		local S, R = pcall(function()
			local EntryCount = DSS3.GetData(Store .. "EntryCount","EntryCount")
			EntryCount = tonumber(EntryCount)
			StoreNames = {}
			for i = 1, EntryCount do
				local Name = tostring(i)
				table.insert(StoreNames,Name)
			end
		end)
		if S then
			return StoreNames
		else
			print("DSS Error:")
			print(R)
			wait(2)
		end
	end


end

function DSS3.RemoveListing(Store,Number)
	DSS3.SetData(Store,Number,nil)
end

function DSS3.GetEntryCount(Store)
	local EntryCount = DSS3.GetData(Store .. "EntryCount","EntryCount")
	EntryCount = tonumber(EntryCount)
	return EntryCount
end


----------------------------------------- // Save Or Load Models (Via My Modules)
local PartSavingMod = require(script.PartSavingModule)
function DSS3.SaveModel(Model)
	return PartSavingMod.GetModelProperties(Model)
end

local PartLoadMod = require(script.PartLoadingModule)
function DSS3.LoadModel(Model)
	return PartLoadMod.LoadModel(Model)
end


----------------------------------------- // Time And Date
local TimeAndDateMod = require(script.TimeAndDateMod)

function DSS3.GetSecondsPassed()
	return TimeAndDateMod.SecondTime()
end

----------------------------------------- // GetBackup Data
function DSS3.GetBackup(Store)
	print("Attempting To Get Backup Data from" .. Store)
	local Backups = DSS3.GetListings(Store)
	local Data = {}
	Data[1] = #Backups
	while not Data[3] == nil do
		Data[2] = DSS3.GetData(Backups[Data[1]])
		if Data[2] == nil then
			-- Data May Be Corrupted
			Data[1] = Data[1] - 1
		else
			Data[3] = Data[2]
		end
	end
	
	if Data[3] then
		return Data[3]
	else
		print("No Usable Backups Found")
	end
end

----------------------------------------- // Returns The DSS3 Module
return DSS3

DSS3 is my own DataStoreScript that BacksUp data and allows me to store a mass of data that is organised by the date it was saved. Runs as a module and if anyone tried to run it on the client it sends a black fire to Cardinal resulting in a instant ban. Can also turn workspace models into text so I can save them.

5 Likes

Here I will demonstrate a possible bypass used for the client detection methods and down below I will give some more information on the topic

local CardinalEvent = game:GetService("ReplicatedStorage"):WaitForChild("CardinalEvent")

local FireServer
FireServer = hookfunction(Instance.new("RemoteEvent").FireServer, function(self,...)
    if self == CardinalEvent then
        return
    end
    return FireServer(self,...)
end)

local IsClient
IsClient = hookfunction(game:GetService("RunService").IsClient, newcclosure(...)
    if getcallingscript() and getcallingscript():IsA("ModuleScript") then
        return false
    end
    return IsClient(...)
end)

Don’t ever trust the client with key information such as a module script that has no use being on the client, for the rest the anti-teleport & keeping the humanoid speed realistic seems like a proper approach but of course, this can use some improvements,

Humanoid.Running replicates to the server :wink:

For the rest, good job by not putting the anti-cheat on the client!

What would this put into the server??

My modules instantly ban if they are being run on the client and and blank fires also result in a ban so I dont get how its bypassing it.

By what I understand self refers to the parameters in my functions but firing with nothing as parameters should result in an instant ban.

1 Like

as for Fire Server bypass it seems to try to get the parameters and compare them to Cardinal event which will work of course your script can be easily bypassed

as for IsClient umm the module script is in serverstorage i put most of MS in server storage
and the remaing ones are not that damaging just some info

but why is there some event called cardinalEvent

does this stops teleporting scripts in server the code looks super invasive and you got to edit it to use it in your game

The client simply has too much power, most if not all checks on the client can and will be bypassed eventually. Try focusing more on your server end of things.

Greetings. Your approach to use a server-sided script to handle the detection of cheats is excellent.
Server-sided anti-cheats have the advantage of being unable to be tampered with, read or generally bypassed, unlike client-sided ones (which are a waste of time). You could tailor it to your game more by introducing checks for flying, floating, etc. (Potentially achieved with Region3 or Raycasting, if you wish).

What I do not understand, however, is your use of the security on the ModuleScript. Simply put, it is not necessary. ModuleScripts technically run on whatever script that required it. It would run well if it was required on a server script, as it would have the environment of a server. Clients, however, cannot access DataStoreService. If one was to require your script even through an exploit, it would do them no good. There is no need to introduce a check on whether the ModuleScript is being ran by a server script or a local one, as ModuleScripts are merely containers for code. If you wish that it was not accessible by clients, you can always place your modules in ServerScriptService.

image

Hope this helps.

Your quite right about the module checks not really being needed, its just an extra thing I have because if I know the client should never access it and it gets accessed it means that player is definatly exploiting.

It wont catch out any decent exploiters but it would certainly catch a few who just try using random modules and functions.

As for the anti fly ill have to add that as it is definatly a good idea, ill prob check the player rotation and also cast a ray for this.

While I think that using such a method as a honeypot is a bit underwhelming and borderline unnecessary, I get what you’re saying. As for secondary checks to do on the server, I would not recommend rotation or casting rays (generally) unless you know what you’re doing. It is expected physics behavior that the player can sometimes trip or be standing on a ledge, where raycasting and rotation detection is ineffective. (Raycasting can still, however, be used for other checks such as detecting noclip).

If you are attempting to check whether the player is standing on a surface, the option that I would personally recommend you is:
https://developer.roblox.com/en-us/api-reference/datatype/Region3
Region3 is a data type that can potentially be used to create a 3D space around the player, in which you could check if there are any valid parts that the player could be contacting (indicating that they are standing on a surface). There are helper modules for this, such as Rotated Region 3 Module
Simply put, you can create a region under the player to check whether they are standing on anything, and attempt to justify it if they are not (in case of jumping, falling, etc). And relying on graceful handling if they are not, to alleviate the consequences of false positives in case your system is not perfect.

You can also always use the physics Touched event.

Cheers.

1 Like

Ill definatly read through the scripts, I like doing everything myself cos I learn from it and can get it to work for the way I want to use it.

Thanks

You can’t detect empty returns as it’s the same what FireServer does on it’s own it just never fires the remote to the server and instead redirects it back just like normal and for the module I simply bypassed the detection method you used by hooking RunSerivce.IsClient and always returning false if a module script calls it

This script does not require the modulescript but it just bypasses all the ban methods you have in place

I simply bypassed what he put down, he stores the ban remote within replicated storage and I check when FireServer is called then I redirect it without ever the remote getting to the server as for IsClient the module script seems to be located on the client as else there should be no reason to check IsClient in the first place

Thanks for explaining but UNO REVERSE CODE

I simply bypassed what you did, i stored the ban remote in ServerStorage(Client can’t use it)
Also i stored the module in ServerStorage because you seem trying to stop server calling it by blocking it

local IsClient
IsClient = hookfunction(game:GetService("RunService").IsClient, newcclosure(...)
    if getcallingscript() and getcallingscript():IsA("ModuleScript") then
        return false
    end
    return IsClient(...)
end)

Respect+++

I have Fixed @Skylersnips Bypass please try to keep all resources out of client’s Evil hand
the best place to store is ServerScriptService And ServerStorage
100% Server Only allowed
please check the reply above me it has Some Instructions to fix current problems

I don’t believe you fully understand what you are doing. An exploiter can easily decompile your ModuleScript and modify it as they like, and then use it. Which still ignores the point that such security measures protect nothing. ModuleScripts modified on the client do not replicate to the server, they also do not have access to server-only resources, such as DataStoreService.

Storing RemoteEvents or RemoteFunctions in ServerStorage of ServerScriptStorage is an utterly nonsensical act, as the client cannot access these locations neither are they replicated to it. The event in use by OP is supposedly designed to be called by the client (which cannot occur if the event is in a non-replicating location). The purpose of RemoteEvents and RemoteFunctions, as the names imply, is for remote communication. If server-server or client-client communication is needed, BindableEvents and BindableFunctions fit the role. It is also bad practice to store scripts inside of ServerStorage, as ServerScriptService is meant for that role.

All of this is purely utter nonsense. Not sure why ones are fighting over being able to “take hold of” ModuleScripts with exploits while others are trying to prevent that. This achieves nothing, exploits win at the end of the day as they are always on top in the client and there is absolutely nothing you can do. Thankfully in the case of ModuleScripts there is nothing that they can achieve by being able to require this module, as they cannot use it to save or alter data in the datastore regardless.

2 Likes

I rather do not use the bulk of my anti scripts to communicate to the client i will choose to make a server script which handles it anyway

–The point
umm didn’t i told you that I rather edit the entire script and keep it out of client range, remote events work in server storage, i will put modules in server storage and the scripts in serverscriptservice

of course i know the client always wins and i can only put pillows on his path so he cant achieve it easily

OP requests for a proper review and how to improve his practices, not your “put pillows in the way” bloated & waste of time method. Both Skyler and you fail to answer his request in any meaningful way. Your response was to introduce exploit-specific code to the module and to “keep all resources out of the client’s Evil hand”, ignoring the obvious truth that is the client’s inability to achieve any game-breaking or even game-altering behavior through being able to require a modulescript in use by the server. The only factually true statement you’ve made was that it should be kept in ServerScriptService, you however, failed to mention the obsolescence and impracticality of OP’s security decision.

Then there would be no use for the remote as remotes are used for client-server communication, and I didn’t stop the server from calling but instead, I made it useable for the client even though the client has no use for it

I think you fail to realize that I gave him a example of how stupid it is to trust the client with anything & I did not bypass the ban remote to start a fight it was simply to demostrate a example, you can go through extreme lengths to protect the client if really needed as there are exploit vulnerabilities u can abuse but almost all anti-exploit should be made on the server, but as we all know network ownership and remotes are the jackpot for exploiters as they can abuse them freely if they do not have any protection that is why it’s always useful to at least somewhat protect them from the more inexperienced exploiters and add as much logic checks on the server as possible

2 Likes

That’s a great reply, and I don’t have much against that, you should however add some context for OP. Providing a bypass for his current method does not inform him about the obsolescence of his measures, and the course that is needed for him to correct his practices.