Security report (responsible disclosure)
PlayerState.tunnel:update() merges arbitrary client tables into persisted character state
Affected: vrp/modules/player_state.lua:243-252; persistence at User.lua:46; replay logic player_state.lua:198-215
function PlayerState.tunnel:update(state)
local user = vRP.users_by_source[source]
if user and user:isReady() then
for k,v in pairs(state) do
user.cdata.state[k] = v -- ← no key whitelist
end
vRP:triggerEvent("playerStateUpdate", user, state)
end
end
No whitelist on keys. cdata.state is serialized to the database on save and replayed on every spawn: weapons are granted from stored state (:207), health/armour restored (:209-215), position teleports applied (:198-200).
Exploit chain
Client sends:
update({ weapons = { WEAPON_RAILGUN = { ammo = 250 } }, health = 200, custom_key = anything })
→ state persists across deaths and relogs; the "clear weapons/health on death" logic (:222-228) is bypassed because the client re-pushes state after each wipe. Beyond the direct weapon/health persistence abuse, this is uncontrolled key injection into a persisted blob that other modules read (in_owned_vehicle, customization), making future consumers attacker-configurable.
Suggested fix
Whitelist exactly the fields clients are meant to report (weapons, health, armour, position, heading, customization), type/range-check values, and treat weapons as non-persistable across death.
Security report (responsible disclosure)
PlayerState.tunnel:update()merges arbitrary client tables into persisted character stateAffected:
vrp/modules/player_state.lua:243-252; persistence atUser.lua:46; replay logicplayer_state.lua:198-215No whitelist on keys.
cdata.stateis serialized to the database on save and replayed on every spawn: weapons are granted from stored state (:207), health/armour restored (:209-215), position teleports applied (:198-200).Exploit chain
Client sends:
→ state persists across deaths and relogs; the "clear weapons/health on death" logic (
:222-228) is bypassed because the client re-pushes state after each wipe. Beyond the direct weapon/health persistence abuse, this is uncontrolled key injection into a persisted blob that other modules read (in_owned_vehicle,customization), making future consumers attacker-configurable.Suggested fix
Whitelist exactly the fields clients are meant to report (
weapons,health,armour,position,heading,customization), type/range-check values, and treat weapons as non-persistable across death.