返回首页
EN
H7 / SECURITY RESEARCH NOTES

Discuz CSRF 备份拖库分析

本页导航5 个章节

前言

  • 将《代码审计-企业级Web代码安全架构》这本书提及的所有漏洞案例完整详细复现,并提供源代码包,方便后续新人从这本书入手的时候自己复现案例才能学的更深刻。

漏洞标题

Discuz CSRF 备份拖库分析 书P84

准备工作

  • 源代码包提供下载,用phpstudy即可。环境配置如下:
  • nginx 1.15
  • mysql 5.5.62
  • php 5.2.17

开始

  • 简要描述: discuz后台在做数据库备份时,没有做csrf防御导致数据库可以被恶意备份拖库

  • 分析开始: 首先找到备份功能点,如图,可以看到两个应用,一个UCenter,一个discuz,为什么会存在俩呢? 因为他们俩应用都是属于一个公司的,discuz是个论坛应用程序,ucenter是一个会员整合应用程序, 公司用ucenter是为了让discuz方便和公司旗下其他产品进行一个无缝整合。 这里选择一个应用UCenter,点击提交,如图:

image.png

image.png

image.png

  • 从第一张图可以发现,点击提交后,发送了两个请求,两个请求是先后发送的(这里展示不了动态), 可以发现,从第一个请求可以发现,请求的是uc_server/admin.php这个文件,现在查看这个文件 源码:

image.png

  • 可以发现,入口文件对获取的$_GET,$_POST等都进行了daddslashes()过滤,查看该源码后发现主要是 运用了addslashes()这个php函数进行转义,再往下该入口文件调用控制器和方法的参数分别是$m和$a, 那么我们现在知道了,该请求调用的是 uc_server/control/admin/db.php文件中的onoperate()方法,再往下如图:

image.png

  • 可以发现刚才调用的daddslashes函数和getgpc函数,getgpc函数默认从$_REQUEST变量中获取参数, 待会儿onoperate()方法会用到,现在查看onoperate()方法,如图:

image.png

  • 可以看到该方法获取外部参数赋值为变量时,用了getgpc()方法,这里还调用了父类的一个数组属性 $this->cache['apps'][$appid],查看该属性,如图:

image.png

  • 可以看到该属性属于base类,由于并且类中构造方法调用了base()方法,base()方法中调用了init_cache() 方法,所以跟随查看init_cache()方法,如图:

image.png

  • 可以看到该方法第一行代码即调用了cache()方法,传入参数settings,并将返回值赋值给该类属性 $this->settings,可以知道该属性已经定义为数组,所以可以知道返回值肯定为数组。跟随cache()方法, 如图:

image.png

  • 可以看到这里定义了一个静态类属性$_CACHE,类型为数组,然后包含了 uc_server/data/cache/settings.php,查看该文件:

image.png

  • 可以看到该文件中是一个数组$_CACHE['settings'],主要是一些配置参数。然后返回init_cache()方法,

image.png

  • 该方法第二行代码也是同样调用了cache()方法,传入了apps这个参数,包含了 /uc_server/data/cache/apps.php这个文件,然后后续代码就是设置时间,时区什么的,不用管,这里 看一下apps.php文件的内容,如图:

image.png

  • 可以看到也是一些配置文件,比如appid=1,type=discuz,url这些后续会用到。咱们现在返回onoperate() 方法,如图:

image.png

  • 现在知道了$app变量的内容了,继续往下读,咱们请求的参数appid为0,可以看到第98行代码构造了一个url ,将原本请求的admin.php更换成了api/dbbak.php,原本请求的url为 http://localhost/949s/949s/upload/uc_server/admin.php?m=db&a=operate&t=export& appid=0&backupdir=backup_200729_6roSV0,构造后,$url变量为 http://localhost/949s/949s/upload/uc_server/api/dbbak.php?apptype=UCENTER&method =export&sqlpath=backup_200729_6roSV0&time=time()(最后这个time参数没写出来,就是一个 时间戳

  • 第99行代码调用了一个authcode()方法对传入的参数进行加密,这里就不看这个方法源码了,然后101行代码 将$url变量和URL编码后的$code变量连接起来构成一个新的$url变量,值得注意的是,这里的$code部分 是经过加密,然后url编码处理过的。

  • 然后后续第106行代码调用了dfopen2方法,就是涉及备份的方法,查看该方法,如图:

image.png

  • 可以看到,这里给$url变量新增了一个__times__参数,值为1,然后接着调用dfopen()方法,如图:

image.png

  • 该方法调用了php的parse_url函数将url各部分解析为数组中的元素,因为这里传入的是$post参数为 空,所以构造的是get请求数据包,请求的url即是之前传入的$url变量,继续往下看,如图:

image.png

  • 可以看到调用了php函数fsockopen来请求之前我们构造的$url变量,然后获取返回的结果,请求的 是uc_server/api/dbbak.php文件,查看该文件源码,如图:

image.png

  • 看源码可以知道包含了一个配置文件config.inc.php,配置文件内容如图:

image.png

  • 然后将$url变量加密的部分进行了解密,并通过 parse_str函数将url各个参数作为$get数组的数组 元素,再通过_stripslashes方法对$get数组进行了一次过滤,查看一下_stripslashes方法源码:

image.png

  • stripslashes是php的函数,主要是返回反转义的字符串。然后返回dbbak.php继续往下看源码:

image.png

  • 可以看到往下定义了一个dbstuff的类,继续往下看,如图:

image.png

  • 可以看到下面实例化了该类,并且根据我们的$apptype为ucenter做了BACKUP_DIR常量定义,这里的 BACKUP_DIR即为uc_server/data/backup/,然后连接了数据库,继续往下看:

image.png

  • 可以看到根据我们传入的参数method执行相应的操作,并且根据传入的参数sqlpath和BACKUP_DIR拼接 起来作为创建备份文件夹的名称,在这里即创建了uc_server/data/backup/backup_200729_6roSV0 这个文件夹,创建的备份文件夹名称是最初前端请求的参数backupdir,也就是说我们可以自定义备份文件夹 名称,继续往下看:

image.png

  • 可以看到这里有一个backupfilename参数,但是我们并没有传入该参数,根据这里的逻辑来说就是没有 获取到backupfilename的话,就自己生成一个backupfilename,那说明这个备份文件名也可以通过前 端传入backupfilename这个参数来自定义,那么现在文件夹名和文件名都可以自定义了,继续往下看:

image.png

image.png

image.png

  • 这是换了一个浏览器,可以看到可以直接访问咱们的备份好的文件,然后标题是通过CSRF来拖库呢,所以 呢,这里就演示一个可能造成的CSRF例子,首先在前台论坛创建一个帐号发一个帖子,帖子中放一张图片, 该图片用来构造exp,利用方法是,当管理员查看该帖子时,就会利用当前管理员的权限发送一个备份文件 请求,创建相应我们想要创建的备份文件,然后就造成了CSRF攻击拖库了,(特别注意:这里采用两个浏览器, 这里普通用户前台发帖用的chrome浏览器,管理员登录在firefox浏览器) 演示如下:

image.png

  • 这里发帖后,当管理员查看该帖子时,就会看到一张破碎的图片,实际就发送了备份文件的请求,如下图:

image.png

  • 这里构造的exp创建了一个文件夹名为test1,文件名为test1-1.sql,如图:

image.png

  • 复现完毕!