HIGH adonisjsdeserialization attack

Deserialization Attack in Adonisjs

How Deserialization Attack Manifests in Adonisjs

Adonisjs applications are vulnerable to deserialization attacks when they deserialize untrusted data using JavaScript's built-in mechanisms without proper validation. A common pattern occurs in API endpoints that accept serialized objects via HTTP requests, such as JSON payloads that are later processed with unsafe methods. For example, if an endpoint uses JSON.parse() on user-controlled input and then passes the result to a function that evaluates or instantiates objects based on class names or prototypes, it may lead to remote code execution.

In Adonisjs, this risk is heightened when developers use custom serialization/deserialization logic in models or services that rely on the constructor property or __proto__ manipulation. Attackers can craft malicious payloads that exploit prototype pollution or invoke dangerous methods during deserialization. For instance, a route that handles user preferences might deserialize a settings object from a cookie or header without validating its structure, allowing an attacker to inject a payload that alters object behavior.

Real-world parallels include CVE-2018-16487 in lodash and CVE-2019-5418 in Rails, where unsafe deserialization led to server-side compromise. In Adonisjs, similar risks exist when using third-party libraries that extend Object.prototype or when custom toJSON/fromJSON methods are implemented without safeguards.

Adonisjs-Specific Detection

Detecting deserialization flaws in Adonisjs requires analyzing both static code patterns and runtime behavior. middleBrick identifies these vulnerabilities through its Input Validation and Data Exposure checks, which scan for endpoints that consume user-controlled data and reflect it in responses or internal processing without adequate sanitization. The scanner sends crafted payloads designed to trigger prototype pollution or unsafe object reconstruction, monitoring for anomalous responses that indicate successful exploitation.

For example, middleBrick may send a JSON payload containing {\"__proto__\":{\"isAdmin\":true}} to an endpoint that merges user input into a configuration object. If the application reflects elevated privileges in subsequent responses or behaves anomalously, it signals a deserialization or prototype pollution issue. The scanner also checks for error messages that reveal stack traces or internal object structures, which can aid attackers in refining exploits.

Developers can manually inspect routes that use request.body(), request.header(), or cookie parsers where data is passed to utility functions like Object.assign(), _.merge() (from lodash), or custom model hydration methods. Look for missing validation schemas or the absence of libraries like @adonisjs/validator to enforce strict input shapes.

Adonisjs-Specific Remediation

To mitigate deserialization risks in Adonisjs, avoid using unsafe merging patterns and instead rely on strict data validation. Use the built-in @adonisjs/validator to define explicit schemas for incoming data, ensuring only expected properties are accepted. For example, when updating a user profile, define a schema that permits only username, email, and bio, rejecting any extra or prototype-modifying fields.

// controller/updateUser.js
const { schema, rules } = require('@adonisjs/validator')

async update ({ request, response, auth }) {
  const userSchema = schema.create({
    username: schema.string.optional({}, [rules.alphaNum(), rules.maxLength(25)]),
    email: schema.string.optional({}, [rules.email(), rules.maxLength(255)]),
    bio: schema.string.optional({}, [rules.maxLength(160)])
  })

  const payload = await request.validate({ schema: userSchema })
  const user = await auth.getUser()
  user.merge(payload)
  await user.save()

  return response.json({ message: 'Profile updated' })
}

This approach prevents prototype pollution by ignoring any properties not explicitly defined in the schema, including __proto__ or constructor.

Additionally, avoid deep merging libraries like lodash.merge with user input. If such functionality is necessary, sanitize the input first by stripping dangerous keys. For instance, use a utility function to recursively remove __proto__ and prototype properties before merging.

// utils/safeMerge.js
function safeMerge(target, source) {
  if (typeof source !== 'object' || source === null) return source
  const safeSource = {}
  for (const [key, value] of Object.entries(source)) {
    if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue
    safeSource[key] = typeof value === 'object' && value !== null ? safeMerge({}, value) : value
  }
  return Object.assign(target, safeSource)
}
module.exports = { safeMerge }

Finally, leverage middleBrick’s continuous monitoring (available in Pro and Enterprise tiers) to regularly scan endpoints for regression. By integrating the GitHub Action or CLI into your CI/CD pipeline, you can automatically detect unsafe deserialization patterns before deployment, ensuring that only validated, schema-compliant data enters your application logic.

Frequently Asked Questions

Can middleBrick detect deserialization vulnerabilities in Adonisjs applications without source code access?
Yes, middleBrick performs black-box testing by sending crafted payloads to API endpoints and analyzing responses for signs of successful exploitation, such as prototype pollution indicators or anomalous behavior, without needing source code or credentials.
Is using JSON.parse() alone in Adonisjs sufficient to lead to a deserialization attack?
Not inherently. JSON.parse() is safe for parsing JSON strings into plain objects. Risk arises when the parsed object is later used in unsafe operations like deep merging with user input, prototype manipulation, or dynamic instantiation based on object properties.