Skip to content

huntr.dev - Denial of Service (DoS) Fix #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
349 changes: 154 additions & 195 deletions php-unserialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,183 +5,149 @@
exports.unserialize = unserialize;
exports.unserializeSession = unserializeSession;

/**
* Unserialize data taken from PHP's serialize() output
*
* Taken from https://github.com/kvz/phpjs/blob/master/functions/var/unserialize.js
* Fixed window reference to make it nodejs-compatible
*
* @param string serialized data
* @return unserialized data
* @throws
*/
function unserialize (data) {
// http://kevin.vanzonneveld.net
// + original by: Arpad Ray (mailto:[email protected])
// + improved by: Pedro Tainha (http://www.pedrotainha.com)
// + bugfixed by: dptr1988
// + revised by: d3x
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Chris
// + improved by: James
// + input by: Martin (http://www.erlenwiese.de/)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Le Torbi
// + input by: kilops
// + bugfixed by: Brett Zamir (http://brett-zamir.me)
// + input by: Jaroslaw Czarniak
// % note: We feel the main purpose of this function should be to ease the transport of data between php & js
// % note: Aiming for PHP-compatibility, we have to translate objects to arrays
// * example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}');
// * returns 1: ['Kevin', 'van', 'Zonneveld']
// * example 2: unserialize('a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}');
// * returns 2: {firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}
var that = this,
utf8Overhead = function (chr) {
// http://phpjs.org/functions/unserialize:571#comment_95906
var code = chr.charCodeAt(0);
if (code < 0x0080) {
return 0;
}
if (code < 0x0800) {
return 1;
var utf8Overhead = function (chr) {
// http://phpjs.org/functions/unserialize:571#comment_95906
var code = chr.charCodeAt(0);
if (code < 0x0080) {
return 0;
}
if (code < 0x0800) {
return 1;
}
return 2;
},
error = function (type, msg, filename, line) {
throw new window[type](msg, filename, line);
},
read_until = function (data, offset, stopchr) {
var i = 2, buf = [], chr = data.slice(offset, offset + 1);

while (chr != stopchr) {
if ((i + offset) > data.length) {
error('Error', 'Invalid');
}
return 2;
},
error = function (type, msg, filename, line) {
throw new window[type](msg, filename, line);
},
read_until = function (data, offset, stopchr) {
var i = 2, buf = [], chr = data.slice(offset, offset + 1);

while (chr != stopchr) {
if ((i + offset) > data.length) {
error('Error', 'Invalid');
buf.push(chr);
chr = data.slice(offset + (i - 1), offset + i);
i += 1;
}
return [buf.length, buf.join('')];
},
read_chrs = function (data, offset, length) {
var i, chr, buf;

buf = [];
for (i = 0; i < length; i++) {
chr = data.slice(offset + (i - 1), offset + i);
buf.push(chr);
length -= utf8Overhead(chr);
}
return [buf.length, buf.join('')];
},
_unserialize = function (data, offset) {
var dtype, dataoffset, keyandchrs, keys,
readdata, readData, ccount, stringlength,
i, key, kprops, kchrs, vprops, vchrs, value,
chrs = 0,
typeconvert = function (x) {
return x;
},
readArray = function () {
readdata = {};

keyandchrs = read_until(data, dataoffset, ':');
chrs = keyandchrs[0];
keys = keyandchrs[1];
dataoffset += chrs + 2;

for (i = 0; i < parseInt(keys, 10); i++) {
kprops = _unserialize(data, dataoffset);
kchrs = kprops[1];
key = kprops[2];
dataoffset += kchrs;

vprops = _unserialize(data, dataoffset);
vchrs = vprops[1];
value = vprops[2];
dataoffset += vchrs;

readdata[key] = value;
}
buf.push(chr);
chr = data.slice(offset + (i - 1), offset + i);
i += 1;
}
return [buf.length, buf.join('')];
},
read_chrs = function (data, offset, length) {
var i, chr, buf;

buf = [];
for (i = 0; i < length; i++) {
chr = data.slice(offset + (i - 1), offset + i);
buf.push(chr);
length -= utf8Overhead(chr);
}
return [buf.length, buf.join('')];
},
_unserialize = function (data, offset) {
var dtype, dataoffset, keyandchrs, keys,
readdata, readData, ccount, stringlength,
i, key, kprops, kchrs, vprops, vchrs, value,
chrs = 0,
};

if (!offset) {
offset = 0;
}
dtype = (data.slice(offset, offset + 1)).toLowerCase();

dataoffset = offset + 2;

switch (dtype) {
case 'i':
typeconvert = function (x) {
return x;
},
readArray = function () {
readdata = {};

keyandchrs = read_until(data, dataoffset, ':');
chrs = keyandchrs[0];
keys = keyandchrs[1];
dataoffset += chrs + 2;

for (i = 0; i < parseInt(keys, 10); i++) {
kprops = _unserialize(data, dataoffset);
kchrs = kprops[1];
key = kprops[2];
dataoffset += kchrs;

vprops = _unserialize(data, dataoffset);
vchrs = vprops[1];
value = vprops[2];
dataoffset += vchrs;

readdata[key] = value;
}
return parseInt(x, 10);
};

if (!offset) {
offset = 0;
}
dtype = (data.slice(offset, offset + 1)).toLowerCase();

dataoffset = offset + 2;

switch (dtype) {
case 'i':
typeconvert = function (x) {
return parseInt(x, 10);
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'b':
typeconvert = function (x) {
return parseInt(x, 10) !== 0;
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'd':
typeconvert = function (x) {
return parseFloat(x);
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'n':
readdata = null;
break;
case 's':
ccount = read_until(data, dataoffset, ':');
chrs = ccount[0];
stringlength = ccount[1];
dataoffset += chrs + 2;

readData = read_chrs(data, dataoffset + 1, parseInt(stringlength, 10));
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 2;
if (chrs != parseInt(stringlength, 10) && chrs != readdata.length) {
error('SyntaxError', 'String length mismatch');
}
break;
case 'a':
readArray();
dataoffset += 1;
break;
case 'o':
ccount = read_until(data, dataoffset, ':');
dataoffset += ccount[0] + 2;

ccount = read_until(data, dataoffset, '"');
dataoffset += ccount[0] + 2;

readArray();
dataoffset += 1;
break;
default:
error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
break;
}
return [dtype, dataoffset - offset, typeconvert(readdata)];
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'b':
typeconvert = function (x) {
return parseInt(x, 10) !== 0;
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'd':
typeconvert = function (x) {
return parseFloat(x);
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'n':
readdata = null;
break;
case 's':
ccount = read_until(data, dataoffset, ':');
chrs = ccount[0];
stringlength = ccount[1];
dataoffset += chrs + 2;

readData = read_chrs(data, dataoffset + 1, parseInt(stringlength, 10));
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 2;
if (chrs != parseInt(stringlength, 10) && chrs != readdata.length) {
error('SyntaxError', 'String length mismatch');
}
break;
case 'a':
readArray();
dataoffset += 1;
break;
case 'o':
ccount = read_until(data, dataoffset, ':');
dataoffset += ccount[0] + 2;

ccount = read_until(data, dataoffset, '"');
dataoffset += ccount[0] + 2;

readArray();
dataoffset += 1;
break;
default:
error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
break;
}
;
return [dtype, dataoffset - offset, typeconvert(readdata)];
};

function unserialize (data) {
return _unserialize((data + ''), 0)[2];
}

Expand All @@ -192,30 +158,23 @@ function unserialize (data) {
* @return unserialized data
* @throws
*/
function unserializeSession (input) {
return input.split(/\|/).reduce(function (output, part, index, parts) {
// First part = $key
if (index === 0) {
output._currKey = part;
}
// Last part = $someSerializedStuff
else if (index === parts.length - 1) {
output[output._currKey] = unserialize(part);
delete output._currKey;
}
// Other output = $someSerializedStuff$key
else {
var repper = part.replace(/(\n|\r)/g," ");
var match = repper.match(/^((?:.*?[;\}])+)([^;\}]+?)$/);
if (match) {
output[output._currKey] = unserialize(match[1]);
output._currKey = match[2];
} else {
throw new Error('Parse error on part "' + part + '"');
}
}
return output;
}, {});
function unserializeSession(data) {
var pos = 0;
var ret = { };
do {
var key = '';
var c = '|';
while(data.length > pos && (c = data.charAt(pos)) != '|') {
key += c; pos++;
}
if (key == '' || key == "\r" || key == "\n") break; // eof
pos++; // skip '|'
var r = _unserialize(data, pos);
if (r[1] == 0) return null; // parser stuck
pos += r[1];
ret[key] = r[2];
} while (pos < data.length);
return ret;
}

// /Wrapper
Expand Down