Contents

Code Snippet Serie - 02 - Memory Exhaustion via Unchecked Input Length

Challenge Description

/assets/images/writeups/code_snippets/code-snippet-02-01.png

This challenge, authored by @Ethnical and @fadam, involves exploiting a vulnerability in a blockchain network server that processes incoming network messages. The vulnerability allows an attacker to cause a Denial of Service (DoS) by exhausting the server’s memory.

Vulnerability Overview

🛑 Vulnerability: The vulnerability lies in the unchecked allocation of memory based on user-controlled input length, leading to potential memory exhaustion.

Technical Analysis

Vulnerability Details

  1. Unchecked Length Parameter:

    • The length parameter is read from the network without any validation.
    • This parameter directly influences the size of the buffer allocated in readFullBytes.
  2. Memory Allocation:

    • The function make([]byte, length) allocates a buffer of size length.
    • If length is excessively large, this can lead to significant memory allocation, potentially exhausting the server’s memory.

Exploitation Process

  1. Crafting a Malicious Input:

    • An attacker can send a message with a very large length parameter.
    • For example, setting length to 0xFFFFFFFF (maximum value for a 32-bit unsigned integer).
  2. Sending the Malicious Input:

    • The crafted message is sent to the server.
    • The server attempts to allocate a buffer of size 0xFFFFFFFF bytes.
  3. Resulting Denial of Service:

    • The server’s memory is exhausted, causing it to crash or become unresponsive.
    • This disrupts the blockchain network, affecting all nodes that receive the malicious message.

Mitigation

🔒 To mitigate this vulnerability, the following measures can be taken:

  • Validate the length parameter before using it to allocate memory.

  • Set a maximum allowable length to prevent excessively large allocations.

    const maxLength = 1024 * 1024 // 1MB
    
    func (s *StreamServer) processCmdBookmark(clientId string) error {
        // Read bookmark length parameter from the network
        conn := s.clients[clientId].conn
        length, err := readFullUint32(conn)
        if err != nil {
            return err
        }
        if length > maxLength {
            return fmt.Errorf("length exceeds maximum allowed size")
        }
        // Read bookmark parameter
        bookmark, err := readFullBytes(length, conn)
        if err != nil {
            return err
        }
        //...
    }
    

Conclusion

🔑 Understanding the memory exhaustion vulnerability and its exploitation process is crucial for developers to protect their applications. By prioritizing security measures, including input validation and memory allocation checks, the risks associated with memory exhaustion vulnerabilities can be effectively mitigated.