Mojira Archive
MC-167347

Out-of-bounds write through LWJGL's MemoryUtil.memLengthUTF8

Background

There is a not yet report bug in LWJGL's MemoryUtil.memLengthUTF8 method which incorrectly calculates that a low surrogate followed by any character takes 4 bytes. This is incorrect because it actually can require up to 6 bytes when encoded in UTF-8. When the string is then encoded in the allocated ByteBuffer, it writes out of the bounds, and since it uses Unsafe this can likely be exploited or at least lead to JVM crashes.

Affected lines:
https://github.com/LWJGL/lwjgl3/blob/35e5f80d7c5beb2a06af5fa0a29854a03c739b1f/modules/lwjgl/core/src/main/java/org/lwjgl/system/MemoryUtil.java#L2185-L2190

I haven't reported the bug to LWJGL yet because I do not know how it would be handled and whether other people would notice that it is exploitable when they see the commit which fixes it.
However, I will report it as soon as there is a workaround in Minecraft.

Affected Minecraft methods

(Might be incomplete, but I did not find more)

  • com.mojang.blaze3d.platform.ClipboardManager.setClipboard(long, ByteBuffer, String)
  • com.mojang.blaze3d.platform.NativeImage.fromBase64(String)

Exploitation scope

Based on the affected methods, this could possible be exploited in the following scenarios:

  • Clipboard:
    • copy_to_clipboard clickEvent:
      • Malicous op or even just Creative mode players on server
      • Malicious downloaded worlds
    • Writable book
    • Malicious (block) entity data which user copies through F3 + I
  • Malicious base64 encoded server icon in server list

Proof of concept

  1. Place a command block and paste the command from Command.txt
  2. Activate the command block
  3. Click "Click" in chat

Either the JVM will crash, or the following exception will be thrown:

java.lang.IllegalArgumentException: Missing termination
    at org.lwjgl.system.Checks.assertNT(Checks.java:108)
    at org.lwjgl.system.Checks.checkNT1(Checks.java:139)
    at org.lwjgl.glfw.GLFW.glfwSetClipboardString(GLFW.java:4361)
...

Workaround

Remove all unpaired low surrogates. Unpaired high surrogates are not a problem because when the string is encoded any char following a high surrogate is treated as low surrogate and therefore both will take up at most 4 bytes.

static CharSequence filterUnpairedLowSurrogates(CharSequence charSeq) {
    StringBuilder sb = new StringBuilder(charSeq.length());
    boolean hasHighSurrogate = false;
    
    for (int i = 0; i < charSeq.length(); i++) {
        char c = charSeq.charAt(i);
        
        if (Character.isHighSurrogate(c)) {
            hasHighSurrogate = true;
        }
        // Skip unpaired low surrogates
        else if (!hasHighSurrogate && Character.isLowSurrogate(c)) {
            continue;
        }
        else {
            hasHighSurrogate = false;
        }
        
        sb.append(c);
    }
    
    return sb;
}

Or build your own version of LWJGL, the attached MemoryUtil.diff should solve the issue.

Fixed

Marcono1234

[Mojang] Bartosz Bok

2019-12-08, 08:43 PM

2020-01-10, 09:13 PM

2020-01-10, 04:32 PM

1

2

Confirmed

Very Important

Crash

mojang_internal_2

1.15 Pre-release 6

1.15.2 Pre-Release 1