How to stop players dropping paid tools for others

A paid tool that can be dropped can be handed to anyone, again and again. How to lock paid tools to the player who bought them.

warningLuaudit check: droppable-paid-tools

What it looks like

A game pass or developer product gives the player a Tool, such as a VIP sword, a speed coil or a gravity coil. The tool's CanBeDropped is true, so the buyer can drop it into the world, where anyone can walk over it and take it. Often the server gives a new copy on every respawn, so one buyer can hand out as many as they like.

Luau
player.CharacterAdded:Connect(function()
	if ownsPass(player) then
		ServerStorage.GravityCoil:Clone().Parent = player.Backpack
	end
end)

How to detect it

  • Find every tool given because of a purchase: game pass checks, ProcessReceipt grants and paid crates.
  • Roblox's reference for Tool.CanBeDropped says that when it's true, pressing Backspace parents the tool to Workspace and removes it from the player's Backpack. Roblox's tools guide describes tools placed in the world as pickups that players collect.
  • Check CanBeDropped on the tool in the place or project file, and anywhere a script sets it. Report paid tools that can be dropped.
  • Check how copies are handed out. A tool given again whenever the backpack doesn't have one, such as on each respawn, can be dropped and replaced over and over.
  • Dropping a paid item for another player is also trading it. If the game checks PolicyService IsPaidItemTradingAllowed for trades and gifts, dropping goes around that check. See Paid items traded with no policy check.

What an exploiter could do

They buy the pass once, or get one from a friend who did, then drop the tool, respawn, and drop it again until everyone in the server has one. Players get paid perks for free, sales fall, and paid weapons end up in the hands of players the game never meant to have them.

How to fix it

  • Set CanBeDropped to false on every paid tool, in the saved tool and again when cloning it.
  • Check on the server that a player using a paid tool owns it, for example in the tool's server script, so a copy that does change hands doesn't work.
  • If dropping is part of the game, only let free tools be dropped.

Vulnerable example

Vulnerable
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")

local coilPass = 1234567

Players.PlayerAdded:Connect(function(player)
	local ok, owns = pcall(MarketplaceService.UserOwnsGamePassAsync, MarketplaceService, player.UserId, coilPass)
	if not ok or not owns then
		return
	end
	player.CharacterAdded:Connect(function()
		local backpack = player:WaitForChild("Backpack")
		local coil = ServerStorage.GravityCoil:Clone()
		coil.Parent = backpack
	end)
end)

Fixed example

Fixed
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")

local coilPass = 1234567

Players.PlayerAdded:Connect(function(player)
	local ok, owns = pcall(MarketplaceService.UserOwnsGamePassAsync, MarketplaceService, player.UserId, coilPass)
	if not ok or not owns then
		return
	end
	player.CharacterAdded:Connect(function()
		local backpack = player:WaitForChild("Backpack")
		local coil = ServerStorage.GravityCoil:Clone() :: Tool
		coil.CanBeDropped = false
		coil:SetAttribute("Owner", player.UserId)
		coil.Parent = backpack
	end)
end)

The coil's own server script checks that the character holding it belongs to the player in its Owner attribute before it does anything.

Common false positives

  • Free tools, such as a starting sword or a tool picked up from the map, can be dropped if the game wants that.
  • Paid tools with CanBeDropped set to false, like the fixed example, are fine.
  • Gift and trade systems for paid tools are judged with Paid items traded with no policy check.