JavaScript has no built-in object-serialization format like PHP or Java, so Node deserialization bugs live in third-party libraries that serialize and revive functions. The flagship is node-serialize: its unserialize() rebuilds a serialized function and, through an immediately-invoked function expression, runs it at load time, making the sink a direct RCE primitive.
node-serialize#
node-serialize encodes functions with a _$$ND_FUNC$$_ marker. On unserialize(), a value tagged as a function is passed through eval. Appending () to the function body turns it into an IIFE that executes during deserialization:
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id',function(e,o){console.log(o)});}()"}
The trailing () before the closing quote is the trigger: the revived function invokes itself immediately. Anything calling serialize.unserialize(userInput) on this runs the command. require('child_process') is reachable because the eval runs in module scope here (unlike a bare Function constructor, which is global scope).
Deliver the JSON wherever the app deserializes it: cookies, bodies, cache entries, or queue messages.
Other libraries#
funcster: reconstructs functions in a sandboxed module; escape patterns reachthis.constructor.constructor('return process')()to regainprocessandrequire.serialize-javascript: primarily a serializer, but round-tripping its output througheval-based revival in application code reintroduces the function-execution sink.cryo,node-cryo: object graph serializers that can revive functions.
Exploitation notes#
- Confirm the library and that your input reaches its
unserialize/revive call; a plainJSON.parseis not vulnerable (it never revives functions). - For output,
child_process.exec's callback does not return to you over HTTP; prefer an OOB callback (DNS/HTTP) or a reverse shell. - If the revival uses the
Functionconstructor (global scope) rather thaneval(module scope), reach a loader viaprocess.mainModule.require('child_process')instead of a barerequire.
Tools#
- Hand-crafted
_$$ND_FUNC$$_payloads.
References#
- PortSwigger Web Security Academy: Insecure deserialization
- node-serialize advisory writeups