报价通知插件中的一个漏洞



以下插件用于在 vbulletin 论坛中为成员提供通知。

一位朋友告诉我,由于这个插件,Mysql 数据库可能存在漏洞。 我不是MySQL专家,所以我无法说出问题到底在哪里。

如果有人发现此漏洞,我想知道我应该怎么做才能修复它。

插件附带了这个 php 页面,将显示成员的通知列表 http://textuploader.com/d1hch不确定此 PHP 脚本是否存在任何漏洞

该问题可能与 mysqli::escape_string 或 PDO 准备的声明有关。

<?xml version="1.0" encoding="ISO-8859-1"?>
<product productid="k_quote_notifications" active="0">
<title>Quote Notifications</title>
<description>Notify User after being quoted</description>
<version>1.0.0</version>
<url></url>
<versioncheckurl></versioncheckurl>
<dependencies>
</dependencies>
<codes>
<code version="1.0.0">
<installcode><![CDATA[$db->hide_errors();
$db->query_write("
CREATE TABLE   " . TABLE_PREFIX . "quotedatanew (
`quoted` int(10) NOT NULL default '0',
`quoter` int(10) NOT NULL default '0',
`quotername` varchar(255) NOT NULL default '0',
`postid` int(10) NOT NULL default '0',
`threadid` int(10) NOT NULL default '0',
`threadtitle` varchar(255) NOT NULL default '0',
`dateline` int(10) NOT NULL default '0',
`hasseen` int(1) NOT NULL default '0',
KEY `quoted` (`quoted`)
)
");
$db->show_errors();]]></installcode>
<uninstallcode><![CDATA[$db->query_write("
DROP TABLE " . TABLE_PREFIX . "quotedatanew
");]]></uninstallcode>
</code>
</codes>
<templates>
<template name="NOTI" templatetype="template" date="1401826799" username="AAA" version="1.0.0"><![CDATA[$stylevar[htmldoctype]
<html dir="$stylevar[textdirection]" lang="$stylevar[languagecode]">
<head>
<title>$vboptions[bbtitle]</title>
<style type="text/css">
.alert-box {
color:#555;
border-radius:10px;
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:11px;
padding:10px 36px;
margin:10px;
}
.alert-box span {
font-weight:bold;
text-transform:uppercase;
}
.error {
background:#ffecec url('images/error.png') no-repeat 10px 50%;
border:1px solid #f5aca6;
}
.success {
background:#e9ffd9 url('images/success.png') no-repeat 10px 50%;
border:1px solid #a6ca8a;
}
.warning {
background:#fff8c4 url('images/warning.png') no-repeat 10px 50%;
border:1px solid #f2c779;
}
.notice {
background:#e3f7fc url('images/notice.png') no-repeat 10px 50%;
border:1px solid #8ed9f6;
}
.paginate {
font-family:Arial, Helvetica, sans-serif;
padding: 3px;
margin: 3px;
}
.paginate a {
padding:2px 5px 2px 5px;
margin:2px;
border:1px solid #999;
text-decoration:none;
color: #666;
}
.paginate a:hover, .paginate a:active {
border: 1px solid #999;
color: #000;
}
.paginate span.current {
margin: 2px;
padding: 2px 5px 2px 5px;
border: 1px solid #999;
font-weight: bold;
background-color: #999;
color: #FFF;
}
.paginate span.disabled {
padding:2px 5px 2px 5px;
margin:2px;
border:1px solid #eee;
color:#DDD;
}
</style>
$headinclude
</head>
<body>
$header
$navbar
<table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" border="0" width="100%" align="center">
<tr>
<td class="tcat">$vbphrase[notification_page_title]</td>
</tr>
<tr>
<td class="alt1">$content</td>
</tr>
$paginate
</table>
$footer
</body>
</html>]]></template>
<template name="TEST" templatetype="template" date="1401824077" username="AAA" version="1.0.0"><![CDATA[$stylevar[htmldoctype]
<html dir="$stylevar[textdirection]" lang="$stylevar[languagecode]">
<head>
<title>$vboptions[bbtitle]</title>
$headinclude
</head>
<body>
$header
$navbar
<table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" border="0" width="100%" align="center">
<tr>
<td class="tcat">Title</td>
</tr>
<tr>
<td class="alt1">Text</td>
</tr>
</table>
$footer
</body>
</html>]]></template>
</templates>
<plugins>
<plugin active="1" executionorder="5">
<title>Get Notifications</title>
<hookname>global_start</hookname>
<phpcode><![CDATA[$counter = $vbulletin->db->query_first("SELECT COUNT(*) AS id FROM quotedatanew where quoted =" . $vbulletin->userinfo['userid'] . " and hasseen= '0'");
$count   = $counter['id'];
$notifi  = $vbulletin->db->query_read("SELECT * FROM quotedatanew where quoted =" . $vbulletin->userinfo['userid'] . " and hasseen = '0' ORDER BY dateline DESC LIMIT 4");
while ($noti = $vbulletin->db->fetch_array($notifi)) {
$threadurl    = $vbulletin->options['bburl'] . '/showthread.php?source=noti&p=' . $noti['postid'] . '#post' . $noti['postid'];
$memberurl    = $vbulletin->options['bburl'] . '/member.php?u=' . $noti['quoter'];
$notiurl      = $vbulletin->options['bburl'] . '/noti.php';
$qoutername   = $noti['quotername'];
$threadname   = $noti['threadtitle'];
$phrasequote  = $vbphrase['has_quoted_your_post_in'];
$seeallphrase = $vbphrase['see_all_noti'];
$notihtml .= '<tr><td class="vbmenu_option" style="white-space:normal;max-width:200px;"><a href="' . $memberurl . '">' . $qoutername . '</a> ' . $phrasequote . ' <a href="' . $threadurl . '">' . $threadname . '</a></td></tr>';
}
if ($count > 4) {
$notihtml .= '<tr><td class="vbmenu_option" style="white-space:normal;max-width:200px;"><a href="' . $notiurl . '">' . $seeallphrase . '</a></td></tr>';
}]]></phpcode>
</plugin>
<plugin active="1" executionorder="5">
<title>Insert Notification</title>
<hookname>newpost_complete</hookname>
<phpcode><![CDATA[if ($vbulletin->options['wqm_system'] == true) {

if (preg_match('/[quote=(.*?)]((?:.|s)+?)[/quote]/i', $post['message'])) {
preg_match_all('/[quote=(.*?)]((?:.|s)+?)[/quote]/i', $post['message'], $quotematch);
$quotecount = count($quotematch[0]);
$tempcount  = 0;
$quotearray = array();

while ($tempcount < $quotecount) {
$username     = explode(';', $quotematch[1][$tempcount]);
$quoteduserid = $vbulletin->db->query_first("SELECT userid FROM " . TABLE_PREFIX . "user
WHERE username = '" . $vbulletin->db->escape_string(htmlspecialchars_uni($username[0])) . "'");
if (!in_array($quoteduserid['userid'], $quotearray)) {
if ($quoteduserid['userid'] > 0 AND $quoteduserid['userid'] != $vbulletin->userinfo['userid']) {
$quotearray[] = $quoteduserid['userid'];
// check forum permissions
$quoteduserinfo = fetch_userinfo(intval($quoteduserid['userid']));
$forumperms     = fetch_permissions($foruminfo['forumid'], intval($quoteduserid['userid']), $quoteduserinfo);
if (!($forumperms & $vbulletin->bf_ugp_forumpermissions['canview']) OR !($forumperms & $vbulletin->bf_ugp_forumpermissions['canviewthreads'])) {
$tempcount++;
continue;
}
if (!($forumperms & $vbulletin->bf_ugp_forumpermissions['canviewothers']) AND ($threadinfo['postuserid'] != intval($quoteduserid['userid']) OR $vbulletin->userinfo['userid'] == 0)) {
$tempcount++;
continue;
}
$vbulletin->db->query_write("
INSERT INTO " . TABLE_PREFIX . "quotedatanew (quoted,quoter,postid,threadid,threadtitle,dateline,quotername)
VALUES ('" . $quoteduserid['userid'] . "','" . $vbulletin->userinfo['userid'] . "','" . $post['postid'] . "','" . $threadinfo[threadid] . "','" . $vbulletin->db->escape_string(htmlspecialchars_uni($threadinfo[title])) . "','" . time() . "','" . $vbulletin->db->escape_string(htmlspecialchars_uni($vbulletin->userinfo['username'])) . "')");
}
}
$tempcount++;
}
}

}]]></phpcode>
</plugin>
<plugin active="1" executionorder="5">
<title>Dismiss notification</title>
<hookname>showthread_start</hookname>
<phpcode><![CDATA[$postid = intval($_GET["p"]);
$source = $_GET["source"];
if ($source == "noti") {
$vbulletin->db->query_write("update quotedatanew set hasseen = '1' where postid = '" . $postid . "' and quoted = '" . $vbulletin->userinfo['userid'] . "'");
}]]></phpcode>
</plugin>
</plugins>
<phrases>
<phrasetype name="GLOBAL" fieldname="global">
<phrase name="delete_all_nots" date="1401999437" username="bbb" version="1.0.0"><![CDATA[Õ–› ÄÌ⁄ «· ‰»Ì« ]]></phrase>
<phrase name="has_quoted_your_post_in" date="1401739493" username="AAA" version="1.0.0"><![CDATA[Has Quoted Your Post In]]></phrase>
<phrase name="no_notification_text" date="1401999471" username="bbb" version="1.0.0"><![CDATA[·« ÌÊÃœ  ‰»Ì«  Ü̜…]]></phrase>
<phrase name="noti_icon" date="1401829523" username="AAA" version="1.0.0"><![CDATA[Notifications]]></phrase>
<phrase name="notification_read" date="1401825953" username="AAA" version="1.0.0"><![CDATA[Read]]></phrase>
<phrase name="notification_unread" date="1401826023" username="AAA" version="1.0.0"><![CDATA[Unread]]></phrase>
<phrase name="see_all_noti" date="1401810412" username="AAA" version="1.0.0"><![CDATA[See All Notification]]></phrase>
</phrasetype>
<phrasetype name="vBulletin Settings" fieldname="vbsettings">
<phrase name="setting_wqm_system_desc" date="1401828337" username="AAA" version="1.0.0"><![CDATA[<style type="text/css">
body { background:#555;color:white; }
a:link, a:visited, a:active { color:white; }
.optiontitle { background:#10a113;color:#FFF;border:none; }
.button { background:#10A113;border:none;color:white;padding:6px 12px;}
.button:hover { background:#057c08; }
.tcat { color:white;background: #111;border:none; }
.tcat a:link, .tcat a:visited, .tcat a:active { color:white; }
.tfoot { background:#111;border:none; }
.alt1 { color:white;background:#333; }
.tborder { border:1px solid #000; -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;-moz-box-shadow: 2px 2px 8px #000;-webkit-box-shadow: 2px 2px 8px #000;box-shadow: 2px 2px 8px #000; }
textarea, .bginput, input.col-c, input.col-i, input.col-g { border:1px solid #000;color:#EEE;background:#444; }
.pagetitle { background:#111;color:white;border:1px solid #000;-moz-box-shadow: 2px 2px 8px #000;-webkit-box-shadow: 2px 2px 8px #000;box-shadow: 2px 2px 8px #000; }
</style>]]></phrase>
<phrase name="setting_wqm_system_title" date="1401828337" username="AAA" version="1.0.0"><![CDATA[Enable Notifications System]]></phrase>
<phrase name="settinggroup_Notification System" date="1401828329" username="AAA" version="1.0.0"><![CDATA[Notification System]]></phrase>
</phrasetype>
</phrases>
<options>
<settinggroup name="Notification System" displayorder="590">
<setting varname="wqm_system" displayorder="10">
<datatype>boolean</datatype>
<optioncode>yesno</optioncode>
<defaultvalue>1</defaultvalue>
</setting>
</settinggroup>
</options>
<helptopics>
</helptopics>
<cronentries>
</cronentries>
<faqentries>
</faqentries>
</product>

我看到此代码的唯一潜在问题是查询是通过连接字符串而不是使用预准备语句来构建的,例如:

$quoteduserid = $vbulletin->db->query_first("SELECT userid FROM " . TABLE_PREFIX . "user
WHERE username = '" . $vbulletin->db->escape_string(htmlspecialchars_uni($username[0])) . "'");

这会将查询暴露给潜在的 sql 注入攻击。

我在代码中看到采取了预防措施,例如 where 子句中的username由以下部分组成:

username = '" . $vbulletin->db->escape_string(htmlspecialchars_uni($username[0])) . "'"

username被逃逸db->escape_string,这应该可以防止注入攻击。但是该函数中的错误可能会导致代码不安全。

唯一推荐且防弹的查询方法是使用准备好的 staetements。

这里是准备好的语句的链接 mysqli

预准备语句允许您将查询中使用的参数(例如用户名)直接传递给 mysqli 或 pdo。使用这种方法,参数中的字符序列无法"中断"查询,因为当查询是通过静态字符串和(转义的)变量值的串联构建时可能发生的。


如果要改进代码,则必须以"预准备语句形式"替换每个查询。

预准备语句查询很容易(只需检查$vbullettin->db是 mysqli 还是 PDO 连接即可使用正确的方法)。

由于参数必须以未转义的方式传递,因此您应该检查$vbulletin->db->escape_string实际执行的操作(在示例中),以便存储在数据库中的数据保持其格式。

最新更新