In June 2024, a significant vulnerability was discovered in Square’s Wire library (before version 5.2.). The flaw, registered as CVE-2024-58103, impacts parsing logic in ByteArrayProtoReader32.kt and ProtoReader.kt. The root cause? A missing limit on group nesting — which attackers can abuse to force stack overflows, denial-of-service, or even exploit memory corruption vectors depending on environment settings.

Let’s break down the issue, see how it can be exploited, and what you should do to stay safe.

What is Square Wire?

Wire is a lightning-fast, flexible library for working with Protocol Buffers (protobufs) on Java and Kotlin. It takes care of parsing, serializing, encrypting, and more — both on the client and server side. Since many companies use protobuf for critical cloud and mobile apps, Wire has become widely adopted.

The Vulnerability

Before version 5.2., both ByteArrayProtoReader32.kt and ProtoReader.kt had no recursion limit for groups (nested fields) in protocol buffer messages.

No limit means an attacker can craft a message with hundreds or thousands of recursive group levels.

- Each level adds to the parser’s call stack — too many levels will overflow the stack, crashing the app or service.

Here's a simplified look at the vulnerable code (before 5.2.)

// From ProtoReader.kt (vulnerable)
fun readGroup(tag: Int): Message {
    // ... some parsing logic ...
    while (true) {
        val nextTag = peekTag()
        if (nextTag == END_GROUP) break
        readField(nextTag) // This can recurse infinitely!
    }
    // ...
}

Notice: No depth counter is used. Each group opens a new stack frame. If a message nests too deeply, crash!

Imagine a malicious client sends you a protobuf message like this (in text form)

group1 {
  group2 {
    group3 {
      ...
        groupN { }
      ...
    }
  }
}

*With N = 10,000*, the parser will dive 10,000 call frames deep! Most JVMs can't handle that — StackOverflowError crashes your service.

Let’s show a sample in Kotlin/Java pseudocode of how an attacker could break your server

// Forge an evil proto message with 20,000 nested groups!
fun generateEvilProto(depth: Int): ByteArray {
    var data = ByteArray()
    repeat(depth) {
        data += byteArrayOf(xA) // Start group tag
    }
    repeat(depth) {
        data += byteArrayOf(xB) // End group tag
    }
    return data
}

// Now, try to parse as normal (will crash with StackOverflowError!)
val evilData = generateEvilProto(20000)
val reader = ProtoReader(evilData.inputStream()) // vulnerable
reader.readGroup(1) // BOOM!

> Result: JVM crash, process killed, or even potential remote code execution in some strange JVMs.

Real-World Impact

- Denial of Service (DoS): A remote attacker can repeatedly crash backend servers by sending specially crafted protobuf messages.
- Resource Exhaustion: Causes high CPU/memory usage while parsing.
- Possible Security Bypass: If your security/gateway logic relies on protobuf parsing, attackers can try a DoS before you authenticate data.

From Wire release notes

> *Added a recursion limit to ProtoReader and ByteArrayProtoReader32 to prevent StackOverflow and DoS from deeply-nested groups.*

Here’s what the patched logic looks like

// Pseudocode for the fix
const val MAX_RECURSION_DEPTH = 64

fun readGroup(tag: Int, depth: Int = ) {
    if (depth > MAX_RECURSION_DEPTH)
        throw ProtocolException("Too many nested groups")

    while (true) {
        val nextTag = peekTag()
        if (nextTag == END_GROUP) break
        readField(nextTag, depth + 1) // Pass depth!
    }
}

You accept protobuf messages from untrusted sources.

- You do not wrap your parser in custom stack/depth checks.

`

// In build.gradle

`

2. Validate Data: If you can’t upgrade quickly, wrap parsing in a recursion guard or reject messages with absurdly high nesting.

References

- CVE Details: CVE-2024-58103
- Wire 5.2. Release Notes
- Square Wire GitHub
- Understanding Protobuf Groups

Final Thoughts

This bug shows why input validation and limits are so essential, even with highly trusted libraries like Wire. Always keep your dependencies up-to-date, and when reviewing parsing code, ask: “What happens with unexpectedly large or deeply nested inputs?”

Timeline

Published on: 03/16/2025 04:15:12 UTC