涉及安全漏洞的版本
Discuz! 6.1
Discuz! 7.0
SupeSite 6.0 X-Space 4.0 UC
SupeSite 6.0.1 X-Space 4.0.1 UC
原因——SupeSite X-Space UC部分
Discuz!在安装SupeSite X-Space UC版之后,会在Discuz!的forums表中增加两个字段,Discuz! 6.1增加字段如下:
--- cdb_forums.6.1.discuz.sql
+++ cdb_forums.6.1.supesite.sql
@@ -30,15 +30,17 @@
`modnewposts` tinyint(1) NOT NULL default '0',
`jammer` tinyint(1) NOT NULL default '0',
`disablewatermark` tinyint(1) NOT NULL default '0',
`inheritedmod` tinyint(1) NOT NULL default '0',
`autoclose` smallint(6) NOT NULL default '0',
`forumcolumns` tinyint(3) unsigned NOT NULL default '0',
`threadcaches` tinyint(1) NOT NULL default '0',
`alloweditpost` tinyint(1) unsigned NOT NULL default '1',
`simple` tinyint(1) unsigned NOT NULL,
`modworks` tinyint(1) unsigned NOT NULL,
`allowtag` tinyint(1) NOT NULL default '1',
+ `updateline` int(10) NOT NULL default '0',
+ `allowpost` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`fid`),
KEY `forum` (`status`,`type`,`displayorder`),
KEY `fup` (`fup`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
Discuz! 7.0增加字段如下:
--- cdb_forums.7.0.supesite.sql
+++ cdb_forums.7.0.discuz.sql
@@ -32,17 +32,15 @@
`jammer` tinyint(1) NOT NULL default '0',
`disablewatermark` tinyint(1) NOT NULL default '0',
`inheritedmod` tinyint(1) NOT NULL default '0',
`autoclose` smallint(6) NOT NULL default '0',
`forumcolumns` tinyint(3) unsigned NOT NULL default '0',
`threadcaches` tinyint(1) NOT NULL default '0',
`alloweditpost` tinyint(1) unsigned NOT NULL default '1',
`simple` tinyint(1) unsigned NOT NULL,
`allowtag` tinyint(1) NOT NULL default '1',
`modworks` tinyint(1) unsigned NOT NULL,
`allowglobalstick` tinyint(1) NOT NULL default '1',
- `updateline` int(10) NOT NULL default '0',
- `allowpost` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`fid`),
KEY `forum` (`status`,`type`,`displayorder`),
KEY `fup` (`fup`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=35 ;
其中allowpost字段用于SupeSite X-Space UC的网站管理平台中“论坛相关设置”菜单的“论坛版块”菜单项的“是否允许导入”列。当选中“是否允许导入”复选框时,allowpost字段的值为 1,当取消“是否允许导入”复选框时,allowpost字段的值为0。
原因——Discuz!部分
Discuz!对于用户能否在版面发表主题主要涉及三个设置:
用户所在用户组是否允许发表主题
用户在该版面是否拥有发表主题特殊权限
版面是否允许用户所在的用户组发表主题
这三个设置的判断顺序如下:
用户所在的用户组不允许发表主题——不能发表主题
用户所在的用户组允许发表主题
用户在该版面拥有允许发表主题的特殊权限——能发表主题
用户在该版面拥有禁止发表主题的特殊权限——不能发表主题
用户在该版面不拥有发表主题的特殊权限
版面允许用户所在的用户组发表主题——能发表主题
版面不允许用户所在的用户组发表主题——不能发表主题
这三个设置在数据库中的来源如下:
用户所在用户组是否允许发表主题
数据库usergroups表的allowpost字段,值为1时代表允许发表主题,值为0时代表不允许发表主题。
用户在该版面是否拥有发表主题特殊权限
数据库members表的accessmasks字段,值为0时代表该用户在所有版面都不拥有发表主题特殊权限,值为1时代表该用户在某个版面拥有发表主题特殊权限。
数据库access表的allowpost字段,值为0时代表不拥有发表主题的特殊权限,值为1时代表拥有允许发表主题的特殊权发,值为-1时代表拥有不允许发表主题的特殊权限。
版面是否允许用户所在的用户组发表主题
数据库forumfields表的postperm字段,值以"\t"为分隔符号连接允许用户发表主题的用户组的ID。
这三个设置在程序中的来源如下:
用户所在用户组是否允许发表主题
在/forumdata/cache/usergroup_[usergroup id].php缓存文件中的$allowpost变量。其中[usergroup id]代表用户所在用户组的编号,值为'1'时代表允许发表主题,值为'0'时代表不允许发表主题。
用户在该版面是否拥有发表主题特殊权限
在common.inc.php文件的131行~179行(Discuz! 6.1)或138行~187行(Discuz! 7.0),赋值给$accessmasks变量,值为'0'时代表该用户在所有版面都不拥有发表主题特殊权限,值为'1'时代表该用户在某个版面拥有发表主题特殊权限。
在common.inc.php文件的201行~204行(Discuz! 6.1)或212行~215行(Discuz! 7.0),依据$accessmasks变量SQL语句相关段落赋值给$accessadd1和$accessadd2变量。
在common.inc.php文件的271行~283行(Discuz! 6.1)或286行~298行(Discuz! 7.0),使用$accessadd1和$accessadd2变量生成SQL语句查询出$forum数组,其中$forum['allowpost'] 代表用户在该版面是否拥有发表主题特殊权限,值为NULL时代表用户在该版面不拥有任何特殊权限,也就不拥有发表主题的特殊权限,值为0时代表不拥有发表主题的特殊权限,值为1时代表拥有允许发表主题的特殊权发,值为-1时代表拥有不允许发表主题的特殊权限。
版面是否允许用户所在的用户组发表主题
在viewthread.php文件的206行(Discuz! 6.1)或211行(Discuz! 7.0),当用户在该版面不拥有发表主题的特殊权限时,使用forumperm($forum['postperm']))分解允许用户发表主题的用户组的ID,然后在用户的用户组ID以及扩展组ID列表中查找是否包含。
结果——互相作用导致的漏洞
SupeSite X-Space UC在forums表中增加的allowpost字段与access表中的allowpost字段在查询时同时影响查询结果的$forum['allowpost']变量,情况如下:
用户在该版面拥有特殊权限,但是用户在该版面不拥有发表主题的特殊权限,$forum['allowpost']不变为'0',不受影响。
用户在该版面拥有特殊权限,但是用户在该版面拥有允许发表主题的特殊权限,$forum['allowpost']不变为'1',不受影响。
用户在该版面拥有特殊权限,但是用户在该版面拥有禁止发表主题的特殊权限,$forum['allowpost']不变为'-1',不受影响。
用户在该版面不拥有任何特殊权限
forums表中allowpost字段的值为0,$forum['allowpost']从NULL变为'0'
/include/newthread.inc.php第31行,empty($forum['allowpost']),值不变为true,不受影响。
/include/newthread.inc.php第37行,$forum['allowpost'] == -1,值不变为false,不受影响。
/include/newtrade.inc.php第30行,empty($forum['allowpost']),值不变为true,不受影响。
/include/newtrade.inc.php第36行,$forum['allowpost'] == -1,值不变为false,不受影响。
/wap/include/post.inc.php第50行,empty($forum['allowpost']),值不变为true,不受影响。
/forumdisplay.php第421行,$forum['allowpost'] == 1,值不变为false,不受影响。
/forumdisplay.php第423行,$forum['allowpost'] != -1,值不变为true,不受影响。
/post.php第276行,$forum['allowpost'] == -1,值不变为false,不受影响。
/post.php第282行,$forum['allowpost'] == -1,值不变为false,不受影响。
/post.php第285行,$forum['allowpost'] == -1,值不变为false,不受影响。
/viewthread.php第206行第1处,$forum['allowpost'] != -1,值不变为true,不受影响。
/viewthread.php第206行第2处,|| $forum['allowpost'],值不变为false,不受影响。
forums表中allowpost字段的值为1,$forum['allowpost']从NULL变为'1'
/include/newthread.inc.php第31行,empty($forum['allowpost']),值从true变为false,受影响。
/include/newthread.inc.php第37行,$forum['allowpost'] == -1,值不变为false,不受影响。
/include/newtrade.inc.php第30行,empty($forum['allowpost']),值从true变为false,受影响。
/include/newtrade.inc.php第36行,$forum['allowpost'] == -1,值不变为false,不受影响。
/wap/include/post.inc.php第50行,empty($forum['allowpost']),值从true变为false,受影响。
/forumdisplay.php第421行,$forum['allowpost'] == 1,值从false变为true,受影响。
/forumdisplay.php第423行,$forum['allowpost'] != -1,值不变为true,不受影响。
/post.php第276行,$forum['allowpost'] == -1,值不变为false,不受影响。
/post.php第282行,$forum['allowpost'] == -1,值不变为false,不受影响。
/post.php第285行,$forum['allowpost'] == -1,值不变为false,不受影响。
/viewthread.php第206行第1处,$forum['allowpost'] != -1,值不变为true,不受影响。
/viewthread.php第206行第2处,|| $forum['allowpost'],值从false变为true,受影响。
受影响处会导致的安全漏洞如下:
/include/newthread.inc.php第31行,当用户所在的用户组允许发表主题,用户在该版面不拥有发表主题的特殊权限,版面不允许用户所在的用户组发表主题时,应该不能发表主题。但是forums表中allowpost字段的值为1时,此处程序认为用户在该版面拥有允许发表主题的特殊权限,因此能发表主题。
/include/newtrade.inc.php第30行,当用户所在的用户组允许发表主题,用户在该版面不拥有发表主题的特殊权限,版面不允许用户所在的用户组发表主题时,应该不能发表主题。但是forums表中allowpost字段的值为1时,此处程序认为用户在该版面拥有允许发表主题的特殊权限,因此能发表主题。
/wap/include/post.inc.php第50行,当用户所在的用户组允许发表主题,用户在该版面不拥有发表主题的特殊权限,版面不允许用户所在的用户组发表主题时,应该不能发表主题。但是forums表中allowpost字段的值为1时,此处程序认为用户在该版面拥有允许发表主题的特殊权限,因此能发表主题。
/forumdisplay.php第421行,当用户所在的用户组允许发表主题,用户在该版面不拥有发表主题的特殊权限,版面不允许用户所在的用户组发表主题时,应该不能发表主题,所以不显示“新贴”以及特殊主题链接。但是forums表中allowpost字段的值为1时,此处程序认为用户在该版面拥有允许发表主题的特殊权限,因此能发表主题,所以显示“新贴”以及特殊主题链接。
/viewthread.php第206行第2处,当用户所在的用户组允许发表主题,用户在该版面不拥有发表主题的特殊权限,版面不允许用户所在的用户组发表主题时,应该不能发表主题,所以不显示“新贴”以及特殊主题链接。但是forums表中allowpost字段的值为1时,此处程序认为用户在该版面拥有允许发表主题的特殊权限,因此能发表主题,所以显示“新贴”以及特殊主题链接。
补丁
由于该问题涉及Discuz!的全局包含文件/include/common.inc.php,为解决该缺陷,同时不影响其他功能,Discuz! 6.1可以通过如下补丁解决。
--- common.inc.6.1.discuz.php
+++ common.inc.6.1.supesite.php
@@ -261,28 +261,28 @@
}
$auditstatuson = !empty($mod) && $mod == 'edit' && in_array($adminid, array(1, 2, 3)) && $allowmodpost ? true : false;
$page = isset($page) ? max(1, intval($page)) : 1;
$tid = isset($tid) && is_numeric($tid) ? $tid : 0;
$fid = isset($fid) && is_numeric($fid) ? $fid : 0;
$typeid = isset($typeid) ? intval($typeid) : 0;
if(!empty($tid) || !empty($fid)) {
if(empty($tid)) {
- $forum = $db->fetch_first("SELECT f.fid, f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
+ $forum = $db->fetch_first("SELECT f.fid, f.fup, f.type, f.name, f.status, f.displayorder, f.styleid, f.threads, f.posts, f.todayposts, f.lastpost, f.allowsmilies, f.allowhtml, f.allowbbcode, f.allowimgcode, f.allowmediacode, f.allowanonymous, f.allowshare, f.allowpostspecial, f.allowspecialonly, f.alloweditrules, f.recyclebin, f.modnewposts, f.jammer, f.disablewatermark, f.inheritedmod, f.autoclose, f.forumcolumns, f.threadcaches, f.alloweditpost, f.simple, f.modworks, f.allowtag, ff.* $accessadd1 $modadd1, f.fid AS fid
FROM {$tablepre}forums f
LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
WHERE f.fid='$fid'");
} else {
- $forum = $db->fetch_first("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
+ $forum = $db->fetch_first("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.fid, f.fup, f.type, f.name, f.status, f.displayorder, f.styleid, f.threads, f.posts, f.todayposts, f.lastpost, f.allowsmilies, f.allowhtml, f.allowbbcode, f.allowimgcode, f.allowmediacode, f.allowanonymous, f.allowshare, f.allowpostspecial, f.allowspecialonly, f.alloweditrules, f.recyclebin, f.modnewposts, f.jammer, f.disablewatermark, f.inheritedmod, f.autoclose, f.forumcolumns, f.threadcaches, f.alloweditpost, f.simple, f.modworks, f.allowtag, ff.* $accessadd1 $modadd1, f.fid AS fid
FROM {$tablepre}threads t
INNER JOIN {$tablepre}forums f ON f.fid=t.fid
LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
WHERE t.tid='$tid'".($auditstatuson ? '' : " AND t.displayorder>='0'")." LIMIT 1");
$tid = $forum['tid'];
}
if($forum) {
$fid = $forum['fid'];
$forum['ismoderator'] = !empty($forum['ismoderator']) || $adminid == 1 || $adminid == 2 ? 1 : 0;
foreach(array('postcredits', 'replycredits', 'threadtypes', 'digestcredits', 'postattachcredits', 'getattachcredits') as $key) {
Discuz! 7.0可以通过如下补丁解决。
--- common.inc.7.0.supesite.php
+++ common.inc.7.0.discuz.php
@@ -276,28 +276,28 @@
$page = isset($page) ? max(1, intval($page)) : 1;
$tid = isset($tid) && is_numeric($tid) ? $tid : 0;
$fid = isset($fid) && is_numeric($fid) ? $fid : 0;
$typeid = isset($typeid) ? intval($typeid) : 0;
$modthreadkey = isset($modthreadkey) && $modthreadkey == modthreadkey($tid) ? $modthreadkey : '';
$auditstatuson = $modthreadkey ? true : false;
if(!empty($tid) || !empty($fid)) {
if(empty($tid)) {
- $forum = $db->fetch_first("SELECT f.fid, f.fup, f.type, f.name, f.status, f.displayorder, f.styleid, f.threads, f.posts, f.todayposts, f.lastpost, f.allowsmilies, f.allowhtml, f.allowbbcode, f.allowimgcode, f.allowmediacode, f.allowanonymous, f.allowshare, f.allowpostspecial, f.allowspecialonly, f.alloweditrules, f.allowfeed, f.recyclebin, f.modnewposts, f.jammer, f.disablewatermark, f.inheritedmod, f.autoclose, f.forumcolumns, f.threadcaches, f.alloweditpost, f.simple, f.modworks, f.allowtag, f.allowglobalstick, ff.* $accessadd1 $modadd1, f.fid AS fid
+ $forum = $db->fetch_first("SELECT f.fid, f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
FROM {$tablepre}forums f
LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
WHERE f.fid='$fid'");
} else {
- $forum = $db->fetch_first("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.fid, f.fup, f.type, f.name, f.status, f.displayorder, f.styleid, f.threads, f.posts, f.todayposts, f.lastpost, f.allowsmilies, f.allowhtml, f.allowbbcode, f.allowimgcode, f.allowmediacode, f.allowanonymous, f.allowshare, f.allowpostspecial, f.allowspecialonly, f.alloweditrules, f.allowfeed, f.recyclebin, f.modnewposts, f.jammer, f.disablewatermark, f.inheritedmod, f.autoclose, f.forumcolumns, f.threadcaches, f.alloweditpost, f.simple, f.modworks, f.allowtag, f.allowglobalstick, ff.* $accessadd1 $modadd1, f.fid AS fid
+ $forum = $db->fetch_first("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
FROM {$tablepre}threads t
INNER JOIN {$tablepre}forums f ON f.fid=t.fid
LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
WHERE t.tid='$tid'".($auditstatuson ? '' : " AND t.displayorder>='0'")." LIMIT 1");
$tid = $forum['tid'];
}
if($forum) {
$fid = $forum['fid'];
$forum['ismoderator'] = !empty($forum['ismoderator']) || $adminid == 1 || $adminid == 2 ? 1 : 0;
foreach(array('postcredits', 'replycredits', 'threadtypes', 'threadsorts', 'digestcredits', 'postattachcredits', 'getattachcredits') as $key) {
后记
开源有助于程序接受更广泛的代码复审,及时发现安全隐患并改进,本bug源于SupeSite X-Space UC,由于SupeSite X-Space UC是闭源软件,因此补丁从Discuz!入手,并非治本之策。
[Discuz! - 官方网站] |