MEDIUM out of bounds readhapi

Out Of Bounds Read in Hapi

How Out Of Bounds Read Manifests in Hapi

In a Hapi application, an out‑of‑bounds read typically occurs when user‑supplied data is used as an index or length value without proper validation, causing the code to read memory (or a Buffer) beyond its allocated size. Because Hapi handlers receive raw request data through request.params, request.query, or request.payload, a missing validation step can let an attacker influence buffer operations.

Consider a route that extracts a numeric ID from the path and uses it to read a specific byte from a pre‑allocated Buffer that holds a shared secret:

const Hapi = require('@hapi/hapi');
const init = async () => {
  const server = Hapi.server({ port: 3000 });
  // 32‑byte secret buffer
  const secret = Buffer.from('0123456789abcdef0123456789abcdef', 'hex');

  server.route({
    method: 'GET',
    path: '/secret/{index}',
    handler: (request, h) => {
      // BUG: index is taken directly from the path, no bounds check
      const idx = Number(request.params.index);
      // If idx is negative or >= secret.length, Buffer.readUInt8 throws
      const byte = secret.readUInt8(idx);
      return { value: byte };
    }
  });

  await server.start();
  console.log('Server running at', server.info.uri);
};

init();

If an attacker supplies /secret/-1 or /secret/100, readUInt8 throws a RangeError, which Hapi treats as an unhandled rejection and returns a 500 error. While this may seem like a simple crash, the error message can leak stack traces, and repeated out‑of‑bounds reads can be used to probe the size of the buffer (information disclosure) or to cause a denial‑of‑service by triggering exceptions at high volume.

Another common pattern appears when handling binary payloads. Suppose a route expects a Buffer where the first two bytes indicate a length field that dictates how many subsequent bytes to slice:

server.route({
  method: 'POST',
  path: '/parse',
  handler: (request, h) => {
    const buf = request.payload; // assumed to be a Buffer
    const len = buf.readUInt16BE(0); // attacker‑controlled length
    // BUG: no validation that len <= buf.length - 2
    const slice = buf.slice(2, 2 + len);
    return slice.toString('utf8');
  }
});

Here, a malicious client can set len to a value larger than the actual payload, causing slice to read past the end of the Buffer. In Node.js, Buffer.slice does not throw; it returns a Buffer that includes zero‑filled bytes beyond the original data, potentially exposing adjacent memory contents if the underlying SlowBuffer was not zero‑filled. This can lead to inadvertent disclosure of other process memory or application data.

Both examples illustrate how missing input validation in Hapi handlers can turn a routine data‑access operation into an out‑of‑bounds read vulnerability.

Hapi-Specific Detection

Detecting out‑of‑bounds reads in a Hapi service requires observing how user‑controlled values influence buffer or array accesses. Because the vulnerability is rooted in missing validation, static analysis alone is insufficient; dynamic probing is needed to confirm that a handler reacts abnormally when given extreme numeric inputs.

middleBrick performs unauthenticated, black‑box scanning of the API surface. When it encounters a Hapi endpoint, it:

  • Extracts all path parameters, query strings, and body fields that are numeric or can be coerced to a number.
  • Generates a series of probes: negative numbers, zero, very large integers (e.g., 2^32‑1), and values that exceed typical buffer sizes.
  • Sends each probe to the endpoint and monitors the response:
    • HTTP 500 with a RangeError or stack trace indicates an unhandled out‑of‑bounds read.
    • A successful response that returns unexpected data (e.g., extra null bytes, repeated patterns) can signal a silent over‑read via Buffer.slice or similar.
    • Changes in response length or timing that correlate with the probe value suggest a length‑driven read.
  • Correlates findings with the 12 security checks, tagging the issue under Input Validation and, when applicable, Data Exposure.

For the first example (/secret/{index}), middleBrick would detect that supplying -1 or 100 triggers a 500 error with a RangeError: Offset is out of range message. For the binary payload example, it would notice that increasing the declared length field beyond the actual payload size returns a response padded with zeros, and that the response length grows linearly with the injected length—behaviour inconsistent with a properly bounded slice.

Because middleBrick does not require agents, credentials, or configuration, a security engineer can simply point the scanner at the base URL of the Hapi service (e.g., https://api.example.com) and receive a prioritized finding that includes:

  • The exact URL and HTTP method probed.
  • The parameter name (index or len) that caused the over‑read.
  • Severity rating (typically Medium) based on the potential for information disclosure or denial‑of‑service.
  • Remediation guidance that references Hapi‑specific validation techniques.

This automated check complements manual code review and ensures that any Hapi endpoint exposing buffer or array accesses is continuously validated throughout the development lifecycle.

Hapi-Specific Remediation

Fixing out‑of‑bounds reads in Hapi centers on validating all user‑supplied numeric values before they are used to index or slice buffers. Hapi’s built‑in validation (via Joi) makes this straightforward and keeps the validation logic close to the route definition.

**1. Validate path parameters with Joi**

Replace the raw Number(request.params.index) conversion with a Joi schema that enforces a valid range:

const Joi = require('@hapi/joi');

server.route({
  method: 'GET',
  path: '/secret/{index}',
  options: {
    validate: {
      params: Joi.object({
        index: Joi.number().integer().min(0).max(31).required()
      })
    }
  },
  handler: (request, h) => {
    const idx = request.params.index; // already validated and cast to number
    const byte = secret.readUInt8(idx);
    return { value: byte };
  }
});

If the supplied index falls outside 0‑31, Hapi returns a 400 Bad Request before the handler runs, preventing the RangeError.

**2. Validate payload‑driven lengths**

For binary payloads, validate the length field against the actual buffer size:

server.route({
  method: 'POST',
  path: '/parse',
  options: {
    validate: {
      payload: Joi.binary().max(1024).required() // limit overall size
    }
  },
  handler: (request, h) => {
    const buf = request.payload;
    if (buf.length < 2) {
      return h.response('Payload too short').code(400);
    }
    const len = buf.readUInt16BE(0);
    // Ensure length does not exceed available bytes
    if (len > buf.length - 2) {
      return h.response('Invalid length field').code(400);
    }
    const slice = buf.slice(2, 2 + len);
    return slice.toString('utf8');
  }
});

Here, the handler explicitly checks that len is not greater than buf.length - 2. If the check fails, a 400 response is returned, blocking the over‑read.

**3. Use safe alternatives when possible**

When the goal is to extract a substring from a Buffer, consider using buf.toString('utf8', start, end) which internally validates the bounds and throws a clear error if they are invalid. Wrapping the call in a try/catch allows you to convert the error into a user‑friendly 400 response.

try {
  const text = buf.toString('utf8', 2, 2 + len);
  return h.response(text);
} catch (err) {
  if (err instanceof RangeError) {
    return h.response('Invalid range').code(400);
  }
  throw err;
}

**4. Leverage Hapi’s lifecycle extensions for centralized validation**

If many routes share similar validation rules, you can register a onPreHandler extension that inspects request.params or request.query for numeric fields and applies a common schema. This reduces duplication and ensures consistent protection across the API.

By applying these Hapi‑native validation techniques—Joi schemas for path/query/payload, explicit bounds checks before Buffer operations, and safe API usage—you eliminate the root cause of out‑of‑bounds reads. The fixes are straightforward, do not require external libraries, and align with the principle that middleBrick only detects and reports; it is the developer’s responsibility to remediate using the platform’s built‑in security features.

Frequently Asked Questions

Can middleBrick fix an out‑of‑bounds read vulnerability in my Hapi API?
No. middleBrick only detects and reports the issue, providing detailed findings and remediation guidance. It does not modify code, apply patches, or block requests. You must implement the recommended fixes—such as adding Joi validation or explicit bounds checks—in your Hapi source.
What severity does middleBrick assign to an out‑of‑bounds read finding in Hapi?
The severity is typically rated as Medium. While the vulnerability can lead to information disclosure or denial‑of‑service, it generally does not allow arbitrary code execution. The exact rating may vary based on the context (e.g., whether exposed data includes secrets or credentials).