实战审计之dedecms

举报
亿人安全 发表于 2023/04/11 11:16:39 2023/04/11
【摘要】 环境搭建:框架介绍:本项目为dede自研系统,其中所有功能点都由dede自开发,访问对应PHP文件即可找到对应功能点。大部分文件都包含了/dede/config.php,该文件中检测了用户的登录状态,且对于XSS进行了过滤。二、代码审计1.任意文件写入通过搜索 fwrite() 函数,发现这里存在可疑写入点,而我们知道得知dede的路由可以通过直接访问php文件的形式进行,那么我们先看看这里...

环境搭建:

框架介绍:

本项目为dede自研系统,其中所有功能点都由dede自开发,访问对应PHP文件即可找到对应功能点。

大部分文件都包含了/dede/config.php,该文件中检测了用户的登录状态,且对于XSS进行了过滤。

二、代码审计

1.任意文件写入

通过搜索 fwrite() 函数,发现这里存在可疑写入点,而我们知道得知dede的路由可以通过直接访问

php文件的形式进行,那么我们先看看这里的写入内容是怎么可控的。

我们可以看到这里代码70行的变量 $configstr 是经过序列化的 $faqs 变量传入的,那么这里的 $faqs

这个数组是怎么传入的呢?这里其实可以直接访问该文件,因为dede的每个文件都是一个功能点,并且

文件会直接渲染进行输出比较直观(见代码78行)直接访问该文件发现如下功能点,而这里的验证码设置功能处正是传入参数的地方这里抓包我们可以清楚的看到此处参数可控的地方但在代码的第60行是存在过滤的,这里当你 question 参数传入单引号时会被正则替换为双引号没办法

进行闭合,而 quswer 参数则并没有任何限制,反而帮我们删除了反斜杠,所以这里 answer 参数处就存

在绕过。

我们在去看看要写入的文件内容

漏洞复现:

2.目录遍历

搜索 read() 函数,发现在危险函数中存在参数可控的地方,我们进入 select_images.php 文件。

漏洞复现:

3.Mysql数据库操作getshell

在测试功能点模块管理时,发现一处可以操作数据库的地方,我们点击修改。其实我们可以直接在删除程序处输入我们想要执行的SQL语句进行测试,但这里我们是站在审计的角度

去看此处漏洞,所以找到该文件进行分析。

这里传入了两个参数 action 以及 hash ,这两个参数在审计该漏洞是很关键的。

这里通过uninstall执行该处分支,然后通过上面传入的hash参数值来定位文件, GetFileLists() 获取

对应文件,跟进 GetFileLists() 函数。'

在该函数中首先包含了 modulescache.php 文件,并通过 $hash 返回xml文件内容。

modulescache.php 文件内容

然后找到该xml,查看该xml内容,该文件内容中就是相应模块的配置信息。

然后回到上面代码的的第425行,又执行了 uninstallok 分支。该分支就是卸载模块的分支,这里最重

要的是通过 GetSystemFile() 获取文件内容,这里传入了另一个参数 delsql ,跟进该函数‘

这里依旧是通过 GetHashFile() 函数获取文件内容。

这里的delsql内容中正是删除程序处的文件的base64内容

最后在代码518行执行SQL语句,所以我们可以通过Mysql日志文件来getshell,而这里需要网站的绝对

路径,由于存在目录遍历,所以可以轻松获取绝对路径。

漏洞复现:

访问cyw.php

4.任意文件写入2

同样的方法,在搜索危险函数 fwrite() 时发现在 article_string_mix.php 中存在可以控制写入内容

的地方。

在 article_string_mix.php 中 $allsource 写入的内容是可控的,但在代码29-34行中存在危险函数

过滤,但在过滤函数中过滤不严格导致绕过,实现代码执行。

漏洞复现:

通过 preg_replace() 绕过危险函数过滤,造成代码执行。

downmix.data.php中我们的内容已经写入

访问dede/article_template_rand.php文件,执行phpinfo()。

5.任意文件写入3

这里依旧是同样的功能点,所以大家看到这里其实也可以发现,在dedecms中使用 fwrite() 函数写入

配置文件的操作是很多的,在该系统中我们只需要观察两点即可,一写入的位置为可解析的php文件,

二写入的内容我们是可控的即可。

在函数 ReWriteConfig() 中,我们可以发现代码的第38行和第42行都是用了 fwrite() 函数,向上回

溯发现该处的值是从代码31行的 sysconfig 表中拿到的,我们去看看该表中的内容。

大致一看其实就是存储了网站的配置信息,上面的代码中可以看到type为 number 时会将 varname 字段

的值以及value字段的值写入配置文件,如果类型不为type时,这里会存在一个小小的过滤,这里的单引

号会被替换为空。

所以这里就有两个思路

1.我们可以直接在type=number类型的字段中直接插入我们要执行的代码

2.通过\反斜杠来转义原本的单引号,然后使我们的代码逃逸出来

在 dopost 参数为 save 时,我们可以发现这里接收了post传入的参数,并且将传入的内容写入到了

sysconfig 表中。在代码74行中执行了上面分析的 ReWriteConfig() 函数。

漏洞复现:

我们访问/dede/sys_info.php文件,发现该处功能点确实是配置系统参数处,所以我们直接在

type=number参数处插入我们的代码。

第二种方法可以通过\反斜杠转义原有的单引号,使我们的代码逃逸出来。

这样的话也可以造成代码执行,只不过方法不同。


6.Mysql数据库操作getshell2

在测试系统功能点的时候,发现一处可以操作数据库的地方,下面的功能可以执行SQL语句。

漏洞分析:

这里执行到query分支,108-111行这里存在过滤,这里过滤了drop关键字,116行执行我们SQL语句。

漏洞复现:

set global general_log = on;

set global general_log_file = 'D:/phpstudy_pro/WWW/www.dedecms12.com/uploads/shell.php';

7.文件上传漏洞

该处首先具有前端限制,上传 .jpg 后缀文件,结合brup抓包,发现处理上传功能的文件为dede/archives_do.php

然后结合调试,来看看具体代码

入口文件通过 config.php 会实现权限认证和一些外部参数过滤注册

我们这里上传文件会带有$_FILES参数,上面通过全局分析得知会触发uploadsafe.inc.php的过滤

过滤后,通过AdminUpload()实现最终文件上传

include/helpers/upload.helper.php

最终实现文件上传的AdminUpload()来自upload.helper.php

传入AdminUpload()$ftype固定为imagelit,则一定会进入对应的检测判断

在检测判断代码中,$sparr定义了一个MIME Type白名单,若上传文件的MIME Type不在白名单中直接退出,MIME Type我们可控,所以这里一定要设置MIME Type为图片类型

但这里要注意的一点是,当MIME Type为图片类型时,在安全过滤文件uploadsafe.inc.php检测中,还会通过getimagesize()再次判断文件是否为图片类型,不够这里我们也可以绕过

最后梳理一下,该功能点,系统只做了两个限制,MIMI Type为图片类型,可控。但MIME Type为图片类型时会通过getimagesize()检测,这里也可绕过。下面将来复现一下,看是否可以利用

保证Content-Type为图片类型,构造图片的文件头,绕过文件上传的限制,并且会返回上传文件名和路径

漏洞复现:

后台:【核心】-【常用操作】-【所有档案列表】-【添加文档】,该功能可以发布文章,而且具有文件上传的功能

8.文件上传漏洞2

9.xss漏洞

测试发现还是黑盒好测一点,在dedecms后台还是存在很多xss的,本次是在黑盒测试后,在回头审计代码的问题,其实这样白盒审计意义不大,主要记录下思路

因为dedecms是多入口文件,每个入口文件都需要包含具有全局过滤函数的文件来判断外部数据的安全,如果发现有的文件没有包含这样这种文件,那么这个入口文件可能就存在相关漏洞

在全局分析中发现并没有对外部数据做xss全局过滤,另外注意到dedecms具有视图类负责显示输出,封装了很多输出的功能,在平时白盒审计xss漏洞需要注意echo,innerHTML这类输出到前端的关键词,但在dedecms中还需要注意视图类封装的输出函数

qrcode.php

qrcode.php及加载的文件都没有做xss过滤,通过common.inc.php会注册全局变量

$id只能为整数类型,$type类型可控

加载模板qrcode.htm,利用视图类格式化输出$id,$type的值,$type可控,这里就存在xss漏洞

可以看到这里的触发点$dtp->SetVar('type',$type);,然而在seay这种代码扫描工具中是不会在意这些点的,同样有些框架对sql操作也做了很好的封装,如果只是依靠seay的结果来做代码审计,可能会忽略掉很多关键点

最后有图有真相

10.url 重定向漏洞

eay似乎没有 url 重定向漏洞的扫描,不过该漏洞审计也比较简单,主要关注能重定向的一些关键词,再看重定向地址是否可控

这里看一个dedecms出名的url重定向漏洞,网上有很多讲解,xray都有poc

plus/download.php

$link做了base64解码

程序中有一个很奇怪的限制,in_array($linkinfo['host'], $allowed),然而download.php中却没有$linkinfo这个参数dedecms后台也有一些url重定向漏洞,这里就不多关注这个洞了

11.会员中心任意用户密码修改

在用户密码重置功能处,php存在弱类型比较,导致如果用户没有设置密保问题的情况下可以绕过验证密保问题,直接修改密码(管理员账户默认不设置密保问题)。值得注意的是修改的密码是member表中的密码,即使修改了管理员密码也是member表中的管理员密码,仍是无法进入管理

漏洞代码分析

php弱类型比较问题很常见,在不同类型比较时,如果使用的是==,php会将其中一个数据进行强制转换为另一个,比如'123a'就会被强制转换成123。这样就出现了弱类型比较问题,当然如果使用===判断比较就不会出现问题了。常见比较如下

'' == 0 == false '123' == 123             //'123'强制转换为123 
'abc' == 0         //intval('abc')==0 
'123a' == 123            //intval('123a')==123 
'0x01' == 1             //被识别为十六进制
'0e123456789' == '0e987654321'  //被识别为科学计数法 
[false] == [0] == [NULL] == [''] 
NULL == false == 0 
true == 1

dedecms的/member/resetpassword.php就是用来处理用户密码重置的问题,问题出在75行开始处理验证密保问题处。

可以看到,这段代码先是从数据库取出相关用户的密保问题及密保答案,在对用户输入做了一些处理后,进行了关键性的判断if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer) ,就在这里用了弱类型判断==

首先我们知道,如果没有设置密保的话safequestion从数据库取出默认为'0',safeanswer为空。根据empty函数特性,'0'会被判断为空,会进入重新将$safequestion赋值为''。而'0' != '' ,所以我们需要一个输入即不使empty为空,且弱类型等于'0'的字符串。'00'、'000'、'0.0'以上这些都是可以的。

接下来safeanswer既然本来就为空,那么不输入正好也就相等了。跟踪sn函数跟踪newmail。可见在sn函数中将send参数设置了'N',其实就是生成了暂时密码并插入了数据库中,并进行跳转:

漏洞复现:

在找回密码处,点击通过安全问题取回。

总结:最终梳理下来,这里其实就是在用户没有设置密码问题时,后台数据库默认保存为空,并且后台在进行密保问题判断时采用弱类型比较,导致可以绕过,最终结果是,凡是没有设置密码问题的用户,都有密码被任意修改的风险

12.会员中心任意用户登陆

dedecms的会员模块的身份认证使用的是客户端session,在Cookie中写入用户ID并且附上ID__ckMd5,用做签名。主页存在逻辑漏洞,导致可以返回指定uid的ID的Md5散列值。原理上可以伪造任意用户登录。

漏洞代码分析

在/member/index.php中会接收uid和action参数。uid为用户名,进入index.php后会验证Cookie中的用户ID与uid(用户名)并确定用户权限。

我们可以看到当uid存在值时就会进入我们现在的代码中,当cookie中的last_vid中不存在值为空时,就会将uid值赋予过去,$last_vid = $uid;,然后PutCookie。

那么这么说,我们控制了$uid就相当于可以返回任意值经过服务器处理的md5值。

而在接下来会验证用户是否登录。

现在我们来看看,dedecms会员认证系统是怎么实现的:/include/memberlogin.class.php

$this->M_ID等于Cookie中的DedUserID,我们继续看看GetCookie函数它不但读了cookie还验证了md5值。

这样,由于index.php中我们可以控制返回一个输入值和这个输入值经过服务器处理后的md5值。那么如果我们伪造DedUserID和它对应的MD5就行了。

最后一个问题,因为我们上面是通过用户名伪造ID的,用户名为字符串而ID为整数,但好在在构造用户类中将M_ID intval了一下$this->M_ID = intval($this->M_ID); 那么这么说,如果我们想伪造ID为1的用户的Md5,我们只要在上面设置uid(用户名)为'000001'即可。

漏洞复现:

现在我们的思路就是

  1. 先从member/index.php中获取伪造的DedeUserID和它对于的md5
  2. 使用它登录

访问member/index.php?uid=0000001并抓包(注意cookie中last_vid值应该为空)。

REF:

https://www.freebuf.com/articles/web/281747.html

https://blog.szfszf.top/article/25/

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。