Vulnérabilités XSS et CSRF dans le plugin WordPress LiveOptim free

Le plugin LiveOptim contient des vulnérabilités dans ses versions 1.1.3-free et inférieures, 1.1.6-free, 1.1.7-free, 1.1.8-free. Les failles sont du type XSS et CSRF.

Inutile de nous attarder sur chaque version, faisons plutôt un rapide tour des failles découvertes suite à la veille effectuée sur le plugin durant le mois d'avril/mai 2014

XSS

Le plugin WordPress LiveOptim est basé sur un script PHP standalone, ce qui se ressent bien à la vue du code :

	public function export_sql($table_name){
	global $wpdb;
	$table_name2 = str_replace("{prefix}",$wpdb->prefix,$table_name);
	$connexion = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
	mysql_select_db(DB_NAME, $connexion);
	$query = "show create table ".$table_name2."";	
	$creations="";
	$insertions="";
	
	$listeCreationsTables = mysql_query($query, $connexion);
	while($creationTable = mysql_fetch_array($listeCreationsTables)) { 
		...

L'utilisation de mysql_connect, mysql_select_db, mysql_query, et mysql_fetch_array est signe de la provenance d'un script maison PHP, mal reconverti.

Avant que la 1.1.9-free ne corrige ces failles, on pouvait aussi y trouver des chaînes non désinfectées (sanitize) :

<td class="requete"><input name="requete" value="<?php echo $lMotCle['requete']; ?>" type="text" /></td>
<td class="destination"><input name="destination" value="<?php echo $lMotCle['destination']; ?>" type="text" /></td>
<td class="position"><input name="position" value="<?php echo $lMotCle['position']; ?>" type="text" /></td>

Il manque ici des esc_attr() après les echo. Ceci représente une faille XSS, le fait de pouvoir mettre dans le champs ">foobar et le voir s'écrire HORS du champ, indique alors qu'un code plus complexe en HTML et/ou JavaScript fonctionnerait aussi bien.

Le codex de WordPress contient pourtant bien une page complète sur le sujet : http://codex.wordpress.org/Data_Validation, encore faut-il savoir qu'il faut le faire.

CSRF

Nous pouvions aussi y trouver des formulaires sans aucun jeton de sécurité (nonce) :

public function execute() {
	$requete = $_POST['requete'];
	$destination = $_POST['destination'];
	$position = $_POST['position'];

	if ( !isset($position) || is_null($position) || strlen($position) <= 0 || !intval($position) ) $position = null;

	$id = ConteneurMotCle::getInstance()->add( $requete, $destination, $position );
	ConteneurConfig::getInstance()->suptouscache();
		
	$this->redirection('action=mot-cle-lister');
}

Ici aucun jeton de sécurité n'est envoyé ni vérifié. Un pirate peut alors forcer une personne ayant les droits - ce qui indique que vérifier les droits avec un current_user_can( 'manage_options' ) est inutile - de valider le formulaire. Le pirate peut alors créer ou modifier les mots-clé de la page de réglages du plugin dans mon exemple.

XSRF²

Même chose avec les liens de type actions, c'est à dire un lien qui va faire des actions en base de données comme un lien "Supprimer le mot-clé" :

public function execute() {
	$id = $_GET['id'];
		
	ConteneurMotCle::getInstance()->remove($id);
	ConteneurConfig::getInstance()->suptouscache();
		
	$this->redirection('action=mot-cle-lister');
}

Pourtant, là aussi le codex a une page complète sur le sujet : https://codex.wordpress.org/WordPress_Nonces particulièrement wp_nonce_url(), wp_nonce_field et check_admin_referer() pour ce citer qu'elles.

Ce combo avec la faille XSS précédente est dévastatrice, le pirate va alors pouvoir ajouter du contenu JavaScript distant afin de récupérer des informations sensibles sur votre site. Aïe.

Concernant les requêtes en base de données, une non compréhension de la façon de les préparer se fait sentir :

public function modifCapping($valcap) {
	global $wpdb;		
	$query = 'UPDATE '.$wpdb->prefix.'liveoptim_capping SET capping = '.$valcap.'';
	$sql = $wpdb->prepare($query, null);
	$rep = $wpdb->get_results($sql);
	return null;
}

Le second paramètre de $wpdb->prepare() est null. La façon correcte est de passer le paramètre $valcap et de remplacer le $valcap dans $query par %s.

Celà dit, le get_results() est là aussi inutile puisqu'aucun résultat n'est renvoyé pour un update, le return null; aussi ne sert pas.

La fonction $wpdb->update() est faite pour ça ! C'est la façon WordPress de coder. Le plugin est comme le second paramètre de son propre $wpdb->prepare().

Premium ?

LiveOptim existe aussi en version payante, si vous utilisez cette, le changelog de la 1.6.0 n'indique rien sur ces correctifs :

/*
Plugin Name: Liveoptim
Plugin URI: http://www.liveoptim.com/
Description: <strong>Liveoptim</strong> est une extension qui vous simplifiera la vie.
Author: Erwan Milbeo - Copyright 2012
Version: 1.6.0
Author URI: http://www.liveoptim.com/
*/
...
== Changelog ==
= Version 1.2 (25/03/2013) =
- Seconde version 

Est-ce corrigé ? Est-ce encore vulnérable ? Posez leur la question vous qui l'avez achetée.

Et le code étant obfusqué, il n'est pas possible de facilement le lire - il est possible de faire du reversing, mais c'est théoriquement illégal - :

lo-obf

Ceci n'est pas en concordance avec la licence GPL pour information :

Sell the derivative work and publish the source code in obfuscated form

Un code de plugin obfusqué cherche habituellement à être masqué dans le but premier de ne pas être facilement modifiable et donc plagiable. Mais l'effet pervers est qu'il permet aussi cacher des choses qu'il ne souhaite pas que son utilisateur sache.

Récupérer des informations, envoyer des requêtes, inclure des redirections ou des publicités, la liste est aussi longue que votre imagination.

Je ne dis pas ce que LiveOptim le fait, je dis qu'un code obfusqué peut le faire et que LiveOptim Premium est obfusqué.

Prenez soin de vous et si vous utilisez encore ce plugin, tenez vous à jour plus que jamais !

 

Share!

Commentaires

  1. Hello hello !

    Tout d’abord je tiens à lancer ici un énorme MERCI à Mister Julio pour sa contribution à l’amélioration de la sécurisation de mon plugin LiveOptim pour WordPress. Ca ne s’invente pas ! Julio, bien qu’il exerce principalement à Miami, aura su – pour cette occasion – aider les petits français de mes équipes de développeurs MKT Lines afin qu’ils mettent le tout au carré. Aussi, comme on dit là bas >> First of all : BIG UP !! >> Julio EST l’Expert.

    Ensuite je souhaite ajouter que 2 développeurs PHP chez nous sont devenus « amoureux » de la sécu dans le cadre de cette épreuve et ont même décidé d’aller jusqu’au bout de la démarche pour finalement sécuriser à 100% les versions PREMIUM & STANDALONE. Ca c’est fait !

    La difficulté pour LiveOptim est que son déploiement en version plugin WP constituait une « marche arrière » par rapport à sa version et au projet d’origine standalone destinée à 100% des sites PHP (CMS ou pas). Le positionnement sur une version FREE WordPress.org a été conduit par notre volonté de portabilité, de facilitation d’installation pour l’utilisateur lambda et le développeur (non expert/agence web et bloggueurs WordPress) et bien entendu de buisness model…
    L’autre difficulté était justement de s’adapter à un univers nouveau, celui de WordPress et de s’adapter dans le même temps (toujours pour des objectifs de portabilité) aux exigences des autres CMS tels que…… #Chutttt ;)

    Mais je crois que nos équipes ont été réactives autant que faire se peut pour donner satisfaction à l’éthique de la formidable fondation WordPress.org et à ses utilisateurs. On peut toujours progresser. C’est ce que nous faisons depuis 10 ans chez MKT Lines. Et cette rencontre avec Julio nous aura été très profitable. Encore merci !

    J’aurais bien quelques nuances sur la méthode d’éducation d’un « marché » telle que la sécurité WordPress, mais ça je me le garde pour mes échanges privés avec Julio. ;)

    Note aux lecteurs : Tous ceux qui sont sur les versions FREE allant jusqu’à 1.1.8 PENSEZ A METTRE A JOUR !
    Merci de votre compréhension.
    Rien n’est jamais simple…

    Notre équipe est à votre disposition si vous avez des question sur LiveOptim en général.

    Bonne journée à tous!
    Erwan

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser le tag [php][/php] pour ajouter quelques lignes de PHP, si c'est un pavé, merci d'utiliser service comme pastebin.com.