0
0
mirror of https://github.com/Wurst-Imperium/Wurst7.git synced 2024-09-20 01:12:13 +02:00

Fix 1.20.x sign detection vulnerability

This commit is contained in:
Alexander01998 2024-02-25 22:36:05 +01:00
parent 57cdc850cb
commit 1ade6559f9
11 changed files with 228 additions and 29 deletions

View File

@ -11,6 +11,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.IllegalFormatException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -175,18 +176,29 @@ public enum WurstClient
return wurstFolder;
}
public String translate(String key)
public String translate(String key, Object... args)
{
if(otfs.translationsOtf.getForceEnglish().isChecked())
return ILanguageManager.getEnglish().get(key);
{
String string = ILanguageManager.getEnglish().get(key);
try
{
return String.format(string, args);
}catch(IllegalFormatException e)
{
return key;
}
}
// This extra check is necessary because I18n.translate() doesn't
// always return the key when the translation is missing. If the key
// contains a '%', it will return "Format Error: key" instead.
if(!I18n.hasTranslation(key))
return key;
return I18n.translate(key);
return I18n.translate(key, args);
}
public WurstAnalytics getAnalytics()

View File

@ -86,14 +86,15 @@ public final class AltManagerScreen extends Screen
public void init()
{
listGui = new ListGui(client, this, altManager.getList());
WurstClient wurst = WurstClient.INSTANCE;
Exception folderException = altManager.getFolderException();
if(folderException != null && shouldAsk)
{
Text title =
Text.translatable("gui.wurst.altmanager.folder_error.title");
Text message = Text.translatable(
"gui.wurst.altmanager.folder_error.message", folderException);
Text title = Text.literal(
wurst.translate("gui.wurst.altmanager.folder_error.title"));
Text message = Text.literal(wurst.translate(
"gui.wurst.altmanager.folder_error.message", folderException));
Text buttonText = Text.translatable("gui.done");
// This just sets shouldAsk to false and closes the message.
@ -105,9 +106,10 @@ public final class AltManagerScreen extends Screen
}else if(altManager.getList().isEmpty() && shouldAsk)
{
Text title = Text.translatable("gui.wurst.altmanager.empty.title");
Text message =
Text.translatable("gui.wurst.altmanager.empty.message");
Text title = Text
.literal(wurst.translate("gui.wurst.altmanager.empty.title"));
Text message = Text
.literal(wurst.translate("gui.wurst.altmanager.empty.message"));
BooleanConsumer callback = this::confirmGenerate;
ConfirmScreen screen = new ConfirmScreen(callback, title, message);

View File

@ -46,7 +46,8 @@ public abstract class ClientPlayNetworkHandlerMixin
public void onOnServerMetadata(ServerMetadataS2CPacket packet,
CallbackInfo ci)
{
if(!WurstClient.INSTANCE.isEnabled())
WurstClient wurst = WurstClient.INSTANCE;
if(!wurst.isEnabled())
return;
// Remove Mojang's dishonest warning toast on safe servers
@ -58,10 +59,10 @@ public abstract class ClientPlayNetworkHandlerMixin
}
// Add an honest warning toast on unsafe servers
MutableText title = Text.literal(ChatUtils.WURST_PREFIX).append(
Text.translatable("toast.wurst.nochatreports.unsafe_server.title"));
MutableText message = Text
.translatable("toast.wurst.nochatreports.unsafe_server.message");
MutableText title = Text.literal(ChatUtils.WURST_PREFIX
+ wurst.translate("toast.wurst.nochatreports.unsafe_server.title"));
MutableText message = Text.literal(
wurst.translate("toast.wurst.nochatreports.unsafe_server.message"));
SystemToast systemToast = SystemToast.create(client,
SystemToast.Type.UNSECURE_SERVER_WARNING, title, message);

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2014-2024 Wurst-Imperium and contributors.
*
* This source code is subject to the terms of the GNU General Public
* License, version 3. If a copy of the GPL was not distributed with this
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.wurstclient.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.option.ControlsListWidget;
import net.minecraft.client.gui.widget.ElementListWidget;
import net.minecraft.client.gui.widget.EntryListWidget;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent;
import net.wurstclient.WurstClient;
@Mixin(ControlsListWidget.class)
public abstract class ControlsListWidgetMixin
extends ElementListWidget<ControlsListWidget.Entry>
{
public ControlsListWidgetMixin(WurstClient wurst, MinecraftClient client,
int width, int height, int y, int itemHeight)
{
super(client, width, height, y, itemHeight);
}
/**
* Prevents Wurst's zoom keybind from being added to the controls list.
*/
@WrapOperation(at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/gui/screen/option/ControlsListWidget;addEntry(Lnet/minecraft/client/gui/widget/EntryListWidget$Entry;)I",
ordinal = 1),
method = "<init>(Lnet/minecraft/client/gui/screen/option/KeybindsScreen;Lnet/minecraft/client/MinecraftClient;)V")
private int dontAddZoomEntry(ControlsListWidget instance,
EntryListWidget.Entry<?> entry, Operation<Integer> original)
{
if(!(entry instanceof ControlsListWidget.KeyBindingEntry kbEntry))
return original.call(instance, entry);
Text name = kbEntry.bindingName;
if(name == null || !(name
.getContent() instanceof TranslatableTextContent trContent))
return original.call(instance, entry);
if(!"key.wurst.zoom".equals(trContent.getKey()))
return original.call(instance, entry);
return 0;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2014-2024 Wurst-Imperium and contributors.
*
* This source code is subject to the terms of the GNU General Public
* License, version 3. If a copy of the GPL was not distributed with this
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.wurstclient.mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.text.KeybindTextContent;
import net.minecraft.text.Text;
import net.minecraft.text.TextContent;
@Mixin(KeybindTextContent.class)
public abstract class KeybindTextContentMixin implements TextContent
{
@Shadow
@Final
private String key;
/**
* Ensures that any chat messages, written books, signs, etc. cannot resolve
* Wurst-related keybinds.
*
* <p>
* Fixes at least one security vulnerability affecting Minecraft 1.20 and
* later versions, where the server can detect the presence of Wurst by
* abusing Minecraft's sign editing feature. When a player edits a sign, any
* translated text and keybind text components on that sign are resolved by
* the client and sent back to the server as plain text. This allows the
* server to detect the presence of non-vanilla keybinds, such as Wurst's
* zoom keybind.
*
* <p>
* It is likely that similar vulnerabilities exist or will exist in other
* parts of the game, such as chat messages and written books. Mojang has a
* long history of failing to properly secure their text component system
* (see BookHack, OP-Sign, BookDupe). Therefore it's best to cut off this
* entire attack vector at the source.
*/
@Inject(at = @At("RETURN"),
method = "getTranslated()Lnet/minecraft/text/Text;",
cancellable = true)
private void onGetTranslated(CallbackInfoReturnable<Text> cir)
{
if(key != null && key.contains("wurst"))
cir.setReturnValue(Text.literal(key));
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2014-2024 Wurst-Imperium and contributors.
*
* This source code is subject to the terms of the GNU General Public
* License, version 3. If a copy of the GPL was not distributed with this
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.wurstclient.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.text.TextContent;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.Language;
@Mixin(TranslatableTextContent.class)
public abstract class TranslatableTextContentMixin implements TextContent
{
/**
* Ensures that any chat messages, written books, signs, etc. cannot resolve
* Wurst-related translation keys.
*
* <p>
* Fixes at least one security vulnerability affecting Minecraft 1.20 and
* later versions, where the server can detect the presence of Wurst by
* abusing Minecraft's sign editing feature. When a player edits a sign, any
* translated text and keybind text components on that sign are resolved by
* the client and sent back to the server as plain text. This allows the
* server to detect the presence of non-vanilla translation keys.
*
* <p>
* It is likely that similar vulnerabilities exist or will exist in other
* parts of the game, such as chat messages and written books. Mojang has a
* long history of failing to properly secure their text component system
* (see BookHack, OP-Sign, BookDupe). Therefore it's best to cut off this
* entire attack vector at the source.
*/
@WrapOperation(at = @At(value = "INVOKE",
target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;)Ljava/lang/String;",
ordinal = 0), method = "updateTranslations()V")
private String translate(Language instance, String key,
Operation<String> original)
{
if(key != null && key.contains("wurst"))
return key;
return original.call(instance, key);
}
/**
* Same as above, but for translatable text components with a fallback.
*/
@WrapOperation(at = @At(value = "INVOKE",
target = "Lnet/minecraft/util/Language;get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
ordinal = 0), method = "updateTranslations()V")
private String translateWithFallback(Language instance, String key,
String fallback, Operation<String> original)
{
if(key != null && key.contains("wurst"))
return fallback;
return original.call(instance, key, fallback);
}
}

View File

@ -47,12 +47,13 @@ public final class ForcedChatReportsScreen extends Screen
public ForcedChatReportsScreen(Screen prevScreen)
{
super(Text.literal(ChatUtils.WURST_PREFIX).append(
Text.translatable("gui.wurst.nochatreports.unsafe_server.title")));
super(Text.literal(ChatUtils.WURST_PREFIX)
.append(Text.literal(WurstClient.INSTANCE
.translate("gui.wurst.nochatreports.unsafe_server.title"))));
this.prevScreen = prevScreen;
reason =
Text.translatable("gui.wurst.nochatreports.unsafe_server.message");
reason = Text.literal(WurstClient.INSTANCE
.translate("gui.wurst.nochatreports.unsafe_server.message"));
NoChatReportsOtf ncr = WurstClient.INSTANCE.getOtfs().noChatReportsOtf;
sigButtonMsg = () -> WurstClient.INSTANCE

View File

@ -43,12 +43,12 @@ public final class NcrModRequiredScreen extends Screen
public NcrModRequiredScreen(Screen prevScreen)
{
super(Text.literal(ChatUtils.WURST_PREFIX).append(
Text.translatable("gui.wurst.nochatreports.ncr_mod_server.title")));
super(Text.literal(ChatUtils.WURST_PREFIX + WurstClient.INSTANCE
.translate("gui.wurst.nochatreports.ncr_mod_server.title")));
this.prevScreen = prevScreen;
reason =
Text.translatable("gui.wurst.nochatreports.ncr_mod_server.message");
reason = Text.literal(WurstClient.INSTANCE
.translate("gui.wurst.nochatreports.ncr_mod_server.message"));
OtfList otfs = WurstClient.INSTANCE.getOtfs();

View File

@ -20,7 +20,6 @@ import net.minecraft.text.Text;
import net.wurstclient.Category;
import net.wurstclient.DontBlock;
import net.wurstclient.SearchTags;
import net.wurstclient.WurstClient;
import net.wurstclient.events.UpdateListener;
import net.wurstclient.other_feature.OtherFeature;
import net.wurstclient.settings.CheckboxSetting;
@ -81,15 +80,15 @@ public final class NoChatReportsOtf extends OtherFeature
public MessageIndicator modifyIndicator(Text message,
MessageSignatureData signature, MessageIndicator indicator)
{
if(!WurstClient.INSTANCE.isEnabled() || MC.isInSingleplayer())
if(!WURST.isEnabled() || MC.isInSingleplayer())
return indicator;
if(indicator != null || signature == null)
return indicator;
return new MessageIndicator(0xE84F58, Icon.CHAT_MODIFIED,
Text.literal(ChatUtils.WURST_PREFIX + "\u00a7cReportable\u00a7r - ")
.append(Text.translatable(
Text.literal(ChatUtils.WURST_PREFIX + "\u00a7cReportable\u00a7r - "
+ WURST.translate(
"description.wurst.nochatreports.message_is_reportable")),
"Reportable");
}
@ -102,8 +101,7 @@ public final class NoChatReportsOtf extends OtherFeature
public boolean isActive()
{
return isEnabled() && WurstClient.INSTANCE.isEnabled()
&& !MC.isInSingleplayer();
return isEnabled() && WURST.isEnabled() && !MC.isInSingleplayer();
}
@Override

View File

@ -8,6 +8,7 @@ accessible field net/minecraft/client/MinecraftClient itemUseCooldown I
accessible field net/minecraft/client/gui/hud/ChatHud visibleMessages Ljava/util/List;
accessible field net/minecraft/client/gui/screen/Screen drawables Ljava/util/List;
accessible field net/minecraft/client/gui/screen/ingame/CreativeInventoryScreen selectedTab Lnet/minecraft/item/ItemGroup;
accessible field net/minecraft/client/gui/screen/option/ControlsListWidget$KeyBindingEntry bindingName Lnet/minecraft/text/Text;
accessible field net/minecraft/client/network/ClientPlayNetworkHandler messagePacker Lnet/minecraft/network/message/MessageChain$Packer;
accessible field net/minecraft/client/network/ClientPlayNetworkHandler session Lnet/minecraft/network/encryption/ClientPlayerSession;
accessible field net/minecraft/client/network/ClientPlayerEntity lastPitch F

View File

@ -25,6 +25,7 @@
"ClientPlayerInteractionManagerMixin",
"ClientPlayNetworkHandlerMixin",
"ClientWorldMixin",
"ControlsListWidgetMixin",
"CreativeInventoryScreenMixin",
"DeathScreenMixin",
"DirectConnectScreenMixin",
@ -40,6 +41,7 @@
"IngameHudMixin",
"InGameOverlayRendererMixin",
"KeyBindingMixin",
"KeybindTextContentMixin",
"KeyboardMixin",
"LanguageManagerMixin",
"LivingEntityRendererMixin",
@ -62,6 +64,7 @@
"TelemetryManagerMixin",
"TextVisitFactoryMixin",
"TitleScreenMixin",
"TranslatableTextContentMixin",
"WorldMixin",
"WorldRendererMixin"
],