zoukankan      html  css  js  c++  java
  • DBShop后台RCE之曲线救国

    本文最早发布在朋友的公众号 黑客信徒 中,文章是自己写的 不存在抄袭  特此申明

    ---------------------

    前言

     DBShop是一款基于ZendFramework2框架的电子商务系统,最近在对DBShop做代码审计过程中发现了一个后台RCE的漏洞,由于是后台漏洞本身并不是很严重,但是发现过程较为曲折,决定还是记录下来,希望能给新入门代码审计的小伙伴一些思路。

    正文

     直入主题吧,后台提供了广告管理的模块-》电脑广告-》设置广告-》编辑广告(或添加广告)

     

    可以看到居然可以有代码类型的广告,这就很可疑了,看下处理编辑广告的代码:

    AdController.php的editAction()中可以对已有广告进行编辑,155行处接受了请求中的参数放入array中

     

    接下来会在173行处调用updateAd()将信息存入数据库,同时在194处调用了createAndUpdateAdFile(),用于生成前台调用的广告文件,跟进:

     

    前面的可以先不用管,在595行处通过fileputcontents()可以写入内容到data/moduledata/Ad/dbmall/下的php文件中,写入的内容是通过createAdContent($adInfo, $type)生成的,其实就是我们之前填写的广告content,这里是可以直接写入任意php代码到php文件中的,文件名是固定字符串加广告id(例如广告类别是首页广告,广告位置是幻灯片1,那么生成的文件就是indexhuandengd1_$id.php)。这样就可以直接写入一句话了?

    测试一下:

     

    会生成datamoduledataAddbmallindexhuandengd1_55.php文件

     

    访问一下

     

    居然返回403,既然没有权限访问,我当时的想法是找个本地文件包含漏洞结合起来使用,原因是很多系统习惯如下这样包含本地文件而不会对输入做校验,这样我们就可以通过../进行回溯包含datamoduledataAddbmallindexhuandengd1_55.php文件了

     eg: $Array = include(BasePath.'/xxx/xxx/'.$_GET['get'].'php');

    这样做的原因是开发者认为包含本地的php文件问题,因为自身本地不会有恶意的php文件。遗憾的是在DBShop中并没有找到这样的利用点。于是我想到既然这个目录下没有权限访问,在生成文件时由于文件名是拼凑的,我通过../让其生成在根目录下并不就可以访问了吗?

    下面是编辑广告抓打包,参数ad_place最终就会参与生成文件的文件名拼凑,我们给他加上../

     

    发现确实可以在父目录下生成php文件,但内容是空的,具体原因代码逻辑相关,这里没必要细讲,主要是想给大家说下我当时的思路。

     

    这样也不行,我又想可以不可以通过copy等文件操作函数将生成的一句话移动到其他目录下呢,这中间花费了我很多的时间,依然没有找到,期间一度打算放弃了这个后台RCE,认为只能实现存储型xss了。

    为什么可以实现存储型xss呢?这是因为我们输入在数据库中的广告代码,最后会在首页中直接输出

     

    复现一下

     

    过了一个周末,我依然不甘心,上班后继续想这个问题。为什么会访问403呢?系统是怎么判断哪有目录可以直接访问哪些又不行呢?发现原因的我哭了出来,原来是data目录下有个.htaccess,禁止访问任意文件(被自己蠢哭,现在才想起)。

     

    那么现在的思路就很明确了,找到任意文件删除漏洞,删除.htaccess就好了。全局搜索unlink,终于让我找到了一处。

     

    ThemeController.php中的adClearAction()方法是用于清除专题中的广告,逻辑中不仅需要删除数据库中的数据,如果广告是图片类型的还要在480行处调用unlink()对服务器中的图片进行删除。

     

    但是由于图片路径来源于数据库中,所有开发者认为路径是可信的,没有对文件名做校验。所以我们只要往ThemeAdTable中添加数据时,让themeadbody为我们想要删除的文件路径即可。

    还是在ThemeController.php文件中,adSetAction()用于给专题设置广告。其实根据我对DBShop这段时间的审计,发现开发者喜欢直接将请求中post数据直接转为array后存于数据库中,其实这样很容易导致参数绑定、sql注入以及像存储型xss这样的漏洞。例如这里,正常给专题添加图片类型广告的请求中其实是没有themeadbody参数的,但是我们可以自己添加该参数到请求中,同样能添加到数据库。

     

    但是这里还有一个问题,在409行处调用了adImageUpload,该方法用于处理广告图片上传的,上传成功后再将文件信息返回,然后在410行处对返回信息$adImage做判断,当$adImage['image']不为空时会使用$adImage['image']重新更新ThemeAdTable中的themeadbody值,跟进adImageUpload()

     

    方法的开头就申明了一个数组用于生成随机文件名,继续跟进uploadImage:

     

    可以看到该方法中有很多安全措施,其中就有上传文件重命名,使用指定路径+md5(时间戳+随机值)+后缀(当然方法中也对后缀做了校验,并不是什么后缀都可以的),最后存入数据库中的文件名如下:

     

    文件上传的最后就是将上传文件信息返回,并用于更新数据库中的数据(将我们最先输入的任意文件路径更改为随机文件名)。本来我想的办法就是让上传出错,这样返回的文件信息就会为空,从而不会进行数据库更新操作。结果我在上面93行处发现程序会调用ZendFrameWork框架中的isValid()对上传文件进行校验,当文件非法时,会直接exit()。这样就不会进行后面的文件上传操作,更加不会进行数据库更新了。

    方法也很简单,选择一个文件上传后直接将文件内容置空。至于原因,我也没细看isValid(),目前认为是由于文件名后缀是.gif,而由于文件没有内容,也就是没有幻术,那么文件type应该是就是index/x-empty,ZendFrameWork会认为这个文件是非法的。

     

    ok,现在进行复现,我们先尝试删除锁文件,然后进行系统重装。

    在专题管理-》专题设置中选择一个专题进行广告设置-》选择图片广告编辑

    随便选择一个图片进行上传

     

    抓包修改:

     

    看下数据库中

     

    接下来我们再进行删除广告的操作

     

    构造如下所示请求包,itemid这个参数在添加广告的时候就可以得到,但是themadid这个参数是添加广告时数据库自增的,这个参数怎么得到呢?可以在进行测试之前先添加一个正常的图片广告,然后再删除这个广告时抓包得到themadid为n,那么我们下次再添加广告时themad_id则为n+1(这么麻烦的原因是,当我们添加一个畸形的广告后,操作专题广告的页面将无法正常访问了,应该是路径问题了,没必要深究,这也是为什么我们添加了畸形广告后再进行删除操作时的请求是手工构造的而不是在页面上进行操作)。

     

    可以进行系统重装。

    同样的我们可以删除DBShop/data/.htaccsess,这样我们将可以直接访问该目录下的文件,接下来我们进行RCE复现。

    删除.htaccsess文件后,在广告管理-》电脑端广告-》设置广告-》添加广告,广告位置选择幻灯片1(生成的文件名格式在前面已经讲过了)

     

    这里添加的广告代码是<<??php phpinfo(); 而不是<?php phpinfo();

    这是因为在添加广告代码到数据库中时,model层做了简单的过滤,不过显然这并没有什么用。

     

    最终会生成datamoduledataAddbmallindexhuandengd1_55.php文件,如下

     

    生成的php文件名,前半部分都可以是固定的,后面的数字是广告的id,

     

    访问该php文件即可

     

    最后

     其实个人觉得DBShop的安全做的还行,主要是用了ZendFramework2这个框架,我也是在审计DBShop之前临时看了下这个框架的入门文档才摸清楚这个项目的路由规则的。其实还发现了后台盲注等漏洞,但是发现没有什么学习价值也就不发出来了。最后的最后,文中也说了关于直接获取POST参数放入array后再通过ZendFramework2的insert方法存入数据库中:

     如下图,$data其实就是POST参数集合,这样很容易产生参数绑定漏洞,例如下用户个人信息编辑处,攻击者可能会添加role参数或money等参数(视场景不同)导致逻辑漏洞,不过DBShop中我简单看了下没有发现问题。

     另外我还说容易导致SQL注入,这是因为开发者如果过于相信框架的安全性,没有对输入信息做校验,完全是可以进行int类型的sql注入的。不过好在DBShop在调用insert前都调用各model的checkdata方法,其中会对int类型的参数做intval()过滤。

     

     但是这么多数据库难以保证model层中各属性的类型和数据库中的类型是一一对应的,或者说可能有些int类型的属性忘记调用intval()了,由于工作量有点大,我找了一半没发现就放弃了,不得不说代码审计是体力活啊。全文完

  • 相关阅读:
    jquery笔记
    css选择器
    Linq 巧用 Max,Sum
    Linq Aggregate
    Linq 对象的比较 Contains,Max
    Linq SelectMany 交叉连接
    JQ 标签相关知识
    C# HttpClient设置cookies的两种办法 (转发)
    使用 HttpClient 请求 Web Api
    MySQL 避免重复数据的批量插入与批量更新
  • 原文地址:https://www.cnblogs.com/jinqi520/p/11159619.html
Copyright © 2011-2022 走看看