CVE-2016-9681: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Multiple cross-site scripting (XSS) vulnerabilities in Serendipity before 2.0.5 allow remote authenticated users to inject arbitrary web script or HTML via a category or directory name.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2016-12-25T17:59Z
Repository: https://github.com/s9y/Serendipity
Language: PHP
Files changed: docs/NEWS, include/admin/images.inc.php, serendipity_config.inc.php, templates/2k11/admin/category.inc.tpl
Commit date: 2016-11-28 15:34:10 +0100
Commit message: Sync changes

Diff: diff --git a/docs/NEWS b/docs/NEWS
index c42e415b4..0c4a813dd 100644
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -1,11 +1,14 @@
-Version 2.1 ()
+Version 2.1 (November 28th, 2016)
 ------------------------------------------------------------------------
 
     * [Security] Enhanced media upload check to also check redirects
       for local files, thanks to Xu Yue (again!)
 
+    * [Security] Prevent XSS in adding category and directory names, 
+      thanks to Edric Teo @smarterbitbybit.
+
 Version 2.1-beta2 (September 26th, 2016)
 ------------------------------------------------------------------------
     * Improved backend accessibility by hiding iconfont icons for
       screenreaders (using aria-hidden).
 
diff --git a/include/admin/images.inc.php b/include/admin/images.inc.php
index 085455e40..d4802e09a 100644
--- a/include/admin/images.inc.php
+++ b/include/admin/images.inc.php
@@ -548,11 +548,11 @@ switch ($serendipity['GET']['adminAction']) {
         $nd      = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $new_dir;
         serendipity_plugin_api::hook_event('backend_directory_create', $nd);
 
         /* TODO: check if directory already exist */
         if (is_dir($nd) || @mkdir($nd)) {
-            $data['print_DIRECTORY_CREATED'] = sprintf(DIRECTORY_CREATED, $serendipity['POST']['name']);
+            $data['print_DIRECTORY_CREATED'] = sprintf(DIRECTORY_CREATED, $new_dir);
             @umask(0000);
             @chmod($serendipity['serendipityPath'] . $serendipity['uploadPath'] . $new_dir, 0777);
 
             // Apply parent ACL to new child.
             $array_parent_read  = serendipity_ACLGet(0, 'directory', 'read',  $serendipity['POST']['parent']);
diff --git a/serendipity_config.inc.php b/serendipity_config.inc.php
index 4e4d79506..978c31f86 100644
--- a/serendipity_config.inc.php
+++ b/serendipity_config.inc.php
@@ -45,11 +45,11 @@ include(S9Y_INCLUDE_PATH . 'include/compat.inc.php');
 if (defined('USE_MEMSNAP')) {
     echo memSnap('Framework init');
 }
 
 // The version string
-$serendipity['version'] = '2.1-beta2';
+$serendipity['version'] = '2.1-beta3';
 
 
 // Setting this to 'false' will enable debugging output. All alpha/beta/cvs snapshot versions will emit debug information by default. To increase the debug level (to enable Smarty debugging), set this flag to 'debug'.
 if (!isset($serendipity['production'])) {
     $serendipity['production'] = ! preg_match('@\-(alpha|beta|cvs|rc).*@', $serendipity['version']);
diff --git a/templates/2k11/admin/category.inc.tpl b/templates/2k11/admin/category.inc.tpl
index 4948adaca..2f5ab23b7 100644
--- a/templates/2k11/admin/category.inc.tpl
+++ b/templates/2k11/admin/category.inc.tpl
@@ -52,11 +52,11 @@
             {$formToken}
 
             <div id="category_basics" class="clearfix">
                 <div class="form_field">
                     <label for="category_name">{$CONST.NAME}</label>
-                    <input id="category_name" pattern="{if $new}^(?!({foreach $categories as $cat}{$cat.category_name}|{/foreach})$).*{else}^(?!({foreach $categories as $cat}{if $this_cat.category_name != $cat.category_name}{$cat.category_name}{/if}|{/foreach})$).*{/if}" name="serendipity[cat][name]" type="text" value="{$this_cat.category_name|default:""|escape}" title="Categoryname">
+                    <input id="category_name" pattern="{if $new}^(?!({foreach $categories as $cat}{$cat.category_name|escape}|{/foreach})$).*{else}^(?!({foreach $categories as $cat}{if $this_cat.category_name != $cat.category_name}{$cat.category_name|escape}{/if}|{/foreach})$).*{/if}" name="serendipity[cat][name]" type="text" value="{$this_cat.category_name|default:""|escape}" title="{$CONST.CATEGORY}">
                 </div>
 
                 <div class="form_field">
                     <label for="category_description">{$CONST.DESCRIPTION}</label>
                     <input id="category_description" name="serendipity[cat][description]" type="text" value="{$this_cat.category_description|default:""|escape}">
@@ -72,11 +72,11 @@
                     <label for="parent_cat">{$CONST.PARENT_CATEGORY}</label>
                     <select id="parent_cat" name="serendipity[cat][parent_cat]">
                         <option value="0"{if $cid == 0} selected{/if}>{$CONST.NO_CATEGORY}</option>
                     {foreach $categories as $cat}
                         {if $cat.categoryid == $cid}{continue}{/if}
-                        <option value="{$cat.categoryid}"{if $this_cat.parentid == $cat.categoryid} selected{/if}>{for $i=1 to $cat.depth}&nbsp{/for} {$cat.category_name}</option>
+                        <option value="{$cat.categoryid}"{if $this_cat.parentid == $cat.categoryid} selected{/if}>{for $i=1 to $cat.depth}&nbsp{/for} {$cat.category_name|escape}</option>
                     {/foreach}
                     </select>
                 </div>
 
                 <fieldset class="clearfix inputs">

Vulnerable code: Version 2.1 ()
            $data['print_DIRECTORY_CREATED'] = sprintf(DIRECTORY_CREATED, $serendipity['POST']['name']);
$serendipity['version'] = '2.1-beta2';
                    <input id="category_name" pattern="{if $new}^(?!({foreach $categories as $cat}{$cat.category_name}|{/foreach})$).*{else}^(?!({foreach $categories as $cat}{if $this_cat.category_name != $cat.category_name}{$cat.category_name}{/if}|{/foreach})$).*{/if}" name="serendipity[cat][name]" type="text" value="{$this_cat.category_name|default:""|escape}" title="Categoryname">
                        <option value="{$cat.categoryid}"{if $this_cat.parentid == $cat.categoryid} selected{/if}>{for $i=1 to $cat.depth}&nbsp{/for} {$cat.category_name}</option>
Fixed code: Version 2.1 (November 28th, 2016)
    * [Security] Prevent XSS in adding category and directory names, 
      thanks to Edric Teo @smarterbitbybit.

            $data['print_DIRECTORY_CREATED'] = sprintf(DIRECTORY_CREATED, $new_dir);
$serendipity['version'] = '2.1-beta3';
                    <input id="category_name" pattern="{if $new}^(?!({foreach $categories as $cat}{$cat.category_name|escape}|{/foreach})$).*{else}^(?!({foreach $categories as $cat}{if $this_cat.category_name != $cat.category_name}{$cat.category_name|escape}{/if}|{/foreach})$).*{/if}" name="serendipity[cat][name]" type="text" value="{$this_cat.category_name|default:""|escape}" title="{$CONST.CATEGORY}">
                        <option value="{$cat.categoryid}"{if $this_cat.parentid == $cat.categoryid} selected{/if}>{for $i=1 to $cat.depth}&nbsp{/for} {$cat.category_name|escape}</option>