SPIP is a publishing system for the Internet that focuses on collective operation, multilingualism and ease of use. It is free software, distributed under the GNU/GPL license. It can thus be used for any Internet site, whether it is associative or institutional, personal or commercial as it is explained on their official website.

The audited version is the version 3.2.7 of the CMS which was released just under a year ago. This version can be downloaded via the following link:

How ?

The vulnerability is exploitable by an authenticated user within the application.

The exploitation of the vulnerability is done using a technique similar to the exploitation of an XSS. However, as we will see, the content of the HTML page is evaluated on the server side before being sent back to the user, which transforms this XSS into a PHP code evaluation and therefore into an RCE.

The vulnerable parameter is the cookie $_COOKIE['spip_ecran'] in which we will insert the following payload "><? system('id')%3b ?><body class=".

This gives us the following result:

alt text

And in a browser view:

alt text

Why ?

My lab is composed as follows on my host machine a PhpStorm, a docker LAMP environment (using docker-compose) configured with Xdebug to be able to define breakpoints within the PhpStorm application.

The file introducing the vulnerability is the following file:

File: <ROOT>/ecrire/inc/commencer_page.php


...

/**
 * Calcule les classes CSS à intégrer à la balise `<body>` de l'espace privé
 *
 * Les classes sont calculées en fonction des préférences de l'utilisateur,
 * par exemple s'il choisit d'avoir ou non les icônes.
 *
 * @return string Classes CSS (séparées par des espaces)
 */
function init_body_class() {
	$GLOBALS['spip_display'] = isset($GLOBALS['visiteur_session']['prefs']['display'])
		? $GLOBALS['visiteur_session']['prefs']['display']
		: 2;
	$spip_display_navigation = isset($GLOBALS['visiteur_session']['prefs']['display_navigation'])
		? $GLOBALS['visiteur_session']['prefs']['display_navigation']
		: 'navigation_avec_icones';
	$spip_display_outils = isset($GLOBALS['visiteur_session']['prefs']['display_outils'])
		? ($GLOBALS['visiteur_session']['prefs']['display_outils'] ? 'navigation_avec_outils' : 'navigation_sans_outils')
		: 'navigation_avec_outils';
	$GLOBALS['spip_ecran'] = isset($_COOKIE['spip_ecran']) ? $_COOKIE['spip_ecran'] : "etroit";

	$display_class = array(
		0 => 'icones_img_texte'
		/*init*/,
		1 => 'icones_texte',
		2 => 'icones_img_texte',
		3 => 'icones_img'
	);

	return $GLOBALS['spip_ecran'] . " $spip_display_navigation $spip_display_outils " . $display_class[$GLOBALS['spip_display']];
}

...

Which can be simplified to:

File: <ROOT>/ecrire/inc/commencer_page.php


...

function init_body_class() {

    ...

	$GLOBALS['spip_ecran'] = isset($_COOKIE['spip_ecran']) ? $_COOKIE['spip_ecran'] : "etroit";

    ...

	return $GLOBALS['spip_ecran'] . " $spip_display_navigation $spip_display_outils " . $display_class[$GLOBALS['spip_display']];
}

...

When a user makes a request the following process is performed:

alt text

Go further in the understanding of the framework

The previous injection point also allows the exploitation of other vulnerabilities. Now that we have identified the vulnerable functions, let’s focus on its calls.

Within the framework the only place where this function is statically called is within the init_body() function.

File: <ROOT>/ecrire/inc/commencer_page.php


...

/**
 * Fonction envoyant la double série d'icônes de rédac
 *
 * @uses init_body_class()
 * @uses inc_bandeau_dist()
 *
 * @pipeline_appel body_prive
 *
 * @global mixed $connect_id_auteur
 * @global mixed $auth_can_disconnect
 *
 * @param string $rubrique
 * @param string $sous_rubrique
 * @param integer $id_rubrique
 * @param bool $menu
 * @return string
 */
function init_body($rubrique = 'accueil', $sous_rubrique = 'accueil', $id_rubrique = '', $menu = true) {

	$res = pipeline('body_prive', "<body class='"
		. init_body_class() . " " . _request('exec') . "'"
		. ($GLOBALS['spip_lang_rtl'] ? " dir='rtl'" : "")
		. '>');

	if (!$menu) {
		return $res;
	}


	$bandeau = charger_fonction('bandeau', 'inc');

	return $res
	. $bandeau();
}

...

Which is only called by the function inc_commencer_page_dist():

File: <ROOT>/ecrire/inc/commencer_page.php


...

/**
 * Débute une page HTML pour l'espace privé
 *
 * Préferer l'usage des squelettes prive/squelettes/.
 *
 * @uses init_entete()
 * @uses init_body()
 * @example
 *     ```
 *     $commencer_page = charger_fonction('commencer_page','inc');
 *     echo $commencer_page($titre);
 *     ```
 *
 * @param string $titre Titre de la page
 * @param string $rubrique ?
 * @param string $sous_rubrique ?
 * @param string $id_rubrique ?
 * @param bool $menu ?
 * @param bool $minipres ?
 * @param bool $alertes ?
 * @return string Code HTML
 **/
function inc_commencer_page_dist(
	$titre = "",
	$rubrique = "accueil",
	$sous_rubrique = "accueil",
	$id_rubrique = "",
	$menu = true,
	$minipres = false,
	$alertes = true
) {

	include_spip('inc/headers');

	http_no_cache();

	return init_entete($titre, $id_rubrique, $minipres)
	. init_body($rubrique, $sous_rubrique, $id_rubrique, $menu)
	. "<div id='page'>"
	. auteurs_recemment_connectes($GLOBALS['connect_id_auteur'])
	. ($alertes ? alertes_auteur($GLOBALS['connect_id_auteur']) : '')
	. '<div class="largeur">';
}

...

Once you have been able to trace back to this function, you will not find a call to the inc_commencer_page_dist() function explicitly. You will have to search for occurrences of charger_fonction('commencer_page', 'inc').

alt text

As i said earlier, it is not these occurrences that are responsible for the execution of PHP code. They are responsible for another type of vulnerability, Cross Site scripting (XSS).

alt text

alt text

Or:

alt text

alt text