Pentesters的Angular和AngularJS - 第2部分

 

 

前言

在我们Part1文章中,我们从应用程序安全性的角度讨论了Angular和AngularJS应用程序的一般结构。这一次,我们将探索不同的方法来动态调试Angular应用程序,不管代码是否被最小化。但是为什么这对应用安全研究人员来说很重要呢?通过从浏览器控制台调试Angular应用程序,我们可以根据自己的喜好操纵函数和范围变量。这可以让我们触发能够帮助我们发现前端和后端错误的行为,例如缺少功能级访问控制和不安全的直接对象引用。此外,本文中展示的技术可以通过利用浏览器开发工具提供的功能,更容易地检查整合和简化的Angular代码。

让我们从学习动态调试Angular 1.x应用程序开始。

 

Angular 1.x

假设你正在评估Angular 1.X应用程序,并且你对改变服务功能的行为感兴趣。我们可以通过浏览器中的开发人员工具控制台,按照几个简单的步骤来实现这一点。你想做的第一件事当然是打开开发者工具控制台。你可以通过右键单击任何页面元素并单击“Inspect”(如果你使用的是Chrome)或“Inspect Element”(如果你使用的是Firefox)来执行此操作。接下来,单击“控制台”选项卡。

现在我们已经打开了控制台选项卡,让我们获取感兴趣的服务。在这种情况下,我们对AuthService感兴趣。我们可以打开浏览器控制台并执行以下操作:

var injector = angular.element(document.body).injector();
var authService = injector.get('AuthService');

该变量authService包含AuthService现在所使用的所有导出函数。你可以通过从开发人员控制台的“源”选项卡中搜索代码来获得你感兴趣的服务的名称。很多Angular 1.X应用程序在部署到生产环境之前不会简化代码,所以读取它们的代码应该是一个简单的过程。

请注意,浏览器会在你键入authService.时为你提供自动完成建议。这很有用,因为它告诉我们哪些函数和变量可供我们使用和修改。

现在让我们将函数isLoggedIn改为true

authService.isLoggedIn = function(){ return true; }

现在怎么办?什么都没发生!是这样的,Angular还没有意识到这个变化,所以你需要强制它运行一个digest:

angular.element(document.body).scope().$apply();

完成上述操作后,你可能会注意到屏幕上出现了其他用户界面元素,你可以开始摆弄这些元素,因为应用程序中的其他功能会假设你在运行AuthService.isLoggedIn ( )时已登录。

如果我们想改变范围变量呢?你可以通过获取对包含要操作的范围变量的控制器的引用来实现这一点。第一步是获取对该控制器管理的页面上元素的引用。最简单的方法是使用开发人员工具控制台的“elements”选项卡选择该元素,并在控制台中键入$ 0,这将返回该DOM对象。我们将使用这种技术来获取控制器参考。例如,请注意下图中我们选择由BaseController控制的 <body>元素。键入$ 0后,我们看到控制台中打印了DOM元素。

现在假设你通过检查页面标记找到了一个由名为AdminController的控制器管理的元素。要获取对该控制器的引用,你需要执行以下操作:

var adminController = angular.element($0);

完美,现在我们可以访问该控制器中的所有范围变量。让我们将isAdmin范围变量值更改为true:

adminController.scope().isAdmin = true;

最后,让我们通过调用digest()让Angular得知更改:

angular.element(document.body).scope().$digest();

同样,你也可以更新控制器功能。例如,在下面的代码片段中,我们从根控制器更改了expireSession方法,以便它不执行任何操作。

angular.element($0).scope().$root.expireSession = function(){return;};

 

Angular 2+

好吧,但是Angular2 +怎么样?有什么奇特的动态调试技巧吗?Angular2 +应用程序不像Angular1.x那样容易使用浏览器控制台操作。大多数Angular 2应用程序都是在生产模式下加载到浏览器中的(当然,除非开发团队意外地在开发模式下部署了该应用程序),这限制了我们可以做的事情和功能调试。

将Angular 2+应用程序部署到生产环境时,在设置应用程序模块之前调用该enableProdMode()函数。这使得Angular跳过构建调试元素树,这将允许我们从浏览器中动态调试应用程序。如果是这种情况,最好的办法是使用像Burp这样的工具截取包含调用enableProdMode()的JavaScript代码的脚本文件,并在脚本文件到达浏览器之前将其删除。

然而,有一个问题。如果应用程序代码被简化或与(webpack)[https://webpack.js.org/] 整合在一起,你不能只在Burp中搜索enableProdMode,因为webpack试图通过用随机的短名称重命名函数来减小整合的JavaScript文件的大小。这意味着该函数可以被称为类似于Qa()V()的东西,并且在应用程序调用它之前,它可以被重命名几次。对此有一个解决方案,那就是找到并更改Angular库代码实现的函数。该enableProdMode()函数在webpack的脚本文件中看起来像这样:

function Oa(){if(ib)throw Error("Cannot enable prod mode after platform setup.");ge=!1}

接下来,搜索字符串Cannot enable prod mode after platform setup或其中的一部分并将功能(在浏览器接收之前)更改为以下内容:

function Oa(){console.log("prodmode function hijacked")}

如果成功完成,您将在浏览器控制台中看到如下内容:

此过程也可以使用像Gorp这样的自动化工具,它查找并修改启用生产模式的函数,并重写该函数,以便应用程序在浏览器中加载时可以在开发模式下运行。

太好了,我们正在开发模式下运行应用程序,现在怎么办?我们需要做的第一件事是检查你想要使用的Angular组件的当前状态。

使用开发人员工具在页面中选择一个元素,然后在控制台中键入以下内容:

var state = ng.probe($0);

请注意,如果应用程序仍在生产模式下运行,您将得到null

现在我们可以获得我们在上一步中选择的组件的实例。

var component = state.componentInstance;

由于我们有一个Angular组件的引用,我们现在可以操作组件变量和函数,如下所示:

component.showAllDiscountCodes = true;

最后,我们在组件上调用变更检测器以应用我们的更改:

ng.probe($0).injector.get(ng.coreTokens.ApplicationRef).tick();

但服务呢?在Angular 2+中,服务被注入组件中。然后,这些组件可以使用服务引用来调用服务提供的任何功能。注入的服务类似于C#等语言中的静态帮助程序类。我们所需要做的就是找到并获取对服务变量的引用,然后像上面一样进行修改。

请注意上图中浏览器是如何给我们一个建议变量列表的。这在测试Angular 2+应用程序时非常有用,因为代码通常被简化并且不容易读取。这项技术帮助我们理解代码,而不必通过阅读简化的代码来伤害我们的眼睛。

 

总结

现在,我是否告诉您为使用Angular的每个应用程序执行此操作?当然不!尽管如此,这篇博客文章应该会给你必要的工具来进行角度代码的深入分析。此外,详细的Angular代码分析不应取代我们在每次评估中使用的良好的渗透测试方法。

(完)