Covet –Cross Site Scripting

Details

Affected Software:Subscribe to Comments Plugin

Fixed in Version:2.1

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

A familiar symptom here with the same ole result. In the vulnerable code we see a call to $_SERVER['REQUEST_URI']. For some reason,many developers assume REQUEST_URI cannot be tainted and used for XSS. REQUEST_URI will not only include the path to current php file,it will also include any querystring parameters in the URI as well. Here’s a few examples of what REQUEST_URI will return:

http://spotthevuln.com/blah/
results in –>/

http://spotthevuln.com/blah//index.php

results in –>/blah/index.php

http://spotthevuln.com/blah/index.php?qs=value

results in –>/blah/index.php?qs=value

http://spotthevuln.com/blah/index.php/qs1/qs2

results in –>/blah/index.php/qs1/qs2

As you can see an attacker can easily taint the REQUEST_URI value,using it in XSS attacks. The developers addressed this vulnerability by encoding calls to REQUEST_URI.

Developers Solution

<?phpfunction show_subscription_checkbox ($id='0'){global $sg_subscribe;sg_subscribe_start();if ( $sg_subscribe->checkbox_shown ) return $id;if ( !$email = $sg_subscribe->current_viewer_subscription_status() ):?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"> <input type="checkbox"name="subscribe"id="subscribe"value="subscribe"style="width:auto;"<?php if ($sg_subscribe->default_subscribed) echo 'checked="checked"';?>/> <label for="subscribe"><?php echo $sg_subscribe->not_subscribed_text;?></label></p><?php ?><?php elseif ( $email == 'admin' &&current_user_can('manage_options') ):?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"><?php echo str_replace('[manager_link]',$sg_subscribe->manage_link($email,true,false),$sg_subscribe->author_text);?></p><?php else:?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"><?php echo str_replace('[manager_link]',$sg_subscribe->manage_link($email,true,false),$sg_subscribe->subscribed_text);?></p><?php ?><?php endif;$sg_subscribe->checkbox_shown = true;return $id}function show_manual_subscription_form (){global $id,$sg_subscribe,$user_email;sg_subscribe_start();$sg_subscribe->show_errors('solo_subscribe','<div class="solo-subscribe-errors">','</div>',__('<strong>Error:</strong>','subscribe-to-comments'),'<br />');if ( !$sg_subscribe->current_viewer_subscription_status() ):get_currentuserinfo();?><?php ?><?php ?><?php ?>-<form action="http://<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ?>"method="post">+<form action="http://<?php echo $_SERVER['HTTP_HOST'] . wp_specialchars($_SERVER['REQUEST_URI']);?>"method="post"><input type="hidden"name="solo-comment-subscribe"value="solo-comment-subscribe"/><input type="hidden"name="postid"value="<?php echo $id;?>"/>-<input type="hidden"name="ref"value="<?php echo 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];?>"/>+<input type="hidden"name="ref"value="<?php echo urlencode('http://' . $_SERVER['HTTP_HOST'] . wp_specialchars($_SERVER['REQUEST_URI']));?>"/><p class="solo-subscribe-to-comments"><?php _e('Subscribe without commenting','subscribe-to-comments');?><br /><label for="solo-subscribe-email"><?php _e('E-Mail:','subscribe-to-comments');?><input type="text"name="email"id="solo-subscribe-email"size="22"value="<?php echo $user_email;?>"/></label><input type="submit"name="submit"value="<?php _e('Subscribe','subscribe-to-comments');?>"/></p></form><?php ?>

CaddyShack – Cross Site Scripting

Details

Affected Software:WebChat Module for Jive

Fixed in Version:August of 2008

Issue Type:Cross Site Scripting

Original Code: Found Here

Description

This week’s vulnerability affected a webchat module created by Jive Software.  The bug is straightforward,  the JSP code takes an attacker controlled value and uses it to build dynamic HTML.  Although the bug is straightforward,this week’s example was a great/simple exercise in identifying a vulnerable pattern and tracing to find other vulnerable patterns in the code.  This week’s sample has three separate vulnerabilities that were all addressed via single patch.  All these have similar symptoms/patterns (although the specifics are a bit different).  Identifying vulnerable patterns and searching for these patterns in other places in code is an essential skill for security code auditors.  Did you find all three bugs that were patched?

Developers Solution

1
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
 public class FormUtils {

private FormUtils() {
}

public static String createAnswers(FormField formField,HttpServletRequest request) {
final StringBuffer builder = new StringBuffer();
if (formField.getType().equals(FormField.TYPE_TEXT_SINGLE)) {
String cookieValue = getCookieValueForField(formField.getVariable(),request);
String insertValue = "";
if(ModelUtil.hasLength(cookieValue)){
insertValue = "value=\""+cookieValue+"\"";
}
- builder.append("<input type=\"text\"name=\""+ formField.getVariable() + "\""+insertValue+"style=\"width:75%\">");
+builder.append("<input type=\"text\"name=\""+ formField.getVariable() + "\""+StringUtils.escapeHTMLTags(insertValue)+"style=\"width:75%\">");
}
else if (formField.getType().equals(FormField.TYPE_TEXT_MULTI)) {
builder.append("<textarea name=\""+ formField.getVariable() + "\"cols=\"30\"rows=\"3\">");
builder.append("</textarea>");
}
else if (formField.getType().equals(FormField.TYPE_LIST_SINGLE)) {
builder.append("<select name=\""+ formField.getVariable() + "\">");
Iterator iter = formField.getOptions();
String cookieValue = ModelUtil.emptyStringIfNull(getCookieValueForField(formField.getVariable(),request));
while (iter.hasNext()) {
FormField.Option option = (FormField.Option)iter.next();
String selected = option.getValue().equals(cookieValue) ? "selected":"";
- builder.append("<option value=\""+ option.getValue() + "\""+selected+">"+ option.getLabel() + "</option>");
+builder.append("<option value=\""+ StringUtils.escapeHTMLTags(option.getValue()) + "\""+selected+">"+ option.getLabel() + "</option>");
}
builder.append("</select>");
}
else if (formField.getType().equals(FormField.TYPE_BOOLEAN)) {
Iterator iter = formField.getOptions();
int counter = 0;
while (iter.hasNext()) {
FormField.Option option = (FormField.Option)iter.next();
String value = option.getLabel();
builder.append("<input type=\"checkbox\"value=\""+ value + "\"name=\""+ formField.getVariable() + counter + "\">");
builder.append("&nbsp;");
-builder.append(value);
+builder.append(StringUtils.escapeHTMLTags(value));
builder.append("<br/>");
counter++;
}
}

Tree – Cross Site Scripting

Details

Affected Software:WordPress-to-lead for Salesforce CRM

Fixed in Version:1.0.5

Issue Type:Cross Site Scripting

Original Code: Found Here

Description

This week’s vulnerability is an XSS bug in a Salesforce plugin for WordPress.  This bug is a bit interesting as it seems the developer attempted to sanitize an attacker controlled variable,but used the incorrect API.  Looking at the vulnerable code,we see the following line:

return ‘<label for=”‘.$id.’”>’.$label.’:</label><br/><input size=”45″name=”‘.$id.’”value=”‘.stripslashes($options[$id]).’”/><br/><br/>’;

In the line above,we see that $options[$id] is placed into the rendered HTML.  $options[$id] appears to be attacker controlled and the developers used the stripslashes() API to sanitize $options[$id] before displaying the value in HTML.  Unfortunately,stripslashes() doesn’t help prevent XSS vulnerabilities and created an opportunity for XSS.  The developer fixed this vulnerability by using the correct API to sanitize against XSS vulnerability (htmlentities).

Developers Solution

1
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
<?php
if (!class_exists('OV_Plugin_Admin')) {
class OV_Plugin_Admin {

var $hook               = '';
var $filename   = '';
var $longname   = '';
var $shortname  = '';
var $ozhicon    = '';
var $optionname = '';
var $homepage   = '';
var $accesslvl  = 'manage_options';

function Yoast_Plugin_Admin() {
add_action( 'admin_menu',array(&$this,'register_settings_page') );
add_filter( 'plugin_action_links',array(&$this,'add_action_link'),10,2 );
add_filter( 'ozh_adminmenu_icon',array(&$this,'add_ozh_adminmenu_icon' ) );

add_action('admin_print_scripts',array(&$this,'config_page_scripts'));
add_action('admin_print_styles',array(&$this,'config_page_styles'));
}

function add_ozh_adminmenu_icon( $hook ) {
if ($hook == $this->hook)
return WP_CONTENT_URL . '/plugins/' . plugin_basename(dirname($filename)). '/'.$this->ozhicon;
return $hook;
}

function config_page_styles() {
if (isset($_GET['page']) &&$_GET['page'] == $this->hook) {
wp_enqueue_style('dashboard');
wp_enqueue_style('thickbox');
wp_enqueue_style('global');
wp_enqueue_style('wp-admin');
wp_enqueue_style('ov-admin-css',WP_CONTENT_URL . '/plugins/' . plugin_basename(dirname(__FILE__)). '/yst_plugin_tools.css');
}
}

function register_settings_page() {
add_options_page($this->longname,$this->shortname,$this->accesslvl,$this->hook,array(&$this,'config_page'));
}

function plugin_options_url() {
return admin_url( 'options-general.php?page='.$this->hook );
}


function add_action_link( $links,$file ) {
static $this_plugin;
if( empty($this_plugin) ) $this_plugin = $this->filename;
if ( $file == $this_plugin ) {
$settings_link = '<a href="' . $this->plugin_options_url() . '">' . __('Settings') . '</a>';
array_unshift( $links,$settings_link );
}
return $links;
}

function config_page() {

}

function config_page_scripts() {
if (isset($_GET['page']) &&$_GET['page'] == $this->hook) {
wp_enqueue_script('postbox');
wp_enqueue_script('dashboard');
wp_enqueue_script('thickbox');
wp_enqueue_script('media-upload');
}
}


function checkbox($id,$label) {
$options = get_option($this->optionname);
return '<input type="checkbox"id="'.$id.'"name="'.$id.'"'. checked($options[$id],true,false).'/><label for="'.$id.'">'.$label.'</label><br/>';
}


function textinput($id,$label) {
$options = get_option($this->optionname);
-                       return '<label for="'.$id.'">'.$label.':</label><br/><input size="45"type="text"id="'.$id.'"name="'.$id.'"value="'.stripslashes($options[$id]).'"/><br/><br/>';
+                       return '<label for="'.$id.'">'.$label.':</label><br/><input size="45"type="text"id="'.$id.'"name="'.$id.'"value="'.htmlentities(stripslashes($options[$id])).'"/><br/><br/>';
}

Nails –Cross Site Scripting

Details

Affected Software:DojoToolkit

Fixed in Version:1.4.2

Issue Type:XSS

Original Code: Found Here

Description

This 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 Solution

1
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);
}

});
}

})();

Drop Top - Cross Site ScriptingDrop Top –Cross Site Scripting

Details

Affected Software:Openfire by Ignite Realtime

Fixed in Version:3.6.1

Issue Type:XSS

Original Code: Found Here

Description

This 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 Solution

1
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="&nbsp;<fmt:message key="login.login"/>&nbsp;"></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>&nbsp;</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>

Will - Sql InjectionWill – Sql Injection

Details

Affected Software:WP Category Manager plugin

Fixed in Version:1.3.1.0

Issue Type:Sql Injection

Original Code: Found Here

Description

This 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 Solution

1
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;

?>

Everything – Cross Site Scripting

Details

Affected Software:Dojo Toolkit SDK

Fixed in Version: 1.4.2

Issue Type:Cross Site Scripting (XSS)

Original Code: Found Here

Description

This 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 Solution

1
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:""};