【BUUCTF】【WEB】[HITCON 2016]Leaking
2026/6/12 23:25:39 网站建设 项目流程

考点:Node.js Buffer 未初始化内存泄露漏洞、代码执行长度限制、内存读取

打开题目,看到是一堆代码,而这些代码里不仅有python,还有C++,Javascript,而这些元素组合起来就是完整的 Node.js 源码。

Node.js 源码是什么?

Node.js 源码是开源跨平台 JavaScript 运行时环境的实现代码,它不是单一语言编写的,而是由C++(约 60%)、JavaScript(约 30%)和少量 Python(构建工具)混合组成,核心目标是让 JavaScript 能在服务器端运行。

这些代码不用完全看懂,直接看向带flag的那行代码:

eval("var flag_" + randomstring.generate(64) + " = \"hitcon{" + flag + "}\";")

对其进行部分拆解:

最核心:randomstring.generate(64)

  • 作用:生成一个 64 位的完全随机字符串
  • 例子:每次运行都会生成不一样的,比如第一次是abc123def456...,第二次是xyz789uvw012...
  • 目的:让变量名永远无法被猜到

2. 字符串拼接:"var flag_" + 随机字符串+ " = \"hitcon{" + flag + "}\";"

+号把几个字符串拼在一起,最终会变成一行完整的 JavaScript 代码。

举个具体的例子

  • 假设randomstring.generate(64)生成了abc123
  • 假设真正的 flag 是hitcon{123456}
  • 拼接后的结果就是:
    "var flag_abc123 = \"hitcon{123456}\";"

3. 最关键:eval(...)

  • 作用:把括号里的字符串,当成真正的 JavaScript 代码来执行
  • 上面例子中,eval 执行后,就相当于在程序里写了这么一行代码:
    var flag_abc123 = "hitcon{123456}";

先搞懂 3 个最基础的概念

1. 计算机内存到底是什么?

内存是计算机的 "临时工作台",所有正在运行的程序和数据都存在这里。

  • 内存本质上是一个巨大的字节数组,每个字节有一个唯一的编号,叫做 "内存地址"
  • 每个字节可以存储一个 0-255 之间的数字
  • 所有的文字、图片、代码、flag,最终都会被转换成数字存在内存里

类比:内存就像一个有 10 亿个格子的储物柜,每个格子有一个编号,每个格子能放一张写着数字的小纸条。

2. 什么是 "内存分配"?

程序运行时需要存东西,就必须向操作系统 "申请" 一块内存,这个过程就叫内存分配

  • 操作系统有一个 "内存管理员",负责记录哪些格子已经被占用,哪些是空的
  • 程序说:"我要 800 个格子存东西"
  • 内存管理员找到连续 800 个空格子,把第一个格子的编号告诉程序
  • 程序就可以往这 800 个格子里写数据了

3. 两个最关键的内存分配函数:mallocvscalloc

这是整个漏洞的核心中的核心。

函数作用行为类比
malloc(size)分配size个字节的内存只分配格子,不擦除格子里原来的纸条租一个储物柜,管理员只给你钥匙,不打扫里面的东西,上一个租客留下的纸条还在
calloc(count, size)分配count * size个字节的内存分配格子,并且把所有格子里的纸条都擦成 0租一个储物柜,管理员会把里面打扫得干干净净,所有格子都是空的

malloc分配的内存是 "脏的",里面有之前的数据;calloc分配的内存是 "干净的",全是 0。

在 Node.js 8.0 之前,当你写

Buffer(800)

时,底层会执行这样的 C++ 代码:

// 只做一件事:调用malloc分配800个字节的脏内存 char* data = malloc(800); // 没有任何清零操作 return Buffer::New(data, 800);
  • 它用的是 **malloc**,所以分配到的内存里全是之前残留的数据
  • 这就是为什么叫AllocUnsafe(不安全分配),这就是漏洞的产生

总结:当服务器收到你的请求,执行Buffer(800)后,底层调用malloc(800)向内存管理员申请 800 个连续的格子,内存管理员在空闲格子里找,正好找到了刚才存放 flag 的那 800 个格子,把这 800 个格子的起始地址返回给 Buffer。

漏洞原理:

  1. 当你在 JS 中执行Buffer(800)时,会调用上面的Buffer::New函数
  2. AllocUnsafe使用malloc分配 800 字节的内存
  3. malloc 只分配内存,不初始化内容,内存中保留着之前的数据
  4. 之前eval("var flag_xxxx = 'hitcon{...}'")执行时,flag 被写入了内存
  5. 当新分配的 Buffer 正好覆盖了之前存放 flag 的内存区域时,就能读取到 flag

而代码里明确写了参数名是data:

if (req.query.data && req.query.data.length <= 12) {

因此我们可以得出这样的paylaod。

?data=Buffer(800)

把它拼接到靶机地址后面:

每运行一次就会下载一个文件,等下载到第三四次的时候就会看到flag(其实我之前一直在尝试用自动化脚本找到内存里的flag,但是一直出问题,所以才下载了这么多文件......最后只能改手动了)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询