So i recently had an exploit be used at my game (ARC-74) that “teleported” some of my SCPs/Unanchored parts to the player. I spent about 1 hour fixing, including a bypass for “nokick” exploit.
How the exploit works:
Essentially what the exploit does is it gets all unanchored parts in the game and then “attaches” them to the character via a bodyposition, and exploits MSR/SR to get all the unanchored parts on the map. It then “pulls” them towards the player, thus causing chaos.
When banning the person, because my map is “in doors” the SCPs are not able to return to the correct positions, as they would get stuck in the walls/bounce off them. This led to this happening: See Image
How the anti-exploit script works:
My solution, was to whenever it detected this bodyposition being made it would fire a remote event that reported this to a webhook & kicked them, then added them to a ban datastore.
This bypassed the anti-kick by making the kick happen server-sided instead of client, and of course will most likely permanently patch this due to the fact that the anti-kick script cannot just stop all remotes on the client being fired, as that would break most games.
Now you don’t need to report it, however I was purely using the webhook to collect data on exploiters and also in case they attempted to appeal to me.
There is a read me in the folder once you insert it on how to set everything up.
Hopefully this helps people protect their games better from this type of exploit.
Edit: Yes this is not the most efficient way to do it, but it works. This also contains a method for bypassing exploiters “anti-kick” exploit.
This honestly does not look as a permanent fix since exploiters could easily delete the LocalScript that fires the ban event if they find out that it is a honeypot. It should check the added childs from the server by connecting this function to every new character in-game so it cant be deleted or modified by an exploiter.
This solution is very bad because exploiters can literally just stop the remote from being fired and there is a much easier way to solve it.
The reason why your Unachored parts are able to be controlled by certain players is probably that you didn’t set the network ownership of the parts to the server.
To do so all the have to do is use the SetNetworkOwner method and just set all NPC’s parts to the server
Even if they delete the local script, you can hide the function in another script or name the script to something that looks like its required for the game to run.
This seems to rely on the fact that exploiters will use BodyPosition while doing this?
Why would anyone use BodyPosition when teleporting items around?
As long as someone sets Part’s CFrame, your “patch” will not work.
Also, someone already said this, but setting network ownership to nil makes it so part’s network ownership is always on server as opposed to regular behavior which automatically assigns based on distance etc.
addcmd('tpunanchored',{'tpua'},function(args, speaker)
if sethidden then
local players = getPlayer(args[1], speaker)
for i,v in pairs(players) do
local Forces = {}
for _,part in pairs(workspace:GetDescendants()) do
if Players[v].Character:FindFirstChild('Head') and part:IsA("BasePart" or "UnionOperation" or "Model") and part.Anchored == false and not part:IsDescendantOf(speaker.Character) and part.Name == "Torso" == false and part.Name == "Head" == false and part.Name == "Right Arm" == false and part.Name == "Left Arm" == false and part.Name == "Right Leg" == false and part.Name == "Left Leg" == false and part.Name == "HumanoidRootPart" == false then
for i,c in pairs(part:GetChildren()) do
if c:IsA("BodyPosition") or c:IsA("BodyGyro") then
c:Destroy()
end
end
local ForceInstance = Instance.new("BodyPosition")
ForceInstance.Parent = part
ForceInstance.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
table.insert(Forces, ForceInstance)
if not table.find(frozenParts,part) then
table.insert(frozenParts,part)
end
end
end
if not simRadius then
execCmd('simulationradius')
end
for i,c in pairs(Forces) do
c.Position = Players[v].Character.Head.Position
end
end
else
notify('Incompatible Exploit','Your exploit does not support this command (missing sethiddenproperty)')
end end)
That’s the exploit. It uses bodypositions to pull unachored parts towards the player.
The script still contains a bypass for “antikick” scripts + a “honeypot” for exploiters.
This is not a good way to patch this method. You never trust the client. An exploiter can just stop the remoteevent from firing. The best way to fix this is to weld an unanchored part 10k studs above the map, then doing a check every 10 seconds or so to check if the network owner has changed, if it’s a player, kick them.
I remember having a problem with this exploit myself.
I solved it by running a check on the server every second or so, which creates a sum total of all player network owned objects. It then creates an individual tally for each player within a lookup table representing how many parts each player had network ownership over. If any one player network owned an overwhelming majority of the parts (in my case it was something like 90%), I kicked them.
However, I am aware that this would not work for all types of games, as having one player control a majority of physically simulated objects might be intended in some cases.
It worked for my use case though. Don’t rely on client - side anti cheats as the client can literally manipulate every function / security check which you employ, no matter how much you try to put them off messing with your client code.