───✱*.。:。✱*.:。✧*.。✰*.:。✧*.。:。*.。✱ ───

Description

PaperMC introduced an API known as MiniMessage around 4 years ago. Prior to somewhat recently, there hasn’t been any major exploits involving this. However, users have recently discovered that many chat plugins are vulnerable to certain malicious payloads being sent.

Scope

MiniMessage introduces certain “click” events. Including, but not limited to, opening URLs (this does have a modal for confirmation), and sending chat commands. Most importantly, sending chat commands doesn’t have any sort of protection. A malicious actor could, for example, send a message that would run /op <player> upon click.

Something else interesting is that certain chat plugins have access to PlaceholderAPI. This is purely client side, but you can do some trolling. For instance, you can send a player’s exact coordinates such a message such as hello <papi:player_name>, you are at <papi:player_x> <papi:player_y> <papi:player_z>.

Example

This is what a message sent using this exploit could look like. Upon clicking the “/rewards”, it could run any command. In this case, it just does /spawn. However, one can imagine what else can be done with this, especially involving players with administrator permissions.

Hey, the message below is a fake message!<br>
<green>
	You have unclaimed rewards in
	<b><u><click:run_command:/spawn>/rewards</click></u></b>!
</green>

Patching

Many chat plugins have this exploit patched; however, if your chat plugin is custom, or if you have control over it then you have a few ways to prevent this exploit. Most importantly, never call MiniMessage.miniMessage().deserialize() on raw chat input. If you do, the sender is authoring components, which can include click events, hover events, selectors, etc.

MiniMessage provides a helper function, MiniMessage.miniMessage().escapeTags() that you can use to preserve the literal text, which would just be a string such as "<b>Hello, this is bold text<b>". You can also use the helper MiniMessage.miniMessage().stripTags(), which will remove all tags completely.

Another option is to use a restricted MiniMessage instance. This is good, since you can have some formatting; you build a parser that only allows a subset of components, such as colors and decorations.

MiniMessage safeMiniMessage = MiniMessage.builder()
	.tags(TagResolver.builder()
        .resolver(StandardTags.color())
        .resolver(StandardTags.decorations())
        .build())
    .build();

In this instance, we built an instance with just colors and decorations. This would include things like <red>, <green>, <blue>, <bold>, <italic>, and other visual formatting tags, without including dangerous ones like <click>.

───✱*.。:。✱*.:。✧*.。✰*.:。✧*.。:。*.。✱ ───