top of page

Gemma Verified Group

Public·66 members

Ricardo Garrett
Ricardo Garrett

Node Js Php Serialize And Unserialize



// using node v14.15.3const unserialize = require('php-serialize');const a = unserialize('a:3:i:19694;a:5:s:16:"ignore_ignore_id";s:5:"19694";s:15:"ignore_messages";s:1:"1";s:17:"ignore_signatures";s:1:"1";s:13:"ignore_topics";s:1:"1";s:12:"ignore_chats";s:1:"0";i:25895;a:5:s:16:"ignore_ignore_id";s:5:"25895";s:15:"ignore_messages";s:1:"0";s:17:"ignore_signatures";s:1:"0";s:13:"ignore_topics";s:1:"0";s:12:"ignore_chats";s:1:"1";i:19917;a:5:s:16:"ignore_ignore_id";s:5:"19917";s:15:"ignore_messages";s:1:"1";s:17:"ignore_signatures";s:1:"0";s:13:"ignore_topics";s:1:"0";s:12:"ignore_chats";s:1:"0";');const b = Object.values(a).filter(v => (v.ignore_chats === '1')).map(v => v.ignore_ignore_id);console.log(a); /* prints out: '19694': ignore_ignore_id: '19694', ignore_messages: '1', ignore_signatures: '1', ignore_topics: '1', ignore_chats: '0' , '19917': ignore_ignore_id: '19917', ignore_messages: '1', ignore_signatures: '0', ignore_topics: '0', ignore_chats: '0' , '25895': ignore_ignore_id: '25895', ignore_messages: '0', ignore_signatures: '0', ignore_topics: '0', ignore_chats: '1' */console.log(b)/* prints out[ '25895' ]*/




Node Js Php Serialize And Unserialize



Otherwise I will have to load all the rows, loop them and unserialize them and assign them to a temporary PHP array and then json_encode it back to JavaScript which seems highly inefficient if I can send the data still serialized so that JavaScript can unserialize the data when it needs to.


When an input to the parse method references a class, then it will first check if you passed a reference to that class in the (optional) second argument. If not, a mock will be created for that class (to avoid undesired side-effects). In either case an instance of that class will be created. If the input string specifies a custom serialization happened then the method unserialize on that object instance will be called. You must provide the logic in that method as the string itself does not give information about how that should be done. It is only known in the PHP code that generated that string.


const PHP = stdClass: function() , stringify(val) const hash = new Map([[Infinity, "d:INF;"], [-Infinity, "d:-INF;"], [NaN, "d:NAN;"], [null, "N;"], [undefined, "N;"]]); const utf8length = str => str ? encodeURI(str).match(/(%.)?./g).length : 0; const serializeString = (s,delim='"') => `$utf8length(s):$delim[0]$s$delim[delim.length-1]`; let ref = 0; function serialize(val, canReference = true) return serialize(val); , // Provide in second argument the classes that may be instantiated // e.g. MyClass1, MyClass2 parse(str, allowedClasses = ) allowedClasses.stdClass = PHP.stdClass; // Always allowed. let offset = 0; const values = [null]; const specialNums = "INF": Infinity, "-INF": -Infinity, "NAN": NaN ; const kick = (msg, i = offset) => throw new Error(`Error at $i: $msg\n$str\n$" ".repeat(i)^`) const read = (expected, ret) => expected === str.slice(offset, offset+=expected.length) ? ret : kick(`Expected '$expected'`, offset-expected.length); function readMatch(regex, msg, terminator=";") read(":"); const match = regex.exec(str.slice(offset)); if (!match) kick(`Exected $msg, but got '$str.slice(offset).match(/^[:;]'`); offset += match[0].length; return read(terminator, match[0]); function readUtf8chars(numUtf8Bytes, terminator="") const i = offset; while (numUtf8Bytes > 0) code>>11 === 0x1B ? 2 : 3; return numUtf8Bytes ? kick("Invalid string length", i-2) : read(terminator, str.slice(i, offset)); const create = className => !className ? : allowedClasses[className] ? Object.create(allowedClasses[className].prototype) : new [className]: function() [className]; // Create a mock class for this name const readBoolean = () => readMatch(/^[01]/, "a '0' or '1'", ";"); const readInt = () => +readMatch(/^-?\d+/, "an integer", ";"); const readUInt = terminator => +readMatch(/^\d+/, "an unsigned integer", terminator); const readString = (terminator="") => readUtf8chars(readUInt(':"'), '"'+terminator); function readDecimal() const num = readMatch(/^-?(\d+(\.\d+)?(E[+-]\d+)? function readKey() const typ = str[offset++]; return typ === "s" ? readString(";") : typ === "i" ? readUInt(";") : kick("Expected 's' or 'i' as type for a key, but got $str[offset-1]", offset-1); function readObject(obj) for (let i = 0, length = readUInt(":"); i key != i) ? obj : Object.values(obj); function readCustomObject(obj) if (typeof obj.unserialize !== "function") kick(`Instance of $obj.constructor.name does not have an "unserialize" method`); obj.unserialize(readUtf8chars(readUInt(":"))); return read("", obj); function readValue() const typ = str[offset++].toLowerCase(); const ref = values.push(null)-1; const val = typ === "n" ? read(";", null) : typ === "s" ? readString(";") : typ === "b" ? readBoolean() : typ === "i" ? readInt() : typ === "d" ? readDecimal() : typ === "a" ? readArray() // Associative array : typ === "o" ? readObject(create(readString())) // Object : typ === "c" ? readCustomObject(create(readString())) // Custom serialized object : typ === "r" ? values[readInt()] // Backreference : kick(`Unexpected type $typ`, offset-1); if (typ !== "r") values[ref] = val; return val; const val = readValue(); if (offset !== str.length) kick("Unexpected trailing character"); return val; /**************** EXAMPLE USES ************************/// Unserialize a sequential arrayconsole.log(PHP.parse('a:4:i:0;s:4:"This";i:1;s:2:"is";i:2;s:2:"an";i:3;s:5:"array";'));// Unserialize an associative array into an objectconsole.log(PHP.parse('a:2:s:8:"language";s:3:"PHP";s:7:"version";d:7.1;'));// Example with class that has custom serialize function:var MyClass = (function () const priv = new WeakMap(); // This is a way to implement private properties in ES6 return class MyClass constructor() priv.set(this, ""); this.wordCount = 0; unserialize(serialised) const words = PHP.parse(serialised); priv.set(this, words); this.wordCount = words.split(" ").length; serialize() return PHP.stringify(priv.get(this)); )();// Unserialise a PHP string that needs the above class to work, and will call its unserialize method// The class needs to be passed as object key/value as second argument, so to allow this side effect to happen:console.log(PHP.parse('C:7:"MyClass":23:s:15:"My private data";', MyClass ));


Untrusted data passed into unserialize() function in node-serialize module can be exploited to achieve arbitrary code execution by passing a serialized JavaScript Object with an Immediately invoked function expression (IIFE).


During a Node.js code review, I happen to see a serialization/deserialization module named node-serialize. A cookie value that comes from the request was passed into the unserialize() function provided by the module. Here is a sample node.js application to imitate the code:


I have used node-serialize version 0.0.4 for this research. For successful exploitation, arbitrary code execution should occur when untrusted input is passed into unserialize() function. The best way to create a payload is to use the serialize() function of the same module.


The following output was obtainedThe IIFE worked fine but the serialization failed. So I tried adding bracket () after the function body of the previously serialized string and passed it to unserialize() function and lucky it worked. So we have the exploit payload:


The vulnerability in the web application is that it reads a cookie named profile from the HTTP request, perform base64 decode of the cookie value and pass it to unserialize()function. As cookie is an untrusted input, an attacker can craft malicious cookie value to exploit this vulnerability.I used nodejsshell.py for generating a reverse shell payload.


We exploited a deserialization bug to achieve arbitrary code execution with untrusted user input. The Rule of thumb is never to deserialize untrusted user input. The root cause is that it was using eval() internally for deserialization. I also found a similar bug in another module named serialize-to-js. In that module, the require() function in Node.js has no scope during deserialization of an object with IIFE and they were using new Function() internally for deserialization. We can still achieve code execution with a slightly complex payload.


In PHP, the complex data can not be transported or can not be stored. If you want to execute continuously a complex set of data beyond a single script then this serialize() and unserialize() functions are handy to deal with those complex data structures. The serialize() function is just given a compatible shape to a complex data structure that the PHP can handle that data after that you can reverse the work by using the unserialize() function.


About

Welcome to the group! You can connect with other members, ge...

Members

bottom of page