We have a bug in Sumo Simulator where some calls to MarketplaceService:PromptProductPurchase throws the error “do not run what you just ran (vulnerable function)”.
We added debug logging to ensure that the player and the Developer Product ID to the call is correct,
The products are not “third party products” and “third party sales” is enabled either way.
The frequency of this happening in Sumo Simulator is about 2 times per minute and it is happening for multiple users. Purchases seems to work for a lot of users though (including us devs when we test)
-- Relevant code
local success, err = pcall(function()
MarketplaceService:PromptProductPurchase(player, p.Id)
end)
if not success then
local message = ("MarketplaceService:PromptProductPurchase failed, product: %s, player: %s, productId: %s, err: %s"):format(
tostring(name),
tostring(player),
tostring(p.Id),
tostring(err)
)
error(message)
end
Example output in the Error Report (correct player names hidden):
Expected behavior
I expect the call to MarketplaceService:PromptProductPurchase to work as intended.
I have a feeling these errors come from exploit scripts… lots of exploit softwares have these kinds of safeguards in place to prevent their users from running malicious scripts that steal their Robux through these kinds of functions hopefully an engineer can confirm
3 Likes
It would be nice to get that confirmed, then we can just suppress these errors and continue with our day.
Since it is related to purchases it would feel a bit better to understand if its something we can fix or not.
I wonder if it would work to bypass these exploit script engine blocks by calling PromptPurchase on the server if it fails suspiciously on the client.
1 Like
Based on the error format and wording it’s more than likely an exploit one. Does a (temporary) change to how you handle product purchases still make the errors show up?
I am inclined to agree.
Did change it up a bit when I implemented the debug information.
The prompt code is called from multiple different locations depending on the purchase type with the same result.
Will do a server side test tomorrow and see what that gives, because I want people to get purchase prompts even if they use exploits
1 Like
I just tested to fall back to the server calling PromptProductPurchase if it fails on the client, added some temporary logging to see if it works and it indicates that it actually shows the purchase to the exploiter.
Table containing suspicious errors:
local exploitEngineErrors = {
"do not run what you just ran (vulnerable function)."
}
Except from function module.PromptBuyProduct(player, name, buyCompletedCallback)
local success, err = pcall(function()
MarketplaceService:PromptProductPurchase(player, p.Id)
end)
if not success then
local isSuspiciousError = misc.TableContains(exploitEngineErrors, err)
if RunService:IsClient() then
if isSuspiciousError then
clientTriggeredSuspiciousError = true
warn("MarketplaceService:PromptProductPurchase threw suspicious error on client, attempting to run on server")
else
warn("MarketplaceService:PromptProductPurchase threw unknown error on client, attempting to run on server, err: " .. tostring(err))
end
promptBuyProductRemote:FireServer(name)
return
else
local message = ("MarketplaceService:PromptProductPurchase failed on server. product: %s, productId: %s, err: %s"):format(tostring(name), tostring(p.Id), tostring(err))
error(message)
end
end
clientTriggeredSuspiciousError = false
The logging part on the client:
MarketplaceService.PromptProductPurchaseFinished:Connect(function(userId, productId, isPurchased)
if clientTriggeredSuspiciousError then
warn("MarketplaceService:PromptProductPurchaseFinished after suspicious error, isPurchased: " .. tostring(isPurchased))
end
end)
Log output from Error Reports:
1 Like
Interesting. Keep in mind though, that exploiters can fire MarketplaceService.PromptProductPurchaseFinished
at their own will, however many times they like (albeit the data returned by the function ex. isPurchased will still be correct). This is not very known about these MPS events.
2 Likes
I am aware of that - ProcessReceipt is the secure one.
But this is mostly for logging to understand if my workaround seems to work.
1 Like