If you already have all the context, skip directly to the exploit!
Components are the way text is presented in Minecraft, there are multiple types of components, including text components, translatable components, keybind components, and more.
Components are represented as a tree, each component has its type and its children, the children inherit the parent's style (i.e. its color and decorations).
In 1.21.9 a new object component type was added that can render player heads (that I call player sprites) and atlas sprites.
Since player sprites take in the same profile data used in all other places it means we can provide either a player name, UUID, or most importantly, profile properties, which simply means an uploaded player skin.
The process itself is simple, the width of a MOTD is 265 pixels, and can show 2 rows, player heads are 8x8 pixels.
This means we can split any image whose width is a multiple of 8 and is either 8 or 16 pixels high into 8x8 segments, export skin images that have the segment as the skin's front face, and display each segment one after the other as a player sprite by providing profile properties!
Although one slight catch, profile properties is what the Mojang API returns when querying a player's profile, and include data such as the hashes and URLs of a player's skin and cape textures, the game only shows skins uploaded to Mojang's own texture endpoint, something that can only be done when applying a skin to a Minecraft account.
Mineskin is a popular service to upload player skins and receive back profile properties, it uses a large set of Minecraft accounts people have donated to the website to power the service, it applies a given skin to an account, and gives you back the properties.
Awesome! We can now have extra fancy server descriptions.
In the newest snapshot, 26.1-pre-1, the server listing screen was changed to remove all player sprite objects from MOTDs on the client, effectively completely killing off the new tech that was found with them.
When sanitizing, the client recursively iterates on the MOTD component's tree to remove any player sprite objects, and as a fail-safe has this check:
public static MutableComponent resolve(/* ... */, int recursionDepth) {
if (recursionDepth > 100) {
return component.copy();
} else {
// ...
}
// ...
}Caught that? Our little resolver gives up after reaching a depth of 100!
This lets us easily bypass the entire thing by just burying the MOTD component in an empty component nested 100 times.
You can think of it like this:
<empty>
|_<empty>
|_<empty>
|_<empty>
|_<...>
|_<head sprite><head sprite><head sprite><...>
This is all easy and fun in theory, but this massively inflates the size of the MOTD component, 2 row images or even a full row cannot fit with the sheer amount of data the nested empty components add.
By all means, this is not a perfect solution, admittedly, Mojang did successfully patch out player sprite objects, and that kind of sucks :(
I can only speculate, but here are my theories:
Images make MOTDs inaccessible for screen readers
MOTDs are already inaccessible, servers use custom colors, symbols, and text that is structured vertically; In fact, the Narrator does not even voice server MOTDs, this is clearly not a priority for Mojang.
The spread of this to other servers might cause a lot of traffic to the account servers
Mineskin is already a high traffic globally used service with an API that is uploading (hundreds of?) thousands of skins, I doubt the effect here will be anything out of the ordinary.
Generally, the situation is not that bad, every server probably would be using this, making server MOTDs less coherent, and server owners might forget to add a fallback for older versions to also appear correctly in server listing websites, it is unfortunate, but it's not the end of the world, Mojang is very likely to patch the exploit, so don't rely on it! I mostly did this for sake of finding an exploit, making a public demo, and writing about it, an experience I've always wanted to go through :).
And hey, if you learnt something a Star is appreciated :)
- BlazeMCworld - Finding the vulnerability & nesting prototype
- RedVortexDev - Image head generation & server development
- zBinFinn - Hosting the server