From 36ee52aa6e044057066fbcca3e0f250b96b8ba00 Mon Sep 17 00:00:00 2001 From: games647 Date: Mon, 10 Jul 2023 15:42:06 +0200 Subject: [PATCH] Print out a warning if we detect local IP addresses during connections Reference #1055 --- .../games647/fastlogin/bukkit/InetUtils.java | 38 +++++++++++++++++++ .../protocollib/VerifyResponseTask.java | 27 +++++++++---- 2 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 bukkit/src/main/java/com/github/games647/fastlogin/bukkit/InetUtils.java diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/InetUtils.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/InetUtils.java new file mode 100644 index 00000000..d83dfb9e --- /dev/null +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/InetUtils.java @@ -0,0 +1,38 @@ +package com.github.games647.fastlogin.bukkit; + +import java.net.InetAddress; + +public class InetUtils { + + private InetUtils() { + // Utility + } + + /** + * Verifies if the given IP address is from the local network + * + * @param address IP address + * @return true if address is from local network or even from the device itself (loopback) + */ + public static boolean isLocalAddress(InetAddress address) { + // Loopback addresses like 127.0.* (IPv4) or [::1] (IPv6) + return address.isLoopbackAddress() + // Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated) + // Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses + || address.isSiteLocalAddress() + // Example: 169.254.0.0/16, fe80::/10 + // Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration + || address.isLinkLocalAddress() + // non deprecated unique site-local that java doesn't check yet -> fc00::/7 + || isIPv6UniqueSiteLocal(address); + } + + private static boolean isIPv6UniqueSiteLocal(InetAddress address) { + // ref: https://en.wikipedia.org/wiki/Unique_local_address + + // currently undefined but could be used in the near future fc00::/8 + return (address.getAddress()[0] & 0xFF) == 0xFC + // in use for unique site-local fd00::/8 + || (address.getAddress()[0] & 0xFF) == 0xFD; + } +} diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index ef0bda01..8b8aaf7f 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -46,6 +46,7 @@ import com.github.games647.craftapi.model.skin.SkinProperty; import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; +import com.github.games647.fastlogin.bukkit.InetUtils; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import lombok.val; import org.bukkit.entity.Player; @@ -74,7 +75,7 @@ public class VerifyResponseTask implements Runnable { static { ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass( - "util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME + "util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME ); } @@ -149,10 +150,20 @@ public class VerifyResponseTask implements Runnable { } else { //user tried to fake an authentication disconnect( - "invalid-session", - "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}", - session.getRequestUsername(), socketAddress, serverId + "invalid-session", + "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}", + requestedUsername, address, serverId ); + + if (InetUtils.isLocalAddress(address)) { + plugin.getLog().warn("The incoming request for player {} uses a local IP address. " + + "This indicates the use of reverse-proxy like HAProxy, TCPShield, BungeeCord, Velocity, " + + "etc. By default, configurable, this plugin requests Mojang to verify the connecting IP " + + "to this server with the one used to log into Minecraft to prevent MITM attacks. In " + + "order to work this security feature, the actual client IP needs to be forwarding " + + "(keyword IP forwarding). This process will also be useful for other server " + + "features like IP banning, so that it doesn't ban the proxy IP", requestedUsername); + } } } catch (IOException ioEx) { disconnect("error-kick", "Failed to connect to session server", ioEx); @@ -217,15 +228,15 @@ public class VerifyResponseTask implements Runnable { try { // Try to get the old (pre MC 1.16.4) encryption method encryptMethod = FuzzyReflection.fromClass(networkManagerClass) - .getMethodByParameters("a", SecretKey.class); + .getMethodByParameters("a", SecretKey.class); } catch (IllegalArgumentException exception) { // Get the new encryption method encryptMethod = FuzzyReflection.fromClass(networkManagerClass) - .getMethodByParameters("a", Cipher.class, Cipher.class); + .getMethodByParameters("a", Cipher.class, Cipher.class); // Get the needed Cipher helper method (used to generate ciphers from login key) cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS) - .getMethodByParameters("a", int.class, Key.class); + .getMethodByParameters("a", int.class, Key.class); } } @@ -276,7 +287,7 @@ public class VerifyResponseTask implements Runnable { EquivalentConverter converter = BukkitConverters.getWrappedPublicKeyDataConverter(); val wrappedKey = Optional.ofNullable(clientKey).map(key -> - new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature()) + new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature()) ); startPacket.getOptionals(converter).write(0, wrappedKey);