Introduction
"Want to share code between a Zepto mobile app and a jQuery desktop app? No problem. CanJS code (especially models) can be shared across libraries, and so can skill sets! Working on a Dojo project today and a YUI one tomorrow? Don’t throw away all of your skills."
From http://canjs.com/guides/Why.html
Quick Facts
- CanJS 1.1.6 (Uncompressed)
- CanJS 2.0.3 (Uncompressed)
Usage Stats http://trends.builtwith.com/framework/CanJS
{}SEC-A FAIL Template expressions are equivalent to
eval
- {}SEC-B FAIL No sandbox or isolated execution scope
- {}SEC-C PASS Only
SCRIPT
elements with proper type can serve as template containers - {}SEC-D FAIL No enforced separation, no obvious way to outsource templates to static files
- {}SEC-E FAIL No dedicated security response program, no security@canjs.com address
Injection Attacks
CanJS uses ERB-style templates by default. Everything that is placed in-between the <%
and %>
or its siblings is being treated as raw JavaScript. CanJS throws the content of the ERB-style templates into an actual eval
method - that is called myEval
but doesn't do anything but wrapping the real eval
.
In other words: whenever the user has control over content of the ERB-style template blocks, script injections and XSS are very much likely. The "paste-and-go" code example below shows several cases an attacker can use to bypass server- and browser side XSS filters in case the attacked CanJS application allows injections.
```
<% alert(0) %> > > <%== '\x3Cimg src=x onerror=alert(3)>' %> <%%= '' %> can.view('todoList', {}); ```
Another injection problem can be abused by using a concatenation feature that will be discussed later on this page.
```
<%==($a)->abc})-alert(1)-can.proxy(function(){%> can.view('todoList', {}); ```
Now, how do these work internally?
Eval via myEval
CanJS takes it easy and simply throws the extracted template data into a string, wrapped by a bunch of with()
blocks and afterwards sends it to an eval
. That's all.
``` myEval = function(script) { eval(script); },
[...]
var template = buff.join(''),
out = {
out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
};
// Use eval
instead of creating a function, because it is easier to debug.
myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
return out; ```
Eval via string.concatenated code in Scanner.helpers.fn()
CanJS shows no mercy when coming to quirky injection sinks. Aside from the aforementioned eval
via myEval
, CanJS also allows to inject code into a string-concatenated call against can.proxy()
- that is later being evaluated.
``` Scanner.prototype = {
helpers: [
{
name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
fn: function(content) {
var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
parts = content.match(quickFunc);
return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
}
}
],
```