diff --git a/CHANGELOG.md b/CHANGELOG.md index d954e7b1..eb95747c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Removed `TSPlayer.InitSpawn` field. (@DankRank) * `OnPlayerSpawn`'s player ID field is now `PlayerId`. (@DankRank) * `Utils.TryParseTime` can now take spaces (e.g., `3d 5h 2m 3s`) (@QuiCM) +* Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index f1ce0005..223de05b 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -64,6 +64,7 @@ namespace TShockAPI GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer; GetDataHandlers.TileEdit += OnTileEdit; GetDataHandlers.MassWireOperation += OnMassWireOperation; + GetDataHandlers.PortalTeleport += OnPlayerPortalTeleport; } internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args) @@ -1721,6 +1722,34 @@ namespace TShockAPI args.Handled = true; } + internal void OnPlayerPortalTeleport(object sender, GetDataHandlers.TeleportThroughPortalEventArgs args) + { + //Packet 96 (player teleport through portal) has no validation on whether or not the player id provided + //belongs to the player who sent the packet. + if (args.Player.Index != args.TargetPlayerIndex) + { + //If the player who sent the packet is not the player being teleported, cancel this packet + args.Player.Disable("Malicious portal attempt.", DisableFlags.WriteToLogAndConsole); //Todo: this message is not particularly clear - suggestions wanted + args.Handled = true; + return; + } + + //Generic bounds checking, though I'm not sure if anyone would willingly hack themselves outside the map? + if (args.NewPosition.X > Main.maxTilesX || args.NewPosition.X < 0 + || args.NewPosition.Y > Main.maxTilesY || args.NewPosition.Y < 0) + { + args.Handled = true; + return; + } + + //May as well reject teleport attempts if the player is being throttled + if (args.Player.IsBeingDisabled() || args.Player.IsBouncerThrottled()) + { + args.Handled = true; + return; + } + } + /// /// Tile IDs that can be oriented: /// Cannon, @@ -1762,4 +1791,4 @@ namespace TShockAPI }; } -} \ No newline at end of file +} diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index d8c47b3b..c59ce1a7 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1488,7 +1488,8 @@ namespace TShockAPI { PacketTypes.PlayerHealOther, HandleHealOther }, { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, - { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 } + { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, + { PacketTypes.PlayerTeleportPortal, HandlePlayerPortalTeleport } }; } @@ -1510,6 +1511,67 @@ namespace TShockAPI return false; } + /// The event args object for the PortalTeleport event + public class TeleportThroughPortalEventArgs : GetDataHandledEventArgs + { + /// The Terraria player index of the target player + public byte TargetPlayerIndex { get; set; } + + /// + /// The position the target player will be at after going through the portal + /// + public Vector2 NewPosition { get; set; } + + /// + /// The velocity the target player will have after going through the portal + /// + public Vector2 NewVelocity { get; set; } + + /// + /// Index of the portal's color (for use with ) + /// + public int PortalColorIndex { get; set; } + } + + /// When a player passes through a portal + public static HandlerList PortalTeleport = new HandlerList(); + + private static bool OnPlayerTeleportThroughPortal(TSPlayer sender, byte targetPlayerIndex, MemoryStream data, Vector2 position, Vector2 velocity, int colorIndex) + { + TeleportThroughPortalEventArgs args = new TeleportThroughPortalEventArgs + { + TargetPlayerIndex = targetPlayerIndex, + Data = data, + Player = sender, + NewPosition = position, + NewVelocity = velocity, + PortalColorIndex = colorIndex + }; + + PortalTeleport.Invoke(null, args); + + return args.Handled; + } + + private static bool HandlePlayerPortalTeleport(GetDataHandlerArgs args) + { + byte plr = args.Data.ReadInt8(); + short portalColorIndex = args.Data.ReadInt16(); + float newPositionX = args.Data.ReadSingle(); + float newPositionY = args.Data.ReadSingle(); + float newVelocityX = args.Data.ReadSingle(); + float newVelocityY = args.Data.ReadSingle(); + + return OnPlayerTeleportThroughPortal( + args.Player, + plr, + args.Data, + new Vector2(newPositionX, newPositionY), + new Vector2(newVelocityX, newVelocityY), + portalColorIndex + ); + } + private static bool HandleHealOther(GetDataHandlerArgs args) { byte plr = args.Data.ReadInt8();