Skip to content

Latest commit

 

History

History
92 lines (66 loc) · 3.45 KB

File metadata and controls

92 lines (66 loc) · 3.45 KB

Node.js is Web Scale

题解作者:taoky

出题人、验题人、文案设计等:见 Hackergame 2024 幕后工作人员

题目描述

  • 题目分类:web

  • 题目分值:200

小 Q 最近在写 Node.js,需要一个键值数据库来存储数据。

众所周知,其他的数据库无论如何都需要 write to disk,所以它们 don't scale。直接写到 /dev/null 嘛,虽然性能很好,但是好像就拿不到写入的数据了。基于这个想法,小 Q 利用最新最热的还没跑路的大语言模型,生成了一段内存数据库的 Node.js 代码,绝对 web scale!

注:

  • 如果你在好奇标题是什么意思,可以搜索一个标题叫 "Mongo DB Is Web Scale" 的视频(虽然与本题解法无关)。

  • flag 在 /flag 文件中。

  • 点击下面的「打开/下载题目」按钮会为你创建一个独立的题目环境,有效时间一小时。如果环境遇到问题,可以 关闭环境 后再试。

打开/下载题目

题解

这是一道简单的 Node.js 原型链污染的题目。JavaScript 语言的初学者可能很难意识到的是,JavaScript 中 {} 不仅仅是一个字典/哈希表,它事实上是个对象,因此它有一些意料之外的属性。原型链污染就和 Object.prototype.__proto__ 这个属性有关。从效果上讲,通过 __proto__ 属性,可以给对象的的属性设置默认值:

$ node
Welcome to Node.js v23.1.0.
Type ".help" for more information.
> a = {}
{}
> a.__proto__.test = 114
114
> b = {}
{}
> b.test
114

而且可能让其他语言的程序员感到反直觉的是,这样也是可以的:

$ node
Welcome to Node.js v23.1.0.
Type ".help" for more information.
> a = {}
{}
> a["__proto__"]["test"] = 114
114
> b = {}
{}
> b["test"]
114

如果用户可以任意控制这类赋值的话,就构成了原型链污染漏洞。本题也符合这个条件。观察 /set

const keys = key.split(".");
let current = store;

for (let i = 0; i < keys.length - 1; i++) {
  const key = keys[i];
  if (!current[key]) {
    current[key] = {};
  }
  current = current[key];
}

// Set the value at the last key
current[keys[keys.length - 1]] = value;

可以发现它会将用户的 key 输入按 . 分割,然后一个循环操作 current。如果 key__proto__.a 的话,结果就是 current["__proto__"]["a"] 被设定上了用户给定的值。而本题中,执行命令参考的表长这样:

let cmds = {
  getsource: "cat server.js",
  test: "echo 'hello, world!'",
};

这是一个对象,因此会被原型链污染问题影响。接下来怎么做应该不用多介绍了。

JavaScript 中真正的「字典」是 Map。JS 引擎可能会实现成哈希表或者搜索树结构。

此外,JSON.parse() 函数可以正确处理 __proto__ 作为键的情况。但是对 JSON.parse() 的输出做处理时,程序员仍然需要特别注意。