Discuz开发 Discuz! 插件审核(初阶)

[复制链接]
ARCHY明星会员实名认证 发表于 2014-6-12 17:33:50 [Discuz开发] 显示全部楼层 |阅读模式 上一主题 下一主题

马上注册,一起探讨正确快速的建站方法

您需要 登录 才可以下载或查看,没有帐号?快速注册

x
discuz! 插件审核(初阶)

1、[MySQL] 从引号开始
一般来说,ASP常常比PHP容易入侵,很大程度是因为很多ASP初学者,对数据库的安全过滤一概不知,我们常常可以看到下面的这种语句(假设members有三列, id username password,实际需要时可以穷举列数尝试攻击)。
SELECT username FROM members where id = $id
如果$id可以为人为构造的string,则可能有以下语句:
令 string $id = 9999999999 union (all/distinct) select password FROM members where id = 1
SELECT username FROM members where id = 9999999999
union select password as username FROM members where id = 1
理论上可以获得id=1(常常是管理员)的密码,虽然打开 Discuz!的SQL安全机制后,以上攻击失效。
于是,非常重要的,你必须加上一个引号,并且保证 $id 是int类型,或者已经被有效的addslashes
SELECT username FROM members where id='$id'
本人目前所知的情况,还没有有效的办法攻破上述安全处理。
80vul特别提醒插件作者,in()/limit/order by/group by 这四个地方都是容易忘记加引号的。

2、[MySQL] 重新入库问题
我们来看看一段代码(exam by freshlover,有删减)
while ($row = DB::fetch($query)) {
if( $row['tid'] == 0 && $row['check']==1) {
$thread['subject'] = cutstr($row['subject'],80,'');
DB::insert('forum_thread', $thread);
$post['subject'] = $thread['subject'];
$post['message'] = $row['subject'] . "\r\n\r\n参考答案: " . $sys_tf;
DB::insert('forum_post', $post);
}
}
我们看到 $row 是从数据库中取出的,重新入库,相当于被 stripslashes 了一回。
怎么理解呢?
假设在添加题目时,用户输入的subject(题目): C'est la vie
添加题目过程时,程序接收到的subject为 C\'est la vie
入库后,在数据库中的 subject: C'est la vie
本例程中,出库后,subject:C'est la vie
重新入库,subject:C'est la vie (此时引发 MySQL Query Error)
如果是精心准备的代码,那么理论上就可能引发攻击。

3、[Discuz插件] X版本带来的一个鲜为人知的问题
在X版本中,在插件目录下的任意以 .inc.php 结尾的文件都可以通过 plugin.php?id=xxxx:xxxx 的形式访问,不管这个文件有没有登记为模块,也可以通过这种方法被访问。
我们假使有这样的一个插件,它的目录分布如下:
template 模版目录
admincp.inc.php 后台管理文件(包括修改、结单、删除等功能)
pay.inc.php 支付部分程序(index.inc.php通过require方式调用)
list.inc.php 列表部分程序(index.inc.php通过require方式调用)
cp.inc.php 前台管理程序(功能同后台,index.inc.php通过require方式调用)
index.inc.php 主程序(包括插件数据的初始化,cp.inc.php的权限判断)
install.inc.php 安装文件(在XML中已登记)
uninstall.inc.php 卸载文件(在XML中已登记)
cron_payrecord_bakup.inc.php 计划任务文件(由install.inc.php在安装时复制到cron文件夹中)
登记的插件模块
管理中心 admincp.inc.php 支付管理
主导航项目 index.inc.php 主导航项目
我下面给出几个链接
plugin.php?id=xxxx:admincp
在admincp.inc.php没有IN_ADMINCP或者强制权限判断的情况下,我可以通过这个程序进行一些关键的操作(修改、结单、删除),其中的formhash和submitcheck都可以被绕过。
plugin.php?id=xxxx:pay
由于没有index.inc.php提供的初始化信息,在开启register_globals的主机上,可能一些插件设置被外界控制住(例如 $allowgroup 被GET传递的信息强制修改,无权限用户组突破),但由于register_globals=on的主机的确是非常少,这个反而我们可以不那么重视它。
plugin.php?id=xxxx:list
同xxxx:pay,但可能由于对后台设置缺少处理,反而因为register_globals问题中招。
plugin.php?id=xxxx:cp
之前,我们已经假使该文件的权限判断在另一个文件中,所以在没有安全常量的限制下,我们可以进行所谓的关键性操作。
plugin.php?id=xxxx:index
正常访问的连接
plugin.php?id=xxxx:install
插件进入安装过程,但因为runquery函数是在function/plugin中定义的,反而很难对数据进行破坏。
plugin.php?id=xxxx:uninstall
同xxxx:install,一般不能进行卸载攻击,但是也可能造成其他风险。
plugin.php?id=xxxx:cron_payrecord_bakup
运行计划任务程序,咋看问题不大,但是有两点。一些计划任务有明确的时间性,这个连接可能使计划任务在短时间内被重复运行。第二,计划任务很多比较消耗资源,通过计划任务实施DDOS或者CC攻击,达到的效果会快而且好。

我的一些修补建议:
1. 后台管理文件一定要记得加 IN_ADMINCP。

2. 被引用的前台文件,要不去掉“.inc“,使其无法被独立运行,要不将权限判断、初始化代码一并加入,使其可以正常独立运行,要不使用安全常量(比如IN_XXXX),没有安全常量阻止运行。但记得一定要加IN_DISCUZ的判断。

3. 计划任务程序,既然是copy函数,源文件完全可以不以和目标文件一样以 .inc.php 结尾,可以叫做cron_payrecord_bakup.source.php这样问题其实已经被解决了!而且计划任务文件可以请用户直接上传,插件以论坛根目录方式打包。

4. 安装卸载程序,出于安全的目的还有统一的要求,一致写作install.php uninstall.php,同理,更新和环境检查程序写作:upgrade.php check.php,以上名字在导出XML会自动识别,也方便插件作者,一定记得IN_DISCUZ。

4、CSRF(XSRF)问题

我现在要讲的这个问题,有两个特点:普遍、有害。绝大多数的插件,刚注意到CSRF的情况时,往往有许多作品需要大范围地改写,而且它具有有害性,即凡是CSRF攻击,往往都大大小小有利用价值,但是往往不会造成数据库一类的严重安全问题。

首先,我们先讲CSRF在普通用户范围的存在意义,我们先举一个例子,假设有一个版主投票插件,每个人点击“支持“按钮,该版主获得一票,也可以投“反对”票。

<a href="plugin.php?id=test:index&vote=225290&op=support" class="btn">支持</a>
<a href="plugin.php?id=test:index&vote=225290&op=against" class="btn">反对</a>
该版主为了争取选票,他可以在帖子(甚至签名)中嵌入以下的代码:

一旦访问这个帖子,img中的链接即被访问一次,用户在“不知不觉”中投给了他一票。
反对这个版主的人也有办法,他可以在帖子(甚至签名)中嵌入以下的代码:

一旦访问这个帖子,同理,用户在“不知不觉”中投给了他一票。

我们当然知道,这种问题并不会造成非常大的影响,但当我们关注了足够多的严重安全漏洞后,这些小小的漏洞是否值得注意呢?它们的确影响到了插件的初衷。

我们接着讲一种面向管理员的CSRF,这种方法往往需要一定的运气,一般不攻击后台,因为后台的攻击难度太大。

假设刚才的投票插件,有一个前台管理面板,里面自然应该有一个“删除该候选人”的功能,我们假设一下。

225290 cwk32 xxx票支持 xxx票反对 查看投票数据 | 删除该候选人

我们假设“删除该候选人”是这样的一个链接

<a href="plugin.php?id=test:cp&action=del&uid=225290">删除该候选人</a>

假设前台面板是这样的执行代码:
if($_G['gp_action']=='del' && $_G['adminid']==1 && ($uid=intval($_G['gp_uid']))>0){
……
}
这样的话,有一种非常需要运气的方法,只要让有权限的用户访问到“plugin.php?id=test:cp&action=del&uid=225290”这个链接,即可将225290这个候选人删除掉,这里对管理员权限有要求,但是可以这样,比如在站务板发帖。

标题:管理员进来!
内容:…….
据了解,该方法成功率非常高,一般稍有管理的论坛,都会有元老级用户访问的。
这就是典型的面向管理员的CSRF攻击。
尤其是删除操作,我们可能没有进行更详细的判断,这个便成了高热度的情况。

修补建议:

1. 使用formhash

formhash是比较简单有效的一种方法,比如在链接中加一个formhash={FORMHASH}就有效果,在模板语法中,我们可以这样写
<a href="plugin.php?id=test:cp&action=del&uid=225290&formhash={FORMHASH}}">删除该候选人</a>
不过由于Discuz! 0629补丁所修复的一个安全问题涉及security key的取出,formhash的安全性被降低,在了解指定用户的情况下可能制造formhash,但是利用0629漏洞的例子还比较少,0day工具也没有释出,了解对方环境有一定难度,所以formhash还是可信的。

2. 使用submitcheck()

submitcheck有两种好处,一种是确定通过POST的方式提交了一个变量,另一种是对来源地址有审查,也就避免了站外的访问威胁。这在表单中,我认为是有必要添加的。submitcheck同时也会检查formhash。

一般是在添加/删除表单使用的。例如有一个添加候选人的表单,提交按钮的name="addsubmit"。
PHP代码中可以这样写:
if(submitcheck('addsubmit')){
….
}
这样,提交时我可以肯定四个要点:POST提交,有FORMHASH,来源有限,不是FLASH,安全性能提高很多。
如果要对删除操作进行这个检查,通常是通过弹出窗口进行confirm。




上一篇:Discuz x3.2默认模板宽窄屏切换的解决方法
下一篇:个人资料和栏目分组丢失的解决方法

大神点评1

Legen 发表于 2014-6-13 12:31:28 [Discuz开发] 显示全部楼层
不错,谢谢分享
情感文章www.kuachen.com散文
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

快速回复 返回顶部 返回列表