• Blog
  • 奇安信Discuz相关漏洞总结

节分为3部分,首先将对Discuz X3以后的版本出现的主要漏洞做一个简单总结,然后针对discuz的几种密钥做一些分析,最后发布一个discuz最新的后台getshell。

Discuz X3以后漏洞总结

目前市面上基本都是x3以上的Discuz程序了,x3以下的网站占比已经非常低了,因此在此只总结x3以上的漏洞。总结并不是对每个漏洞进行重新分析,这是没有必要的,网上已经有很多优秀的分析文章了。那我们为什么还要总结呢?如果你是在一线做渗透测试或者红队评估的同学,应该会经常遇到discuz,往往大部分同学一看程序版本再搜搜漏洞或者群里问问就放弃了。在大家的印象中discuz是一块硬骨头,没必要耗太多时间在它身上,但事实上discuz并不是你所想象的那么安全。本小节将通过总结discuz的各种小漏洞,再结合我们自己的几次对discuz目标的突破,提出一些利用思路和利用可能。

总结:

  • 针对于discuz的ssrf漏洞,在补丁中限制了对内网ip的访问,导致了很难被利用。
  • 在后台getshell中,建议使用uc_center rce比较方便,并且通杀包括最新版本,后文有分析。
  • UC_KEY 直接getshell已在x3以上的最新版本被修复,但在一些老的3.2以前的版本可能被利用。

以上这些漏洞应该并不全面,且看似都比较鸡肋,但往往千里之堤毁于蚁穴,几个不起眼的小漏洞组合一下会发现威力巨大。仔细的读者应该发现以上漏洞大部分能够造成的最大危害是信息泄露,信息泄露有什么用呢?下面我们将接着分析Discuz的几种密钥,看到这儿你应该已经明白了,通过信息泄露,获得相关密钥,突破discuz的加密体系,进而获取更高的权限。

Discuz的几种密钥分析

通过分析,在discuz中,主要有下面的几种密钥, 这些密钥共同构成了discuz的加密解密体系,这里的命名有重复,我已经标记了对应key值以及key所在的位置。如下表所示:

主要探讨的其实就只有 authkey,UC_KEY(dz),UC_KEY(uc_server),UC_MYKEY,authkey(uc_server) 5种,我们首先来看着几个密钥是怎么来的最后又到了哪儿去。

密钥的产生

authkey,UC_KEY(dz),UC_KEY(uc_server),UC_MYKEY 都是在安装的时候产生。authkey(uc_server)的产生是和UC_MYKEY息息相关的,在后文中详细讲述。生成代码如下所示:

我们看见key的产生都依赖于discuz 自定义的random函数,出现过的authkey爆破问题也因此产生。在安装时由于处于同一个cgi进程,导致mt_rand() 只播种了一次种子,所以产生了随机数种子爆破和推测key的问题,在3.4版本中,authkey的产生已经是拼接了完整的32位字符串,导致了无法进行爆破推算出authkey的前半部,因此这个问题已经被修复,但这个漏洞原理值得学习。代码最后可以看出authkey产生后还放入了数据库中,最终authkey存在于数据库pre_common_setting表和/config/config_global.php配置文件。 代码中的 instal_uc_server()函数实现了UC_KEY(dz),UC_KEY(uc_server)的产生,使用了同一个生成函数_generate_key(),代码如下:

产生的算法牵扯到安装环境和安装过程的http header信息,导致爆破基本失效,从而无法预测,最后UC_KEY(dz)保存到了/config/config_ucenter.php中,UC_KEY(uc_server)保存到了/uc_server/data/config.inc.php中。

Discuz Key的相关思考

我们通过查看源码,去分析每个key影响的功能,通过这些功能点,我们可以去获得更多的信息。信息的整合和利用往往是我们渗透的关键。下面我们将做一些抛砖引玉的思考并举一些例子,但不会面面俱到一一分析,这样也没有意义,具体的代码还是需要读者自己亲自去读才能印象深刻。

1. authkey

authkey的使用在discuz主程序中占比很重,主要用户数据的加密存储和解密使用,比如alipay相关支付数据的存储和使用、FTP密码的存储等等;还用于一些功能的校验,比如验证码的校验、上传hash的校验等等;用户权限的校验也用到了authkey,比如source/class/discuz/discuz_application.php 中_init_user() 利用authkey解码了cookie中的auth字段,并利用解开的uid和pw进行权限校验,但是光知道authkey并不能完成权限校验,我们还需要知道用户的”密码hash“(数据库pre_common_member表中的password字段,此处存储的只是一个随机值的md5,真正的用户密码hash在pre_ucenter_members中),当我们通过其他方法可以读取数据库数据时,我们就可以伪造登陆信息进行登陆,再比如source/include/misc/misc_emailcheck.php中authkey的参与了校验hash的生成,当我们知道了authkey后,通过伪造hash,我们可以修改用户的注册邮箱,然后利用密码找回来登陆前台用户(管理员不能使用密码找回功能)。

2. UC_KEY(dz)

UC_KEY(dz)也是经常提到的UC_KEY GetWebShell的主角。它主要在2个地方被使用:一个是数据库备份api/db/dbbak.php;一个是针对用户以及登录和缓存文件相关的操作,主要函数位于api/uc.php中的uc_note类。

关于UC_KEY(dz)的利用,网上基本都是通过uc.php来GetWebShell,但这个漏洞在新版本已经被修复了。UC_KEY(dz)的利用并不局限与此,你去阅读dbbak.php代码就会发现,有了UC_KEY(dz)我们可以直接备份数据库,下载数据库,从数据库中找到相关信息进行进一步渗透。

另外一个地方就是uc_note类,比如里面的synlogin()函数可以伪造登陆任意前台用户。当然还有其他的函数,在这里就不一一分析。

3. UC_KEY(uc_server)

UC_KEY(uc_server)往往是被大家忽视的一个key,它其实比UC_KEY(dz)的使用更多。首先他同样可以备份数据库,对discuz代码比较熟悉的同学应该知道dbbak.php这个文件有2个,一个是上面提到的api/db/dbbak.php;另外一个是uc_server/api/dbbak.php,他们的代码可以说几乎相同。唯一的区别是api/db/dbbak.php中多了2个常量的定义,基本没有太大影响。这个2个文件都能被UC_KEY(dz)和UC_KEY(uc_server)操控。

UC_KEY(uc_server)几乎管控了Ucenter的所有和权限认证相关的功能。例如权限验证函数 sid_decode() ,在该函数中UC_KEY(uc_server)和用户可控的http header共同产生了用于权限认证的sid,因此我们可以伪造sid绕过一些权限检测。还有seccode的相关利用,在这里就不一一介绍。

整个discuz的程序其实是包含了discuz主程序和Ucenter,Ucenter更依赖于固定密钥体系,个人感觉Ucenter的漏洞可能要比discuz主程序好挖些,你可以去试试。

4. UC_MYKEY

UC_MYKEY主要用来加密和解密UC_KEY(discuz),如下所示:

authkey(uc_server)存储在数据库的pre_ucenter_applications中的authkey字段,authkey(uc_server)生成的代码如下:

现在我们就可以知道其实UC_KEY(dz)是可以从2个地方获取到的,一个是配置文件,一个是数据库。对discuz比较熟悉的同学这里会发现一个问题,通过注入获得的authkey (uc_server),有时候可以直接当UC_KEY(dz)用,但有时候发现是一个大于64位的字符串或小于64位的字符串。这个是因为,如果你是默认discuz主程序和Ucenter安装,这个时候数据库pre_ucenter_applications中的authkey字段存储的就是UC_KEY(dz),如果你通过ucenter后台修改过UC_KEY(dz),数据库pre_ucenter_applications中的authkey字段存储的就是通过上面提到的算法计算出来的结果了,这个结果的长度是变化的,是一个大于等于40位的字符串。

总结

针对于getshell来说,在x3以前的低版本和部分未更新的x3.2以前版本,我们可以直接利用discuz的uc_key(dz)结合api/uc.php前台getshell,获得uc_key(dz)的方法有:

  1. 数据库中的authkey(uc_server)结合UC_MYKEY,这个在UCenter后台也能看见,没有使用*显示。
  2. 文件泄露等问题获得uc_key(dz)

在x3版本以后,对于key的利用主要集中在操作数据库和UCenter功能上,利用各种办法进入discuz后台,结合接下来讲到的后台GetWebShell的方法获取最终权限。

后台GetWebShell的补丁绕过

在小于x3.4的版本中,网上已经公布的利用方法是:后台修改Ucenter数据库连接信息,由于写入未转义,一句话木马直接写入config/config_ucenter.php文件中,导致代码执行。

但是在新版本的x3.4中已经修复了这个漏洞,代码如下:

补丁对 $ucdbpassnew 进行了转义,而且if(function_exists("mysql_connect") && ini_get("mysql.allow_local_infile")=="1" && constant("UC_DBHOST") != $settingnew['uc']['dbhost']), 该补丁还解决了恶意mysql文件读取的问题。

绕过补丁

通过补丁,我们知道了所有的Ucenter配置参数都会进行转义,但是我发现discuz的配置文件更改,都是利用字符替换完成的,在替换字符中,很容易出现问题,所以在源码中寻找配置修改的相关代码,最后在 api/uc.php 中找到了利用点。

在 updateapps 函数中完成了对 uc_api 的更新,这里的正则在匹配时是非贪婪的,这里就会存在一个问题,当uc_api为 define('UC_API', 'http://127.0.0.1/discuz34/uc_server\');phpinfo();//'); 时,我们执行updateapps函数来更新uc_api时就会将phpinfo();释放出来。 要使用updateapps函数来更新uc_api,我们需要知道UC_KEY(dz)的值,而UC_KEY(dz)的值,恰好是我们后台可以设置的。

利用分析

  1. 进入后台站长-Ucenter设置,设置UC_KEY=随意(一定要记住,后面要用), UC_API= http://127.0.0.1/discuz34/uc_server');phpinfo();//  

成功写进配置文件,这里单引号被转移了,我们接下来使用UC_KEY(dz)去调用api/uc.php中的updateapps函数更新UC_API。

  1. 利用UC_KEY(dz) 生成code参数,使用过UC_KEY(dz) GetWebShell的同学肯定不陌生,这里使用的UC_KEY(dz)就是上面我们设置的。
  2. 将生成的数据带入GET请求中的code 参数,发送数据包 
  3. 访问http://127.0.0.1/discuz34/config/config_ucenter.php 代码执行成功  

到此成功GetWebShell,在这个过程中,有一点需要注意的是,我们修改了程序原有的UC_KEY(dz),成功GetWebShell以后一定要修复,有2中方法:

  1. 从数据库中读取authkey(uc_server),通过UC_MYKEY解密获得UC_KEY(dz),当然也有可能authkey(uc_server)就是UC_KEY(dz)。
  2. 直接进入Ucenter后台修改UC_KEY,修改成我们GetWebShell过程中所设置的值。



个性签名:Make it
收藏