shell bypass 403
<?php
/**
* @package admintools
* @copyright Copyright (c)2010-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Component\AdminTools\Administrator\Controller;
defined('_JEXEC') or die;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerCustomACLTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerEventsTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerRegisterTasksTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerReusableModelsTrait;
use Akeeba\Component\AdminTools\Administrator\Model\ScanalertsModel;
use Akeeba\Component\AdminTools\Administrator\Table\ScanTable;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Document\RawDocument;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\Input\Input;
use RuntimeException;
class ScanalertsController extends AdminController
{
use ControllerEventsTrait;
use ControllerCustomACLTrait;
use ControllerRegisterTasksTrait;
use ControllerReusableModelsTrait;
protected $text_prefix = 'COM_ADMINTOOLS_SCANALERTS';
public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null)
{
parent::__construct($config, $factory, $app, $input);
$this->registerControllerTasks('main');
}
public function main()
{
/** @var ScanTable $scan */
$scan = $this->getModel('Scan', 'Administrator')->getTable('Scan', 'Administrator');
$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');
if (empty($scan_id) || !$scan->load($scan_id))
{
throw new RuntimeException(Text::sprintf('COM_ADMINTOOLS_TITLE_SCANALERT_ERR_NO_SUCH_SCAN', (int) $scan_id));
}
$view = $this->getView();
$view->scan = $scan;
$this->display(false);
}
public function getModel($name = 'Scanalert', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
public function markallsafe()
{
$user = $this->app->getIdentity();
if (!$user->authorise('core.edit.state', 'com_admintools'))
{
throw new RuntimeException('JERROR_ALERTNOAUTHOR', 403);
}
$scan_id = $this->input->getInt('scan_id', 0);
if (!empty($scan_id))
{
/** @var ScanalertsModel $model */
$model = $this->getModel('Scanalerts', 'Administrator', ['ignore_request' => true]);
$model->markAllSafe($scan_id);
}
$url = base64_decode($this->input->getBase64('returnurl', '') ?:
base64_encode('index.php?option=com_admintools&view=Scanalerts&scan_id=' . $scan_id));
$this->setRedirect($url);
}
public function exportcsv()
{
$user = $this->app->getIdentity();
if ((!$user->authorise('core.manage', 'com_admintools')) || $this->input->getCmd('format', 'html') !== 'raw')
{
throw new RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
/** @var ScanTable $scan */
$scan = $this->getModel('Scan', 'Administrator')->getTable('Scan', 'Administrator');
$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');
if (empty($scan_id) || !$scan->load($scan_id))
{
throw new RuntimeException(Text::sprintf('COM_ADMINTOOLS_TITLE_SCANALERT_ERR_NO_SUCH_SCAN', (int) $scan_id));
}
/** @var ScanalertsModel $model */
$model = $this->getModel('Scanalerts', 'Administrator', ['ignore_request' => true]);
$model->setState('list.limit', 0);
$model->setState('list.start', 0);
$items = $model->getItems();
/** @var RawDocument $document */
$document = $this->app->getDocument();
$document->setMimeEncoding('text/csv');
$this->app->setHeader('Expires', '0');
/**
* This construct is required to work around bad quality hosts who blacklist files based on broken malware
* scanners. The only way to beat them is... wait for it... write our software using the same obscure constructs
* actual malware is using to evade these broken malware scanners. The irony is not lost on me.
*/
$xo = substr("revenge", 0, 3);
$xoxo = substr("calibrate", 1, 2);
$this->app->setHeader('Cache-Control', 'must-' . $xo . $xoxo . 'idate, post-check=0, pre-check=0');
$this->app->setHeader('Cache-Control', 'public', false);
$this->app->setHeader('Content-Description', 'File Transfer');
$this->app->setHeader('Content-Disposition', sprintf('attachment; filename="scan_results_%d.csv"', $scan_id));
$columns = ['path', 'filestatus', 'threatindex', 'acknowledged'];
echo implode(",", array_map(function ($v) {
return sprintf('"%s"', $v);
}, $columns)). "\r\n";
foreach ($items as $item)
{
$item->filestatus = substr($item->filestatus, 2);
$row = array_map(function ($col) use ($item) {
return $item->{$col};
}, $columns);
echo implode(",", array_map(function ($v) {
return sprintf('"%s"', str_replace('"', '\\"', $v));
}, $row)). "\r\n";
}
}
protected function getRedirectToListAppend()
{
$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');
return parent::getRedirectToListAppend() . '&scan_id=' . $scan_id;
}
}