node.js express框架基础 原型链污染

node.js express框架基础 原型链污染

前言:在打hackergame 2024中遇到了原型链污染的题,但不会nodejs,打算学一学然后将其做了

1.0 安装环境

由于我们需要学习的是express 框架,所以首先要先安装好所需的包

首先自然是先安装node.js 这个去官网下载即可

安装好后检测下npm 和node 指令是否可以使用,然后去安装框架

 npm install express --save
 npm install body-parser --save
 npm install cookie-parser --save
 npm install multer --save

检测是否可以正常运行,我们来运行一下下面的代码

var express = require('express');
var app = express();

app.get('/', function (req, res) {
   res.send('Hello World');
})

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port

  console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

在shell里 node 文件名

访问本地地址的8081端口,检测是否可以正常运行

1.1框架加载

var express = require('express');
var app = express();

这两行代码的作用是加载express框架并新建一个express应用

1.2 路由操作

app.get('/', function (req, res) {
   res.send('Hello World');
})

该操作设置了一个路由,用于处理get请求,当用户访问'/'也就是根目录时,会执行第二个参数,也就

是回调函数里的内容,回调函数将一个字符串发回了客户端,如果我们想要发送文件,就要使用

sendFile了

比如:

app.get('/login',function(req,res){
    res.sendFile(__dirname+'/login.html')
})

发送的内容是文件地址, __dirname 是代码文件的地址 需要注意的是,如果发送html文件则会被浏览

器渲染,浏览器会根据发送数据的MME类型来决定是渲染显示还是下载

如果有前端发送数据到网页里,我们也可以用get函数获得get请求发送的内容

app.get('/login',function(req,res){
    u=req.query.username;
    p=req.query.password;
   // res.sendFile(__dirname+'/login.html');
});

query对应的是get请求,在url中的数据。后面的username 和password为发送数据的name 比如表单

中的name或者查询字符串中的数据的键

如果我们想要接受post发送的数据呢?

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.get('/', function (req, res) {
   res.send('Hello World');
})
app.post('/login', urlencodedParser, function (req, res){
    req.send(req.body.username);
    res.send(req.body.password);

})
var server = app.listen(8081, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log("应用实例,访问地址为 http://%s:%s", host, port);

})

文件遍历:

const fs=require("fs");
fs.readdir("./",function (error,files){
    console.log(files);
});

可以获得当前目录下的文件

1.3 命令执行和代码执行

先看命令执行:

const ch=require('child_process');
ch.exec("calc");

或者这样:

const ch=require('child_process');
ch.spawn("calc");

再看代码执行

eval("console.log(1);");

直接使用eval函数可以做到代码执行

1.4 原型链污染

在js中每一个类都有一个原型,每个对象都有一个内部属性 [[Prototype]],通常可以通过

__proto__ 属性访问。这个属性指向该对象的原型对象。原型对象本身也可能有自己的原型对象,这

样就形成了一个原型链。

但是当我们使用字面量 {} 创建对象时,这些对象的原型默认指向 Object.prototype

Object.prototype 是所有对象的根原型,它包含了一些通用的方法和属性,如 toString

valueOf 等。

需要注意的是,当我们调用一个对象的属性时,会先查找本身的属性,如果自身没有就会沿着原型链查

找,知道找到该属性,这就产生了原型链污染漏洞,我们通过更改原型的属性来污染其他对象,看一个

简单的例子:

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

通过更改a的原型的属性,成功污染b的属性值

我们再转过来看hackergame 2024的node.js的题目,这就是一道原型链污染的题目

不过这个题和往常有点不一样,我们需要注意,对象的属性值是可以以数组形式访问的,就像这样

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

我们查看题目代码,发现了设置键值对的关键一步

app.post("/set", (req, res) => {
  const { key, value } = req.body;

  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;

  res.json({ message: "OK" });
});

问题的关键就在与这个处理嵌套结构的逻辑上,

我们传入__proto__.a 值给cat /flag 然后访问execute get传入cmd=a即可获得flag

posted @ 2024-11-10 00:16  折翼的小鸟先生  阅读(3)  评论(0编辑  收藏  举报