Merge pull request from GHSA-q776-cv3j-4q6m

Patch SendTileRectangle mass-griefing exploit
This commit is contained in:
Lucas Nicodemus 2021-05-22 10:55:31 -07:00 committed by GitHub
commit 914cdb1046
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 57 deletions

View file

@ -13,6 +13,8 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change.
## Upcoming changes
## TShock 4.5.3
* Added permissions for using Teleportation Potions, Magic Conch, and Demon Conch. (@drunderscore)
* `tshock.tp.tppotion`, `tshock.tp.magicconch`, and `tshock.tp.demonconch` respectively.
* Updated HealOtherPlayer damage check to make more sense by respecting `ignoredamagecap` permission. (@moisterrific)
@ -23,6 +25,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* The buff commands now support `-1` as a time option to set buffs that last 415 days (the maximum buff time the game supports). (@moisterrific)
* TShock defaults to saving backups every 10 minutes, and defaults to keeping backups for 4 hours. (@hakusaro)
* Updated SSC bypass messaging. Now, when you connect, you're told if you're bypassing SSC. Console logging has been improved to warn when players are not being saved due to the bypass SSC permission. To turn this warning off, change `WarnPlayersAboutBypassPermission` to `false` in the `sscconfig.json` file. (@hakusaro)
* Fix oversight & exploit allowing specially crafted SendTileRectangle packets to perform large-scale world griefing. In addition, `NetTile.Slope` is now the native value (byte), and accessor methods `Slope1`, `Slope2`, and `Slope3` can be used to get the old style of values out. `HalfBrick` and `Actuator` were removed from `NetTile` because these were initialized to zero and never changed or used. (@bartico6)
## TShock 4.5.2
* Added preliminary support for Terraria 1.4.2.2. (@hakusaro)

View file

@ -1,12 +1,15 @@
using OTAPI.Tile;
using System;
using System.Collections.Generic;
using System.Linq;
using Terraria;
using Terraria.DataStructures;
using Terraria.GameContent.Tile_Entities;
using Terraria.ID;
using Terraria.ObjectData;
using TShockAPI.Net;
namespace TShockAPI.Handlers
@ -198,7 +201,7 @@ namespace TShockAPI.Handlers
TShock.Log.ConsoleDebug("Bouncer / SendTileRect rejected from no permission for tile object from {0}", args.Player.Name);
return;
}
if (TShock.TileBans.TileIsBanned((short)tileType))
{
TShock.Log.ConsoleDebug("Bouncer / SendTileRect rejected for banned tile");
@ -238,12 +241,12 @@ namespace TShockAPI.Handlers
if (tile.type == TileID.LandMine && !newTile.Active)
{
UpdateServerTileState(tile, newTile);
UpdateServerTileState(tile, newTile, TileDataType.Tile);
}
if (tile.type == TileID.WirePipe)
{
UpdateServerTileState(tile, newTile);
UpdateServerTileState(tile, newTile, TileDataType.Tile);
}
ProcessConversionSpreads(Main.tile[realX, realY], newTile);
@ -274,7 +277,7 @@ namespace TShockAPI.Handlers
return;
}
UpdateServerTileState(Main.tile[realX, realY], newTile);
UpdateServerTileState(Main.tile[realX, realY], newTile, TileDataType.Tile);
}
/// <summary>
@ -295,8 +298,14 @@ namespace TShockAPI.Handlers
TileID.Sets.Conversion.HardenedSand[tile.type] && TileID.Sets.Conversion.HardenedSand[newTile.Type] ||
TileID.Sets.Conversion.Thorn[tile.type] && TileID.Sets.Conversion.Thorn[newTile.Type] ||
TileID.Sets.Conversion.Moss[tile.type] && TileID.Sets.Conversion.Moss[newTile.Type] ||
TileID.Sets.Conversion.MossBrick[tile.type] && TileID.Sets.Conversion.MossBrick[newTile.Type] ||
WallID.Sets.Conversion.Stone[tile.wall] && WallID.Sets.Conversion.Stone[newTile.Wall] ||
TileID.Sets.Conversion.MossBrick[tile.type] && TileID.Sets.Conversion.MossBrick[newTile.Type]
)
{
TShock.Log.ConsoleDebug("Bouncer / SendTileRect processing a tile conversion update - [{0}] -> [{1}]", tile.type, newTile.Type);
UpdateServerTileState(tile, newTile, TileDataType.Tile);
}
if (WallID.Sets.Conversion.Stone[tile.wall] && WallID.Sets.Conversion.Stone[newTile.Wall] ||
WallID.Sets.Conversion.Grass[tile.wall] && WallID.Sets.Conversion.Grass[newTile.Wall] ||
WallID.Sets.Conversion.Sandstone[tile.wall] && WallID.Sets.Conversion.Sandstone[newTile.Wall] ||
WallID.Sets.Conversion.HardenedSand[tile.wall] && WallID.Sets.Conversion.HardenedSand[newTile.Wall] ||
@ -307,76 +316,84 @@ namespace TShockAPI.Handlers
WallID.Sets.Conversion.NewWall4[tile.wall] && WallID.Sets.Conversion.NewWall4[newTile.Wall]
)
{
TShock.Log.ConsoleDebug("Bouncer / SendTileRect processing a conversion update - [{0}|{1}] -> [{2}|{3}]", tile.type, tile.wall, newTile.Type, newTile.Wall);
UpdateServerTileState(tile, newTile);
TShock.Log.ConsoleDebug("Bouncer / SendTileRect processing a wall conversion update - [{0}] -> [{1}]", tile.wall, newTile.Wall);
UpdateServerTileState(tile, newTile, TileDataType.Wall);
}
}
/// <summary>
/// Updates a single tile's world state with a change from the tile rect packet
/// Updates a single tile's world state with a set of changes from the networked tile state
/// </summary>
/// <param name="tile">The tile to update</param>
/// <param name="newTile">The NetTile containing the change</param>
public static void UpdateServerTileState(ITile tile, NetTile newTile)
/// <param name="updateType">The type of data to merge into world state</param>
public static void UpdateServerTileState(ITile tile, NetTile newTile, TileDataType updateType)
{
tile.active(newTile.Active);
tile.type = newTile.Type;
//This logic (updateType & TDT.Tile) != 0 is the way Terraria does it (see: Tile.cs/Clear(TileDataType))
//& is not a typo - we're performing a binary AND test to see if a given flag is set.
if (newTile.FrameImportant)
if ((updateType & TileDataType.Tile) != 0)
{
tile.frameX = newTile.FrameX;
tile.frameY = newTile.FrameY;
tile.active(newTile.Active);
tile.type = newTile.Type;
if (newTile.FrameImportant)
{
tile.frameX = newTile.FrameX;
tile.frameY = newTile.FrameY;
}
else if (tile.type != newTile.Type || !tile.active())
{
//This is vanilla logic - if the tile changed types (or wasn't active) the frame values might not be valid - so we reset them to -1.
tile.frameX = -1;
tile.frameY = -1;
}
}
if (newTile.HasWall)
if ((updateType & TileDataType.Wall) != 0)
{
tile.wall = newTile.Wall;
}
if (newTile.HasLiquid)
if ((updateType & TileDataType.TilePaint) != 0)
{
tile.color(newTile.TileColor);
}
if ((updateType & TileDataType.WallPaint) != 0)
{
tile.wallColor(newTile.WallColor);
}
if ((updateType & TileDataType.Liquid) != 0)
{
tile.liquid = newTile.Liquid;
tile.liquidType(newTile.LiquidType);
}
tile.wire(newTile.Wire);
tile.wire2(newTile.Wire2);
tile.wire3(newTile.Wire3);
tile.wire4(newTile.Wire4);
tile.halfBrick(newTile.IsHalf);
if (newTile.HasColor)
if ((updateType & TileDataType.Slope) != 0)
{
tile.color(newTile.TileColor);
tile.halfBrick(newTile.IsHalf);
tile.slope(newTile.Slope);
}
if (newTile.HasWallColor)
if ((updateType & TileDataType.Wiring) != 0)
{
tile.wallColor(newTile.WallColor);
tile.wire(newTile.Wire);
tile.wire2(newTile.Wire2);
tile.wire3(newTile.Wire3);
tile.wire4(newTile.Wire4);
}
byte slope = 0;
if (newTile.Slope)
if ((updateType & TileDataType.Actuator) != 0)
{
slope += 1;
tile.actuator(newTile.IsActuator);
tile.inActive(newTile.Inactive);
}
if (newTile.Slope2)
{
slope += 2;
}
if (newTile.Slope3)
{
slope += 4;
}
tile.slope(slope);
TShock.Log.ConsoleDebug("Bouncer / SendTileRect updated a tile from type {0} to {1}", tile.type, newTile.Type);
}
/// <summary>
/// Performs <see cref="UpdateServerTileState(ITile, NetTile)"/> on multiple tiles
/// Performs <see cref="UpdateServerTileState(ITile, NetTile, TileDataType)"/> on multiple tiles
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
@ -389,7 +406,7 @@ namespace TShockAPI.Handlers
{
for (int j = 0; j < height; j++)
{
UpdateServerTileState(Main.tile[x + i, y + j], newTiles[i, j]);
UpdateServerTileState(Main.tile[x + i, y + j], newTiles[i, j], TileDataType.Tile);
}
}
}
@ -550,10 +567,10 @@ namespace TShockAPI.Handlers
{
for (int y = 0; y < height; y++)
{
UpdateServerTileState(Main.tile[tileX + x, tileY + y], newTiles[x, y]);
UpdateServerTileState(Main.tile[tileX + x, tileY + y], newTiles[x, y], TileDataType.All);
}
//Add a line of dirt blocks at the bottom for safety
UpdateServerTileState(Main.tile[tileX + x, tileY + height], new NetTile { Active = true, Type = 0 });
UpdateServerTileState(Main.tile[tileX + x, tileY + height], new NetTile { Active = true, Type = 0 }, TileDataType.All);
}
player.SendTileRect(tileX, tileY, width, height);

View file

@ -37,17 +37,34 @@ namespace TShockAPI.Net
public bool Wire2 { get; set; }
public bool Wire3 { get; set; }
public bool Wire4 { get; set; }
public byte HalfBrick { get; set; }
public byte Actuator { get; set; }
public bool Inactive { get; set; }
public bool IsHalf { get; set; }
public bool IsActuator { get; set; }
public byte TileColor { get; set; }
public byte WallColor { get; set; }
public bool Slope { get; set; }
public bool Slope1 { get; set; }
public bool Slope2 { get; set; }
public bool Slope3 { get; set; }
public byte Slope
{
get
{
byte sl = 0;
if (Slope1)
sl += 1;
if (Slope2)
sl += 2;
if (Slope3)
sl += 4;
return sl;
}
}
public bool HasColor
{
get { return TileColor > 0; }
@ -85,14 +102,13 @@ namespace TShockAPI.Net
Wire2 = false;
Wire3 = false;
Wire4 = false;
HalfBrick = 0;
Actuator = 0;
Inactive = false;
TileColor = 0;
WallColor = 0;
Lighted = false;
Slope = false;
Slope1 = false;
Slope2 = false;
Slope3 = false;
}
public NetTile(Stream stream)
@ -124,9 +140,7 @@ namespace TShockAPI.Net
bits[6] = true;
if (Inactive)
{
bits[7] = true;
}
stream.WriteInt8((byte) bits);
@ -144,7 +158,7 @@ namespace TShockAPI.Net
if (HasWallColor)
bits[3] = true;
if (Slope)
if (Slope1)
bits[4] = true;
if (Slope2)
@ -195,7 +209,7 @@ namespace TShockAPI.Net
Wire2 = flags2[0];
Wire3 = flags2[1];
Slope = flags2[4];
Slope1 = flags2[4];
Slope2 = flags2[5];
Slope3 = flags2[6];
Wire4 = flags2[7];