| DetailsAffected Software:The Hacker’s Diet (WordPress Plugin) Fixed in Version:0.9.7b Issue Type:SQL Injection Original Code: Found Here DescriptionThis week’s vulnerability was a SQL injection vulnerability affecting the Hacker’s Diet WordPress plugin. In the vulnerable version,the plugin assigns several variables using values obtained directly from the querystring. The variable assignments are shown below: $weeks = $_GET["weeks"]; $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $user_id = $_GET["user"]; $maint_mode = $_GET["maint_mode"];
No sanitization or validation is done before assigning the values. Once the assignments are made,the attacker controlled values are then passed to a dynamic SQL string here resulting in SQL Injection: $query = “select date,weight,trend from “.$table_prefix.”hackdiet_weightlog where wp_id = $user_id and date >\”".date(“Y-m-d”,strtotime(“$weeks weeks ago”)).”\”order by date asc”; $query = “select date,weight,trend from “.$table_prefix.”hackdiet_weightlog where wp_id = $user_id and date >= \”$start_date\”and date <= \”$end_date\”order by date asc”;
The plugin authors patched this vulnerability by validating that the $_GET[“user”] and $_GET[“weeks”] parameters contains only numeric characters. An interesting exercise would be to trace through the code and find where the following variables are being used: $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $maint_mode = $_GET["maint_mode"];
Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| <?php include (dirname(__FILE__)."/jpgraph/jpgraph.php"); include (dirname(__FILE__)."/jpgraph/jpgraph_line.php"); include (dirname(__FILE__)."/jpgraph/jpgraph_scatter.php");
// get our db settings without loading all of wordpress every save $html = implode('',file("../../../wp-config.php")); $html = str_replace ("require_once","// ",$html); $html = str_replace ("<?php","",$html); $html = str_replace ("?>","",$html); eval($html);
mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); mysql_select_db(DB_NAME);
+if (!is_numeric($_GET["user"]) || !is_numeric($_GET["weeks"])) { + exit; +}
$weeks = $_GET["weeks"]; $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $user_id = $_GET["user"]; $maint_mode = $_GET["maint_mode"];
if ($weeks) { - $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date >\"".date("Y-m-d",strtotime("$weeks weeks ago"))."\"order by date asc"; + $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = \"". $user_id . "\"and date >\"".date("Y-m-d",strtotime("$weeks weeks ago"))."\"order by date asc"; } else if ($start_date and $end_date) { - $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date >= \"$start_date\"and date <= \"$end_date\"order by date asc"; + $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = \"". $user_id . "\"and date >= \"$start_date\"and date <= \"$end_date\"order by date asc"; }
result = mysql_query($query); if (mysql_num_rows($result)) { if (mysql_num_rows($result) == 1) { // only one day,gotta finagle the display
$row = mysql_fetch_assoc($result);
// fake day before $weight_data[] = 0; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime("yesterday",strtotime($row["date"])));
// data $weight_data[] = $row["weight"]; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime($row["date"]));
// fake day after $weight_data[] = 0; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime("tomorrow",strtotime($row["date"]))); } else { $num_rows = mysql_num_rows($result); if ($num_rows <= 7 * 2) { // 0-2 weeks $ticks = "daily"; } else if ($num_rows <= 31 * 4) { // 2 weeks - 4 months $ticks = "weekly"; } else { // 4 months + $ticks = "monthly"; }
$count = 1; while ($row = mysql_fetch_assoc($result)) { $weight_data[] = $row["weight"]; $trend_data[] = $row["trend"]; if ($goal >0) { $goal_data[] = $goal; } switch ($ticks) { case "weekly": if ($count == 1) { $x_data[] = date("n/j",strtotime($row["date"])); } else { $x_data[] = ""; if ($count == 7) { $count = 0; } } break; case "monthly": if (date("j",strtotime($row["date"])) == "1") { $x_data[] = date("n/j",strtotime($row["date"])); } else { $x_data[] = ""; } break; case "daily": default: $x_data[] = date("n/j",strtotime($row["date"])); break; }
$count++; } } |
DetailsAffected Software:DojoToolkit Fixed in Version:1.4.2 Issue Type:XSS Original Code: Found Here DescriptionThis week’s vulnerability is a DOM based XSS that could be found in a JavaScript file provided by the DojoToolkit. This JavaScript file was included (via script src) in many pages throughout the DojoToolkit,making those pages vulnerable to XSS. Unlike traditional XSS bugs,server side processing is not required for certain types of DOM based XSS. This is an important concept to understand as some code auditors will skip static pages assuming the attacker will not have the ability to control any values used by the page. The bug starts here: if(window.location.href.indexOf(“?”) >-1){
The JavaScript pulls the address of the loaded page and checks to see if the address contains the “?” character. If the “?” character is found,the JavaScript begins parsing and splitting the URI into various arrays. This parsing and splitting is done in the lines provided below: var str = window.location.href.substr(window.location.href.indexOf(“?”)+1).split(/#/); var ary = str[0].split(/&/); for(var i=0;i<ary.length;i++){ var split = ary[i].split(/=/),
The vulnerable assignment occurs here: value = split[1];
The JavaScript above essentially grabs a querystring value (attacker supplied) and assigns it to the “value” variable. Later,the “value” variable is used in several places,for example: dojo.config.locale = locale = value; document.getElementsByTagName(“html”)[0].dir = value; theme = value;
Considering the assignments listed above,we have a couple different variables that are tainted. I’ve highlighted the tainted variables in red. Tracing the “theme” assignment shown above,we see the tainted value being passed to a document.write statement,resulting in XSS. var themeCss = d.moduleUrl(“dijit.themes”,theme+”/”+theme+”.css”); var themeCssRtl = d.moduleUrl(“dijit.themes”,theme+”/”+theme+”_rtl.css”); document.write(‘<link rel=”stylesheet”type=”text/css”href=”‘+themeCss+’”>’); document.write(‘<link rel=”stylesheet”type=”text/css”href=”‘+themeCssRtl+’”>’);
The patch checked in by the DojoToolkit team sanitizes the “value” JavaScript variable by allowing only word characters (^\w). Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| if(window.location.href.indexOf("?") >-1){ var str = window.location.href.substr(window.location.href.indexOf("?")+1).split(/#/); var ary = str[0].split(/&/); for(var i=0;i<ary.length;i++){ var split = ary[i].split(/=/), key = split[0], -value = split[1]; +value = split[1].replace(/[^\w]/g,"");// replace() to prevent XSS attack switch(key){ case "locale": // locale string | null dojo.config.locale = locale = value; break; case "dir": // rtl | null document.getElementsByTagName("html")[0].dir = value; break; case "theme": // tundra | soria | noir | squid | nihilo | null theme = value; break; case "a11y": if(value){ testMode = "dijit_a11y";} } } }
// always include the default theme files: if(theme || testMode){
if(theme){ var themeCss = d.moduleUrl("dijit.themes",theme+"/"+theme+".css"); var themeCssRtl = d.moduleUrl("dijit.themes",theme+"/"+theme+"_rtl.css"); document.write('<link rel="stylesheet"type="text/css" href="'+themeCss+'">'); document.write('<link rel="stylesheet"type="text/css" href="'+themeCssRtl+'">'); }
if(dojo.config.parseOnLoad){ dojo.config.parseOnLoad = false; dojo.config._deferParsing = true; }
d.addOnLoad(function(){
// set the classes var b = dojo.body(); if(theme){ dojo.removeClass(b,defTheme); if(!d.hasClass(b,theme)){ d.addClass(b,theme);} var n = d.byId("themeStyles"); if(n){ d.destroy(n);} } if(testMode){ d.addClass(b,testMode);} if(dojo.config._deferParsing){ // attempt to elimiate race condition introduced by this // test helper file. 120ms to allow CSS to finish/process? setTimeout(dojo.hitch(d.parser,"parse",b),120); }
}); }
})(); |
DetailsAffected Software:Openfire by Ignite Realtime Fixed in Version:3.6.1 Issue Type:XSS Original Code: Found Here DescriptionThis is a straightforward XSS bug that affecting the Admin Console of OpenFire by Ignite Realtime/Jive software. The code fix is simple,encode a tainted URL variable before using it in markup. The URL variable is assigned an attacker controlled value here: String url = ParamUtils.getParameter(request,“url”);
And is later used in the HTML markup here: <input value=”<%= url %>”>
The one line fix is to encode the URL parameter,which was done here: url = org.jivesoftware.util.StringUtils.escapeHTMLTags(url);
Looking through the code,we see that OpenFire had previously fixed an XSS vulnerability just a few lines above in the USERNAME variable. There is even comment indicating so! It surprising that the Ignite Realtime/Jive developers missed this one as it is literally two lines below the previous fix. String username = ParamUtils.getParameter(request,“username”); if (username != null){ username = JID.escapeNode(username); } // Escape HTML tags in username to prevent cross-site scripting attacks. This // is necessary because we display the username in the page below. username = org.jivesoftware.util.StringUtils.escapeHTMLTags(username); String password = ParamUtils.getParameter(request,“password”); String url = ParamUtils.getParameter(request,“url”);
The assignment of the PASSWORD variable is interesting Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
| <%!
static String go(String url) {
if (url == null) {
return "index.jsp";
}
else {
return url;
}
}
%>
<%-- Check if in setup mode --%>
<%
if (admin.isSetupMode()) {
response.sendRedirect("setup/index.jsp");
return;
}
%>
<% // get parameters
String username = ParamUtils.getParameter(request,"username");
if (username != null) {
username = JID.escapeNode(username);
}
// Escape HTML tags in username to prevent cross-site scripting attacks. This
// is necessary because we display the username in the page below.
username = org.jivesoftware.util.StringUtils.escapeHTMLTags(username);
String password = ParamUtils.getParameter(request,"password");
String url = ParamUtils.getParameter(request,"url");
+ url = org.jivesoftware.util.StringUtils.escapeHTMLTags(url);
// SSO between cluster nodes
String secret = ParamUtils.getParameter(request,"secret");
String nodeID = ParamUtils.getParameter(request,"nodeID");
String nonce = ParamUtils.getParameter(request,"nonce");
// The user auth token:
AuthToken authToken;
... SNIP ...
<html>
<head>
<title><%= AdminConsole.getAppName() %><fmt:message key="login.title"/></title>
<script language="JavaScript">
<!--
// break out of frames
if (self.parent.frames.length != 0) {
self.parent.location=document.location;
}
function updateFields(el) {
if (el.checked) {
document.loginForm.username.disabled = true;
document.loginForm.password.disabled = true;
}
else {
document.loginForm.username.disabled = false;
document.loginForm.password.disabled = false;
document.loginForm.username.focus();
}
}
//-->
</script>
<link rel="stylesheet"href="style/global.css"type="text/css">
<link rel="stylesheet"href="style/login.css"type="text/css">
</head>
<body>
<form action="login.jsp"name="loginForm"method="post">
<% if (url != null) { try { %>
<input type="hidden"value="<%= url %>">
<% } catch (Exception e) { Log.error(e);} } %>
<input value="true">
<div align="center">
<!-- BEGIN login box -->
<div id="jive-loginBox">
<div align="center">
<span id="jive-login-header"style="background:transparent url(images/login_logo.gif) no-repeat left;padding:29px 0 10px 205px;">
<fmt:message key="admin.console"/>
</span>
<div style="text-align:center;width:380px;">
<table cellpadding="0"cellspacing="0"border="0"align="center">
<tr>
<td align="right">
<table cellpadding="2"cellspacing="0"border="0">
<noscript>
<tr>
<td colspan="3">
<table cellpadding="0"cellspacing="0"border="0">
<tr valign="top">
<td><img src="images/error-16x16.gif"width="16"height="16"border="0"alt=""vspace="2"></td>
<td><div style="padding-left:5px;color:#cc0000;"><fmt:message key="login.error"/></div></td>
</tr>
</table>
</td>
</tr>
</noscript>
<% if (errors.size() >0) { %>
<tr>
<td colspan="3">
<table cellpadding="0"cellspacing="0"border="0">
<% for (String error:errors.values()) { %>
<tr valign="top">
<td><img src="images/error-16x16.gif"width="16"height="16"border="0"alt=""vspace="2"></td>
<td><div style="padding-left:5px;color:#cc0000;"><%= error%></div></td>
</tr>
<% } %>
</table>
</td>
</tr>
<% } %>
<tr>
<td><input size="15"maxlength="50"value="<%= (username != null ? username:"") %>"></td>
<td><input size="15"maxlength="50"></td>
<td align="center"><input value=" <fmt:message key="login.login"/> "></td>
</tr>
<tr valign="top">
<td><label for="u01"><fmt:message key="login.username"/></label></td>
<td><label for="p01"><fmt:message key="login.password"/></label></td>
<td> </td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="right">
<div align="right">
<%= AdminConsole.getAppName() %>,<fmt:message key="login.version"/>:<%= AdminConsole.getVersionString() %>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<!-- END login box -->
</div>
</form>
<script type="text/javascript">
<!--
if (document.loginForm.username.value == '') {
document.loginForm.username.focus();
} else {
document.loginForm.password.focus();
}
//-->
</script>
</body>
</html> |
DetailsAffected Software:Openfire by Ignite Realtime Fixed in Version:3.6.3 Issue Type:XSS Original Code: Found Here DescriptionXSS bug in Openfire by Ignite Realtime. Openfire is an Open Source,real time collaboration server. The bug is very straightforward and a simple string like the one presented below takes advantage of the vulnerability. http://www.example.com/group-summary.jsp?search=%22%3E%3C[xss]
This bug was actually part of a number of security bugs reported by Core Security Technologies. You can read their advisory here. The patch simply HTML encodes the tainted search parameter… Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| <% // Get parameters int start = ParamUtils.getIntParameter(request,"start",0); int range = ParamUtils.getIntParameter(request,"range",webManager.getRowsPerPage("group-summary", 15));
if (request.getParameter("range") != null) { webManager.setRowsPerPage("group-summary", range); }
int groupCount = webManager.getGroupManager().getGroupCount(); Collection<Group> groups = webManager.getGroupManager().getGroups(start, range);
String search = null; if (webManager.getGroupManager().isSearchSupported() && request.getParameter("search") != null && !request.getParameter("search").trim().equals("")) { search = request.getParameter("search"); + // Santize variables to prevent vulnerabilities + search = StringUtils.escapeHTMLTags(search);
// Use the search terms to get the list of groups and group count. groups = webManager.getGroupManager().search(search, start, range); // Get the count as a search for *all* groups. That will let us do pagination even // though it's a bummer to execute the search twice. groupCount = webManager.getGroupManager().search(search).size(); }
// paginator vars int numPages = (int)Math.ceil((double)groupCount/(double)range); int curPage = (start/range) + 1; %>
<% if (request.getParameter("deletesuccess") != null) { %>
<div class="jive-success"> <table cellpadding="0" cellspacing="0" border="0"> <tbody> <tr><td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16" border="0" alt=""></td> <td class="jive-icon-label"> <fmt:message key="group.summary.delete_group" /> </td></tr> </tbody> </table> </div><br>
<% } %>
<% if (webManager.getGroupManager().isSearchSupported()) { %>
<form action="group-summary.jsp" method="get" name="searchForm"> <table border="0" width="100%" cellpadding="0" cellspacing="0"> <tr> <td valign="bottom"> <fmt:message key="group.summary.total_group" /> <b><%= groupCount %></b> <% if (numPages > 1) { %>
, <fmt:message key="global.showing" /> <%= LocaleUtils.getLocalizedNumber(start+1) %>-<%= LocaleUtils.getLocalizedNumberstartrange > groupCount ? groupCount:start+range) %>
<% } %> </td> <td align="right" valign="bottom"> <fmt:message key="group.summary.search" />: <input type="text" size="30" maxlength="150" name="search" value="<%= ((search!=null) ? search : "") %>"> </td> </tr> </table> </form>
<script language="JavaScript" type="text/javascript"> document.searchForm.search.focus(); </script>
<% } // Otherwise, searching is not supported. else { %> <p> <fmt:message key="group.summary.total_group" /> <b><%= groupCount %></b> <% if (numPages > 1) { %>
, <fmt:message key="global.showing" /> <%= (start+1) %>-<%= (start+range) %>
<% } %> </p> <% } %>
<% if (numPages > 1) { %>
<p> <fmt:message key="global.pages" /> [ <% for (int i=0; i<numPages; i++) { String sep = ((i+1)<numPages) ? " " : ""; boolean isCurrent = (i+1) == curPage; %> <a href="group-summary.jsp?start=<%= (i*range) %><%= search!=null? "&search=" + URLEncoder.encode(search, "UTF-8") : ""%>" class="<%= ((isCurrent) ? "jive-current" : "") %>" ><%= (i+1) %></a><%= sep %>
<% } %> ] </p> |
DetailsAffected Software: qTranslate plugin Fixed in Version:2.0.2 Issue Type:XSS Original Code: Found Here DescriptionWhew! This is a lot of code for a simple change! This bug affected the qTranslate plugin for WordPress. The bug used the $_SERVER['REQUEST_URI'] variable without realizing it could contain arbitrary values supplied by an attacker. The $_SERVER['REQUEST_URI'] variable is used directly in an HREF in the HTML markup,resulting in a classic XSS vulnerability. The PHP documentation states that [‘REQUEST_URI’] represents: The URI which was given in order to access this page;for instance,‘/index.html’.
No escaping is done before returning the predefined value. Also,[‘REQUEST_URI’] actually returns the file requested as well as any query string parameters in the URI. For example,a request for http://server/index.php?blah=foo will return ‘index.php?blah=foo’. With this in mind,the attacker is free to set up arbitrary query string parameters which contain the XSS payload http://server/qtranslate_widget.php?xss=”><script>alert(document.domain)</script>
Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
| <?php function qtrans_convertURL($url='',$lang='',$forceadmin = false) { global $q_config; // invalid language if($url=='') $url = clean_url($q_config['url_info']['url']); if($lang=='') $lang = $q_config['language']; if(defined('WP_ADMIN')&&!$forceadmin) return $url; if(!qtrans_isEnabled($lang)) return ""; // &workaround $url = str_replace('&','&',$url); $url = str_replace('&','&',$url); // check if it's an external link $urlinfo = qtrans_parseURL($url); $home = rtrim(get_option('home'),"/"); if($urlinfo['host']!='') { // check for already existing pre-domain language information if($q_config['url_mode'] == QT_URL_DOMAIN &&preg_match("#^([a-z]{2}).#i",$urlinfo['host'],$match)) { if(qtrans_isEnabled($match[1])) { // found language information,remove it $url = preg_replace("/".$match[1]."\./i","",$url,1); // reparse url $urlinfo = qtrans_parseURL($url); } } if(substr($url,0,strlen($home))!=$home) { return $url; } // strip home path $url = substr($url,strlen($home)); } else { // relative url,strip home path $homeinfo = qtrans_parseURL($home); if($homeinfo['path']==substr($url,0,strlen($homeinfo['path']))) { $url = substr($url,strlen($homeinfo['path'])); } } // check for query language information and remove if found if(preg_match("#(&|\?)lang=([^&\#]+)#i",$url,$match) &&qtrans_isEnabled($match[2])) { $url = preg_replace("#(&|\?)lang=".$match[2]."&?#i","$1",$url); } // remove any slashes out front $url = ltrim($url,"/"); // remove any useless trailing characters $url = rtrim($url,"?&"); // reparse url without home path $urlinfo = qtrans_parseURL($url); // check if its a link to an ignored file type $ignore_file_types = preg_split('/\s*,\s*/',strtolower($q_config['ignore_file_types'])); $pathinfo = pathinfo($urlinfo['path']); if(isset($pathinfo['extension']) &&in_array(strtolower($pathinfo['extension']),$ignore_file_types)) { return $home."/".$url; } switch($q_config['url_mode']) { case QT_URL_PATH: // pre url // might already have language information if(preg_match("#^([a-z]{2})/#i",$url,$match)) { if(qtrans_isEnabled ($match[1])) { // found language information,remove it $url = substr($url,3); } } if($lang!=$q_config['default_language']) $url = $lang."/".$url; break; case QT_URL_DOMAIN: // pre domain if($lang!=$q_config['default_language']) $home = preg_replace("#//#","//".$lang.".",$home,1); break; default:// query if($lang!=$q_config['default_language']){ if(strpos($url,'?')===false) { $url .= '?'; } else { $url .= '&'; } $url .= "lang=".$lang; } } // see if cookies are activated if(!$q_config['cookie_enabled'] &&!$q_config['url_info']['internal_referer'] &&$urlinfo['path'] == '' &&$lang == $q_config['default_language'] &&$q_config['language'] != $q_config['default_language']) { //:( now we have to make unpretty URLs $url = preg_replace("#(&|\?)lang=".$match[2]."&?#i","$1",$url); if(strpos($url,'?')===false) { $url .= '?'; } else { $url .= '&'; } $url .= "lang=".$lang; } // &workaround $complete = str_replace('&','&',$home."/".$url); return $complete; }
...<SNIP...
function qtrans_use($lang,$text,$show_available=false) { global $q_config; // return full string if language is not enabled if(!qtrans_isEnabled($lang)) return $text; if(is_array($text)) { // handle arrays recursively foreach($text as $key =>$t) { $text[$key] = qtrans_use($lang,$text[$key],$show_available); } return $text; } if(is_object($text)) { foreach(get_object_vars($text) as $key =>$t) { $text->$key = qtrans_use($lang,$text->$key,$show_available); } return $text; } // get content $content = qtrans_split($text); // find available languages $available_languages = array(); foreach($content as $language =>$lang_text) { $lang_text = trim($lang_text); if(!empty($lang_text)) $available_languages[] = $language; } // if no languages available show full text if(sizeof($available_languages)==0) return $text; // if content is available show the content in the requested language $content[$lang] = trim($content[$lang]); if(!empty($content[$lang])) { return $content[$lang]; } // content not available in requested language (bad!!) what now? if(!$show_available){ // check if content is available in default language,if not return first language found. (prevent empty result) if($lang!=$q_config['default_language']) return "(".$q_config['language_name'][$q_config['default_language']].") ".qtrans_use($q_config['default_language'],$text,$show_available); foreach($content as $language =>$lang_text) { $lang_text = trim($lang_text); if(!empty($lang_text)) { return $lang_text; } } } // display selection for available languages $available_languages = array_unique($available_languages); $language_list = ""; if(preg_match('/%LANG:([^:]*):([^%]*)%/',$q_config['not_available'][$lang],$match)) { $normal_seperator = $match[1]; $end_seperator = $match[2]; // build available languages string backward $i = 0; foreach($available_languages as $language) { if($i==1) $language_list = $end_seperator.$language_list; if($i>1) $language_list = $normal_seperator.$language_list; - $language_list = "<a href=\"".qtrans_convertURL($_SERVER['REQUEST_URI'],$language)."\">".$q_config['language_name'][$language]."</a>".$language_list; + $language_list = "<a href=\"".qtrans_convertURL('',$language)."\">".$q_config['language_name'][$language]."</a>".$language_list; $i++; } } return "<p>".preg_replace('/%LANG:([^:]*):([^%]*)%/',$language_list,$q_config['not_available'][$lang])."</p>"; } ?> |
DetailsAffected Software:WP Category Manager plugin Fixed in Version:1.3.1.0 Issue Type:Sql Injection Original Code: Found Here DescriptionThis week’s vulnerability affected the WP Category Manager plugin. There were two interesting characteristics I noticed with this code fix. First,while there were a number of SQL injection vulnerabilities fixed with this change list,there were also a large number of non security fixes included in this change list as well. It’s generally a good idea to keep security change lists separate from other change lists. The number of non security fixes included in this particular list was so distracting,I removed them from the post. The SQL injection fixes are pretty straight forward,changing dynamically built SQL statements into WordPress’ built-in $wpdb->prepare() function. The second characteristic that caught my attention was usage of numeric IDs at the end of SQL statements. For example: where object_id = $postId and term_taxonomy_id= $categoryId”;
This syntax creates a condition in which the typical addslashes() used to protect against SQL injection can be bypassed. For example,an attacker could craft a SQL injection string like: Sqli.php?categoryId=-1 union select 1,2,3,4,5–
As you can see,the injection string above contains no special characters that would be escaped by addslashes(). Fortunately,the Category Manager plugin developers chose to utilze $wpdb->prepare() instead of addslashes(). Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
| <?php
include_once('wpcm-options.php');
if( ! class_exists('wpcm_functions')):
class wpcm_functions { public static function remove_category($postId,$categoryId) { - global $wpdb; - $wpdb->show_errors(); - $queryStr = "DELETE FROM $wpdb->term_relationships - where object_id = $postId and term_taxonomy_id= $categoryId"; + echo $postId;
- $wpdb->query($queryStr); + if(is_int(intval($postId))) + { + global $wpdb; + + $wpdb->show_errors(); + + $queryStr = $wpdb->prepare("DELETE FROM $wpdb->term_relationships where object_id = %d and term_taxonomy_id= %s",$postId,$categoryId); + $wpdb->query($queryStr); + } }
public static function get_posts($category,$page) { global $wpdb; $wpdb->show_errors();
// First figure out how many posts to show per page. This will be your limit $pageSize = wpcm_options::get_option('postsperpage');
$finalQueryLine = '';
if($pageSize != -1) { // Next figure out how many posts to skip. This will be your offset $offset = $pageSize * $page;
$finalQueryLine = "limit ". $pageSize . "offset ". $offset;
+ } + + $querystr = $wpdb->prepare("select wposts.*,wp_term_taxonomy.term_taxonomy_id + from $wpdb->posts wposts + LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id + LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id + LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id + WHERE wp_term_taxonomy.taxonomy = 'category' + and wp_terms.name = '%s' + and wposts.post_status='publish' + ORDER BY wposts.ID ". $finalQueryLine ,$category); + + $postlist = $wpdb->get_results($querystr); + return $postlist; } - $querystr = "select wposts.*,wp_term_taxonomy.term_taxonomy_id - from $wpdb->posts wposts - LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id - LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id - LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id - WHERE wp_term_taxonomy.taxonomy = 'category' - and wp_terms.name = '". $category . "' - and wposts.post_status='publish' - ORDER BY wposts.ID ". $finalQueryLine; - - $postlist = $wpdb->get_results($querystr); - return $postlist; }
public static function get_postCount($category) { global $wpdb; $wpdb->show_errors();
- $querystr = "select count(*) - from $wpdb->posts wposts - LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id - LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id - LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id - WHERE wp_term_taxonomy.taxonomy = 'category' - and wp_terms.name = '". $category . "' - and wposts.post_status='publish'"; + $querystr = $wpdb->prepare("select count(*) + from $wpdb->posts wposts + LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id + LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id + LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id + WHERE wp_term_taxonomy.taxonomy = 'category' + and wp_terms.name = '%s' + and wposts.post_status='publish'",$category);
$result = $wpdb->get_var($querystr,0,0); return $result;
}
public static function render_posts($postlist) { if($postlist) { foreach($postlist as $post) { echo '<div id="catmanagerpost'. $post->ID .'">'; echo '<span ><a href="'. get_permalink($post->ID) .'"title="'.$post->post_title . '">' . $post->post_title . '</a></span><span >' . date_format(date_create($post->post_date),"F j,Y") . '</span>'; echo '<p ><a href="javascript:void(0);"postID="'.$post->ID.'"catID="'. $post->term_taxonomy_id .'"id="catmanremovepost'. $post->ID .'"title="Remove post from this category">Remove</a>| '; echo edit_post_link('Edit Post','','',$post->ID); echo '</p></div>'; } } else { echo '<strong>No posts found</strong>'; } }
public static function render_postcount($category) { $count = wpcm_functions::get_postCount($category);
echo '<span id="wpcmpostcount">'.$count.'</span>'; }
public static function get_categories() { global $wpdb;
$wpdb->show_errors();
$querystr = "select wt.name,wt.term_id from $wpdb->terms wt join $wpdb->term_taxonomy wtt on wtt.term_id = wt.term_id where wtt.taxonomy = 'category' order by wt.name";
$catlist = $wpdb->get_results($querystr); return $catlist; } } endif;
?> |
DetailsAffected Software:Dojo Toolkit SDK Fixed in Version: 1.4.2 Issue Type:Cross Site Scripting (XSS) Original Code: Found Here DescriptionThis was a bug reported by the Gotham Digital Science against the Dojo toolkit SDK. The Dojo toolkit is a popular toolkit used by numerous websites… so in essence this bug provided attackers an opportunity to XSS a large number of websites across the Internet. The bug begins by the capturing of untrusted parameter values from the querystring. This is done by the following JavaScript: 1
| var qstr = window.location.search.substr(1); |
qstr is then split based on the “&” character,proving values for various JavaScript variables including DoJoURL and TestURL. The attacker is free to provide arbitrary values for DoJoURL and TestURL by simply providing the proper querystring values. For example: runner.html?dojoUrl=attacker-controlled&testUrl=attackercontrolled
the attacker supplied values are then used in a document.write() statement,giving the attacker the opprotuntiy to inject arbitrary client side script into any website that happens to include the Dojo library. The vulnerable document.write() statements are provided below: document.write(“<scr”+”ipt type=’text/javascript’djConfig=’isDebug:true’src=’”+dojoUrl+”‘></scr”+”ipt>”); document.write(“<scr”+”ipt type=’text/javascript’src=’”+testUrl+”.js’></scr”+”ipt>”);
The Dojo developers addressed this vulnerability by replacing characters from the attacker controlled input. The specific regular expression used is provided below: value=tp[1].replace(/[<>"']/g,“”);
I see a major issue with this code fix… can you spot it as well? Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| <script type="text/javascript"> // workaround for bug in Safari 3. See #7189 if (/3[\.0-9]+ Safari/.test(navigator.appVersion)) { window.console = { origConsole:window.console, log:function(s){ this.origConsole.log(s); }, info:function(s){ this.origConsole.info(s); }, error:function(s){ this.origConsole.error(s); }, warn:function(s){ this.origConsole.warn(s); } }; } </script> <script type="text/javascript"> window.dojoUrl = "../../dojo/dojo.js"; window.testUrl = ""; window.testModule = ""; // parse out our test URL and our Dojo URL from the query string var qstr = window.location.search.substr(1); if(qstr.length){ var qparts = qstr.split("&"); for(var x=0;x<qparts.length;x++){ - var tp = qparts[x].split("="); - if(tp[0] == "dojoUrl"){ - window.dojoUrl = tp[1]; - } - if(tp[0] == "testUrl"){ - window.testUrl = tp[1]; - } - if(tp[0] == "testModule"){ - window.testModule = tp[1]; - } - if(tp[0] == "registerModulePath"){ - var modules = tp[1].split(";"); - window.registerModulePath=[]; - for (var i=0;i<modules.length;i++){ - window.registerModulePath.push(modules[i].split(",")); - } - } + var tp = qparts[x].split("="),name=tp[0],value=tp[1].replace(/[<>"']/g,""); // replace() to avoid XSS attack + switch(name){ + case "dojoUrl": + case "testUrl": + case "testModule": + window[name] = value; + break; + case "registerModulePath": + var modules = value.split(";"); + window.registerModulePath=[]; + for (var i=0;i<modules.length;i++){ + window.registerModulePath.push(modules[i].split(",")); + } + break; } } document.write("<scr"+"ipt type='text/javascript' djConfig='isDebug:true' src='"+dojoUrl+"'></scr"+"ipt>"); </script> <script type="text/javascript"> try{ dojo.require("doh.runner"); }catch(e){ document.write("<scr"+"ipt type='text/javascript' src='runner.js'></scr"+"ipt>"); } if(testUrl.length){ document.write("<scr"+"ipt type='text/javascript' src='"+testUrl+".js'></scr"+"ipt>"); } </script> <style type="text/css"> @import "../../dojo/resources/dojo.css"; var SHRSB_Globals ={"src":"http:\/\/spotthevuln.com\/wordpress\/wp-content\/plugins\/sexybookmarks\/spritegen_default","perfoption":null}; |
|