分享渗透Vue站点的一些思路

既然是第一篇在这里写的文章,那就整一个独家的活。这篇文章只在这里能看到,其他平台不会发。

如果你在其他平台看到了一样或高度类似的文章,请通知站务。

 

阅前提示:

渗透测试为危险操作,可能涉及到法律问题,请务必保证你持有对方的渗透许可/授权,且不影响到对方实际的生产。

免责声明:

即使你参照了本文的方法进行渗透,本文作者和站点对你的行为也一概不负责,请确保你清楚自己到底在干什么。

 

0x01 为什么写这样一篇文章

驱动我写这篇文章的动力主要是近期在做渗透的过程中,又遇到很多src的站点,前端用的都是Vue。随着时代的发展,前后端分离、前端用MVVM的形式做开发已经逐渐成为了行业的常态,传统的开发方式(JSP/ASP/ASPX + jQuery + 某个UI框架)越来越少见。新一点的系统很多都是采用Vue、React之类的框架来做前端。

毕竟相较于传统开发方式,MVVM在中后台系统中具有传统开发方式完全不可比拟的效率优势,配合一些非常成熟的UI框架,比如Element,或者Antd,在做类似于表单、表格等中后台系统非常常见的内容时,新的开发模式和旧的开发模式比可以节约非常多的代码,效率是直线上升的。

一般来说,使用了这些新框架的站点基本上都是前后端分离的,服务端渲染(Server Side Render)在大企业内有运用,但是中后台系统一般不会用到,这个技术更多被用在内容需要更加频繁变更或稳定控制的活动页面等偏向于前台的地方,中后台程序更多是看业务需求,一般大多数还是以前后端分离、XHR调API这种形式为主。

很多小企业受限于技术实力或者有限的服务器资源,他们基本上不会采用服务端渲染这种模式的,基本上是前后端分离(当然也有少数是前后端整合,把Vue/React打包编译好的文件作为静态文件和SpringBoot之类技术栈的服务端一起打包)。

从安全领域来说,这引入了一个新的问题 —— 对于这些采用新型框架的、JS代码基本完全经过了Webpack编译打包的站点,我们要怎么去寻找它潜在的安全问题?

0x02 Webpack

不论是Vue还是React,Webpack都是不可或缺的一个工具,它可以在开发的过程中提供热更新,也可以在整个项目上线前完成最终针对生产环境的编译、打包工作。

以Vue站点为例,Webpack打包出来的东西,在JS上,主要有两个:

  • app.[hash].js
  • chunk-vendors.[hash].js

如果我们要做渗透的话,chunk-vendor开头的文件是完全不需要我们去关注的,因为这个文件没有意义,里面包含的都是各种各样的依赖,除非你打算寻找的渗透点出在某个前端依赖上,否则chunk-vendor这个文件一般是不需要管的。页面本身的应用主要都在app.[hash].js里。

有的站点在打包后还会生成一个“manifest.[hash].js”,这个文件对于我们来说也有一定的价值,至于它是干什么用的,后面会提到。

对于打包生成的app.[hash].js,在默认情况下,它包括了这个项目前端所有的逻辑。如果是一个传统前端开发模式的站点,基本上是我们进到一个页面之后,那个页面的逻辑代码才会被加载,而在现代前端开发模式中,在不应用服务端渲染的情况下,所有的代码实际上都是预先打包好了,作为一个完全静态的文件放到服务器上,前后端的交互完全通过XHR进行。

这给了我们一个可乘之机 —— 既然你的所有逻辑都在代码里面,那么理论上我就算没有登录到你的系统,进入系统内部,我其实也可以看到所有后台页面的逻辑代码。这种情况下,所有后端的接口和接口的调用方式其实都是暴露的。

这和小程序是一个道理,如果我们对微信小程序进行解包,我们其实可以在ServiceWorker里面看到所有接口的URL、调用接口时传入的参数等等,即使我们根本没有权限进入到那个页面,但是通过代码我们也能够知道那个页面里有什么东西,存在什么样的业务逻辑。

当然,出于安全因素,你必然不可能这么顺风顺水的找到所有的接口,毕竟所有的东西都被混淆、杂糅到了一起,你需要从几万行甚至是几十万行代码里找到接口的URL,而且这些代码还都是被混淆了的。这个时候经验就很重要了,如果你有Vue的开发经验,找到接口逻辑是很简单的事情,这一部分经验后文会写到。

对于Vue的打包,在大部分情况下,Vue官方是不推荐把所有逻辑都打在一个JS里的,而是走一个“按需加载”的思路,我用到了这个页面,我才加载这个页面的内容,就像很多传统开发模式开发出来的前端一样。这个时候你并不一定能看到所有页面的代码,因为代码只在你进入到那个页面的时候才会被加载,每个页面的逻辑都被Webpack单独打包了。

用我自己的Vue站点举个例子,页面根据按需加载的思路拆分打包之后,会是下面这样的目录结构:

Webpack拆分打包目录结构

这个时候我们就不能那么简单的获取到后端的全部代码了,不过这并不意味着我们不能获取。实际上在打包之后的代码里,每个页面对应的JS是什么,通常会被放在app.[hash].js这个文件里面,或者有的会在manifest.[hash].js这个文件里,只要我们找到对应的文件索引表,挨个去下载这些JS文件,我们一样可以掌握整个页面的页面逻辑。

对于JS功力深厚的朋友来说,到这一步取后端代码的事情已经结束了,但是有的朋友可能并不习惯调试混淆之后的代码。这个时候Webpack打包默认会输出的.map文件就很有作用了,我们如果能获取到别人放到服务器上的.map文件,我们就能够还原出一个原本的Vue代码,那么这个时候基本上结构之类的东西都无所遁形了。

但是出于渗透上的方便,最终我们要调试的还是混淆之后的代码,所以我个人不推荐逆向出Vue的源码,然后在Vue源码的基础上搞渗透,其实这和我们直接去看编译打包好的JS是一样的,没有区别,而且编译打包好的JS也是实际运行在浏览器里的东西,我们可以动态地把代码注入到浏览器里实现一些行为。

0x03 找接口

有代码了,下一步就是找里面的接口。一般来说接口都很好找,Vue官方推荐使用axios进行AJAX请求,出于方便,这个库一般是配合vue-axios这个包装依赖使用。

由于Webpack在编译过程中不会在这种被Vue直接使用的属性名、方法名上做混淆,所以对于有的站点,我们可以直接检索下面两个关键词:

  • axios.get
  • axios.post

有的站点会预先设计一个接口地址的映射表,把接口地址映射到某个数字或者Hash上,这个时候我们还需要去找对应的映射表。也有很多站是不会用vue-axios这个包装依赖,而是用的其他包装或者自己写的包装,这个时候它有可能会被Webpack从属性名到方法名一并混淆掉,这个时候找接口的工作会麻烦很多。

但是这不意味着找不到,我们只需要搜索某个已知接口URL的一部分,其实就能大致摸索出一个接口调用的关系,这个时候如果有map文件,通过map文件去摸索逻辑也是可以的。

一般来说调用接口的方法里不会直接看到参数,因为这个参数本身是通过函数传进来的,这个时候我们需要在代码里追根溯源,找到包含axios.get/post函数的调用方,这样就能顺藤摸瓜找到参数。这些操作在VSCode里能够很简单的进行,不过电脑配置相对较差的话处理几十万行的超长代码有一定可能会出现卡死等情况。

找到了接口,后续的渗透就是按照常规的步骤来了。

0x04 找路由

Vue站点有时候并不是所有的页面都有权限上的控制,有的页面我们是可以进去的。

在获得了前端的代码之后,我们可以通过搜索某个已知的页面URL去找到左右的路由信息。对于vue-router,路由都是预先配置好了的,即什么样的匹配规则会路由到什么组件。

在app.[hash].js里面,我们是可以找到这个路由表的,进而我们可以去挨个看看它的界面是否可以在无权限的状态下进去,对于一些权限控制做得不是很好的站点,我们可能可以找到一些能够进去的页面。当然,在路由表里面我们也有可能可以找到一个对Vue源码的引用信息,这可能会对逆向起到一点帮助。

不过在实际的渗透过程里找路由基本上是没什么用的,因为很多的权限控制做的是全局的,比如后端返回401,页面就不让你进入这样。

0x05 绕过权限控制

既然存在权限控制,那么我们自然也可以做权限控制的绕过。在前后端分离的情况下,前端完全是一个纯静态的网站,在没有做什么特别配置的情况下,权限控制实际上都是在前端做的。既然是在前端做的,我们就有修改的空间,有办法绕过它的权限控制。

前后端一体的权限控制往往是由服务器靠302跳转实现的,而不是在前端做跳转,如果是这一种,那么我们就没有太多可以操作权限绕过的空间。但是对于完全的前后端分离,后端服务器没办法用302操作前端的跳转,这意味着权限控制对我们来说是可改可控制的。

权限控制的代码一般是在三个地方埋,全局两个点,局部一个点N个地方。

全局是:

  • axios等请求方法的全局拦截器
  • 路由守卫

局部的话就是根据页面逻辑的一些特定返回值,或者是一些特定的Vuex存储状态来判断用户是否有权限然后去做跳转。

全局拦截器是有关键词可循的,对于使用了vue-axios的站点,我们搜索axios.interceptors一般就能够找到对应的拦截器逻辑,我们把里面涉及到权限的逻辑,比如状态码401执行跳转之类的逻辑删除掉或者修改掉即可。(这一部分删除和修改在本地下载下来的代码上做,而不是在浏览器的F12里做)

路由守卫主要是搜索vue-router创建路由守卫的方法名称,比如beforeEach等,具体参考Vue的文档。

对于局部逻辑,我们做的主要是阻止跳转。比如很多局部地方会用$router.push或者window.location.href做跳转,我们可以挨个排查,根据实际的情况把跳转逻辑给阻止掉或者直接去掉。这里我个人推荐阻止,用一些方法把跳转代码跳过,或者替换掉。为什么这么做呢,因为有的跳转可能会在一个变量赋值里面,甚至是在return里面,这个时候你直接去掉可能会引起一连串的崩溃错误。

我们要在保证在前端页面能够正常运作的情况下绕过权限,去掉了它确实不跳转了,但是页面原本的逻辑也被我们破坏了,等于白搞。

对于本地修改好的代码,我们在浏览器用Reres、Resource Override这种插件就能够实现对页面资源加载的自动拦截和替换,我们把代码替换成自己的代码后,就能在页面内畅通无阻。当然,这个只是在前端畅通无阻,但是这相较于代码,你更能直观地看到后台有哪些模块、有什么业务逻辑。

对于接口,你可以直接像正常前端操作一样得到调用接口的完整HTTP请求,这比自己还原要快。对于存在越权的地方,你基本上可以直接在页面看到数据,这个时候你可以直接去交漏洞报告了。

0x06 后记

这一套思路大概给我自己带来了近千元的奖金收益,后续可能还会给我带来更多的价值。

这篇文章不是方法论,不是通用解,很多站点的情况是不一样的,文章里面的东西不算是什么通用方法,更多是一种渗透思路,你可以这么去做、这么去尝试,很多的东西其实还是要具体情况具体分析。有的时候一个系统涉及到多个不同的后端,甚至是Vue内又iframe嵌套,这种情况也有,还有很多公司的Webpack配置是自定义的,可能不是vue-cli那一套默认的、标准的配置,可能是其他在CI或者什么地方做了一个自己的配置,导致打包出来的东西和标准的有很大差异,或者有的站压根就不用axios。

这些个情况都是会有的,所以文章的方法并不通用,只是一个基础思路。

     

One comment

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

购物车
There are no products in the cart!
Subtotal
¥0.00
Total
¥0.00
Continue Shopping
0